W12_作業一實作記錄 [ MTR05 ] 實作之三


Posted by Christy on 2021-08-30

1. hw1_實作 API 新增留言

MTR05_第十二週(06/28 ~ 07/04):前後端整合_作業一帶著做之打造後端 API:Part1

流程:

a. 先引入連線

b. 引入 header('Content-Type: application/json;charset=utf-8'); 把資料變成 json 格式

c. 錯誤處理:如果沒有 content, nickname, site_key 就回傳錯誤訊息

if (
    empty($_POST['content']) ||
    empty($_POST['nickname']) ||
    empty($_POST['site_key'])
  ) {
    $json = array(
      "ok" => false,
      "message" => "Please input missing fileds"
    );

    $response = json_encode($json);
    echo $response;
    die();
  }

d. 把資料拿進來以後,新增到資料庫裡面

e. 如果沒有資料,做錯誤處理

f. 用 postman 做 API 測試,線上版的不能用,後來下載了桌面版的,在下面選 desktop agent,因為非同源政策的關係,不可以用瀏覽器版本的

遇到的困難:

  1. 跟著老師打程式碼,真的就是要細心!有些錯誤自己看半天都找不到,就這樣卡了一小時,沒關係,找到就好!(成功了真的好開心)

2. hw1_實作 API 顯示留言

MTR05_第十二週(06/28 ~ 07/04):前後端整合_作業一帶著做之打造後端 API:Part1_13':15"

流程:

a. 引入連線還有 header('Content-type: application/json;charset=utf-8');

b. 錯誤處理,檢查 $_GET[] 有沒有拿到東西;在這裡我們拿的資料是 site_key

c. 我們現在用的是 GET 方法拿到 site_key

d. sql 拿資料:要 nickname, content, created_at(記得一定要用防 sql injection 的方法 stmt prepare)

e. 錯誤處理:如果沒有結果的話,就回傳錯誤訊息

f. 為什麼要用get_result():

當使用 stmt prepare 且需要得到回傳的結果時,就要用這個函式

mysqli_stmt::get_result:
Gets a result set from a prepared statement as a mysqli_result object

g. 為什麼要用 array_push():

抓到資料以後,要把資料放到 array 裡面,用法:

<?php
  $stack = array("orange", "banana");
  array_push($stack, "apple", "raspberry");
  print_r($stack);
?>

// 上面會輸出
Array
(
  [0] => orange
  [1] => banana
  [2] => apple
  [3] => raspberry
)

遇到的困難:

  1. 永遠檢查 sql 語法,方法就是把它丟進 mysql 裡面檢查

  2. array 裡面永遠要用逗號分隔


程式碼:

<?php  
  require_once('conn.php');
  header('Content-type: application/json;charset=utf-8');
  if (
    empty($_GET['site_key'])
  ) {
    $json = array(
      "ok" => false,
      "message" => "Please add site_key in url"
    );

    $response = json_encode($json);
    echo $response;
    die();
  }

  $site_key = $_GET['site_key'];

  $sql = "select nickname, content, created_at from discussions where site_key = ? order by id desc";
  $stmt = $conn->prepare($sql);
  $stmt->bind_param('s', $site_key);
  $result = $stmt->execute();

  if (!$result) {
   $json = array(
     "ok" =>  false,
     "message" => $conn->error
    );

    $response = json_encode($json);
    echo $response;
    die(); 
  }

  $result = $stmt->get_result();
  $discussions = array();
  while($row = $result->fetch_assoc()) {
    array_push($discussions, array(
      "nickname" => $row["nickname"],
      "content" => $row["content"],
      "created_at" => $row["created_at"]
    ));
  }
  $json = array(
    "ok" => true,
    "discussions" => $discussions
  );

  $response = json_encode($json);
  echo $response;
?>

3. hw1_實作 API 串接

MTR05_第十二週(06/28 ~ 07/04):前後端整合_作業一帶著做之打造後端 API:Part2

a. 先用 bootstrap 刻 UI: 就是去找適合的物件貼上

b. 串 API 04':50"

b.1 先顯示留言資料:

一定開始要用 $(document).ready()

  <script>
    function escape(toOutput){
      return toOutput.replace(/\&/g, '&amp;')
          .replace(/\</g, '&lt;')
          .replace(/\>/g, '&gt;')
          .replace(/\"/g, '&quot;')
          .replace(/\'/g, '&#x27')
          .replace(/\//g, '&#x2F');
    }

    $(document).ready(() => {
      $.ajax({
        url: "http://localhost:8080/christy/discussions/api_comments.php?site_key=christy",
      }).done(function(data) {
        if (!data.ok) {
          alert(data.message)
          return
        }

        const comments = data.discussions;
        for (let comment of comments) {
          $('.comments').append(`
            <div class="card mt-3">
              <div class="card-body">
                <h5 class="card-title">${escape(comment.nickname)}</h5>
                <p class="card-text">${escape(comment.content)}</p>
              </div>
            </div>
          `)
        }
      });
    })
  </script>

b.2 新增留言:

先監聽表單送出事件,可以查 You might not need jQuery 要怎麼 POST

遇到的困難:

  1. chrome 的 dev tool,如果把 selected context only 打勾,就不會顯示錯誤訊息了

  2. 因為非同源政策,所以要加一個 header 在自己寫的 API 檔案裡面

header('Access-Control-Allow-Origin: *');

  1. 因為要用 jQuery,所以要引入在 head 標籤裡;當然要記得引入 bootstrap

  2. 就算跟著老師一起做,還是會有打錯的地方,如果錯了,記得一定一定要停下來訂正,不要繼續跟著走,不然碼越多就越找不到

這次的錯誤是在用 POST 的時候,裡面的data: newCommentData,前面的 data 忘記打。

雖然影片裡面老師說,ES6 參數一樣就不用打,但是這裡參數不一樣!

天阿,找到錯誤好感動!

$.ajax({
          type: 'POST',
          url: 'http://localhost:8080/christy/discussions/api_add_comments.php',
          data: newCommentData
        }).done(function(data) {
          if (!data.ok) {
            alert(data.message)
            return
          }

4. hw1_實作API「載入更多」

MTR05_第十二週(06/28 ~ 07/04):前後端整合_作業一參考範例(請寫完作業再看)

流程:

有載入更多的按鈕,按下去以後會顯示多五筆留言。

留言順序由新到舊排列,留言大於五筆,才會出現載入更多的按鈕,按下去就會顯示多五筆留言;沒有更多留言時,就不會有載入更多的按鈕。

  1. 先去拿 before 這個資料,把 sql 分成兩種情況:

a. 網址參數有 before

b. 網址參數沒有 before

  1. 記得要帶 id

小結:API 就寫成兩種情況,如果在 postman 可以跑就差不多了


  1. 分頁功能有兩種,page-based, cursor-based
  • page-based 就是有分頁按鈕形式,可以任意跳到某一頁,每一頁的留言筆數是固定的那種

  • cursor-based 的重點在於 before and after

如果 API URL 帶的參數為:...?before=6,就會顯示留言筆數 5, 4, 3, 2, 1

參數為:...?after=5,就會顯示留言筆數為 6, 7, 8, 9, 10

  1. 04':00" - 11':50" 實作 api 載入更多

a. 根據 before 是不是空的,決定要帶什麼參數

我覺得重點觀念是有 before 跟 after 這兩個東西,難理解的觀念是 before 是哪裡來的?sql 語法怎麼寫?在網址上帶參數時,要怎麼規範?

無法再看第三次的時候理解,但是先繼續下去。

遇到的問題:

  1. 在 postman 檢查的時候,要注意 URL 的路徑

  • 老師的程式碼,但是沒有防 sql injection,我下面有改寫。
<?php  
  require_once('conn.php');
  header('Content-type: application/json;charset=utf-8');
  header('Access-Control-Allow-Origin: *');
  if (
    empty($_GET['site_key'])
  ) {
    $json = array(
      "ok" => false,
      "message" => "Please add site_key in url"
    );

    $response = json_encode($json);
    echo $response;
    die();
  }

  $site_key = $_GET['site_key'];

  $sql = 
    "select id, nickname, content, created_at from discussions where site_key = ? " .
    (empty($_GET['before']) ? "" : "and id < ? ") . 
    "order by created_at desc limit 5";

  $stmt = $conn->prepare($sql);
  if (empty($_GET['before'])) {
    $stmt->bind_param('s', $site_key);
  } else {
    $stmt->bind_param('si', $site_key, $_GET['before']);
  }

  $result = $stmt->execute();

  if (!$result) {
   $json = array(
     "ok" =>  false,
     "message" => $conn->error
    );

    $response = json_encode($json);
    echo $response;
    die(); 
  }

  $result = $stmt->get_result();
  $discussions = array();
  while($row = $result->fetch_assoc()) {
    array_push($discussions, array(
      "id" => $row["id"],
      "nickname" => $row["nickname"],
      "content" => $row["content"],
      "created_at" => $row["created_at"]
    ));
  }
  $json = array(
    "ok" => true,
    "discussions" => $discussions
  );

  $response = json_encode($json);
  echo $response;
?>

  • 我的程式碼,有防 sql injection
<?php  
  require_once('conn.php');
  header('Content-type: application/json;charset=utf-8');
  header('Access-Control-Allow-Origin: *');
  if (
    empty($_GET['site_key'])
  ) {
    $json = array(
      'ok' => false,
      'message' => 'Please add site_key in url'
    );

    $response = json_encode($json);
    echo $response;
    die();
  }

  $site_key = $_GET['site_key'];
  $before = $_GET['before'];

  if (!$before) {
    $sql = 'select id, nickname, content, created_at from discussions where site_key = ? order by id desc limit 5';
    $stmt = $conn->prepare($sql);
    $stmt->bind_param('s', $site_key);
  } else {
    $sql = 'select id, nickname, content, created_at from discussions where site_key = ? and id < ? order by id desc limit 5';
    $stmt = $conn->prepare($sql);
    $stmt->bind_param('si', $site_key, $before);
  }

  $result = $stmt->execute();

  if (!$result) {
   $json = array(
     'ok' =>  false,
     'message' => $conn->error
    );

    $response = json_encode($json);
    echo $response;
    die(); 
  }

  $result = $stmt->get_result();
  $discussions = array();
  while($row = $result->fetch_assoc()) {
    array_push($discussions, array(
      'id' => $row['id'],
      'nickname' => $row['nickname'],
      'content' => $row['content'],
      'created_at' => $row['created_at']
    ));
  }
  $json = array(
    'ok' => true,
    'discussions' => $discussions
  );

  $response = json_encode($json);
  echo $response;
?>

5. hw1_實作留言板「載入更多」功能

MTR05_第十二週(06/28 ~ 07/04):前後端整合_作業一參考範例(請寫完作業再看)12':00" 開始

流程:

  1. 把顯示留言包成一個函式

  2. 16':13" 實作載入更多的按鈕

a. 「載入更多」按鈕要放在 comments 裡面,可以把它獨立成一個字串

b. .on():jQuery 的 addeventlistener

c. 18':17" 先把載入更多的按鈕移除,加上新的留言,再把按鈕加回去

用事件代理的方式

遇到的問題:

  1. 更改檔名之後,要特別注意 url 路徑

  2. Id 不用 escape() 因為我們跳脫的是特殊符號,數字不需要

  3. 老實說看完兩遍影片並跟著實作以後,並沒有很懂裡面的一些細節。

  4. 為什麼會有錯誤訊息:

Access to XMLHttpRequest at 'file:///Users/christy/.bitnami/stackman/machines/xampp/volumes/root/htdocs/christy/p_discussions/index.html' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.

是因為我把 url 的名稱改了,但是 ajax 裡面如果名稱有變,應該要完整的把名字寫出來,如果只寫成 url,,就會發生這個錯誤。

要寫完整變成 url: getCommentsURL, 後面接變數名稱

  1. 要怎麼在點擊按鈕時,除了抓留言以外,去偵測留言的數量?

  2. 為什麼在 index.html 裡面,getCommentsAPI 這個函式要放一個 call back fn 當第三個參數?

是為了確保執行順序嗎?例如說一定要先確認是否拿到 siteKey and before 才能繼續下去?這裡不是很明白

function getCommentsAPI(siteKey, before, cb) {
  let url = `http://localhost:8080/christy/p_discussions/api_comments.php?site_key=${siteKey}`
  if (before) {
    url += '&before=' + before
  }
  $.ajax({
    url,
  }).done(function(data) {
    cb(data)
  });
}
  1. 如果看完影片自己硬做的話,就會變成網路上看到的文章先在資料庫新增一個表格存 pageToken,接著把全部的 api 都改掉,然後可能會在哪個坑跌倒,又可能就沒有然後了。

所以說我應該要好好利用資源問問題,不要再亂來了。










Related Posts

Day00 Elements of Clojure 書摘&心得

Day00 Elements of Clojure 書摘&心得

第 19 期 Python 程式設計入門-作業任務4

第 19 期 Python 程式設計入門-作業任務4

Day 2 Review Course Outline

Day 2 Review Course Outline


Comments