iT邦幫忙

0

RESTful API 練習 (XHR, Fetch詳解)

RestFul API

  • GET 取得資料
  • POST 新增資料
  • PUT 完整更新 (會將你設定的覆蓋過去,舊有的屬性消失)
  • PATCH 局部更新 (除了更新亦可新增屬性)
  • DELETE 刪除 (不需傳入 data)

Content-Type: application/x-www-form-urlencoded

  • 為表單欄位
  • 必須以 key1=val1&key2=val2 的方式編碼
  • jQuery 默認提交方式
  • PHP中以 $_POST['title'] 取得對應值
  // 設定表頭如此
  Content-Type: application/x-www-form-urlencoded;charset=utf-8
  // 使用 GET 方法 將參數直接寫在待入的 url 上
  url/?title=test&name=Tom
  // 使用 POST 方法 則將資料轉成字串傳遞
  const data = 'name=TOM&age=30'
  xhr.send(data)

呼叫網址範例

  • GET ~url/users 或 ~url/users/1
  • POST ~url/users
  • PUT ~url/users/1 (需指定id)
  • PATCH ~url/users/1 (需指定id)
  • DELETE ~url/users/1 (需指定id)
  // PUT
  data = { name:'TEST' } name 以外的屬性刪除
  // PATCH
  data = { name:'TEST' } title 保留,只更新 name

XHR

  • Get
  const xhr = new XMLHttpRequest()
  // true 表 async 非同步(預設) false 表 sync 同步
  xhr.open('GET', 'http://localhost:3000/users', true)
  
  // 資料傳送成功並接收回應後,要作些甚麼動作
  xhr.onload = function () {
    if (xhr.status >= 200 && xhr.status < 400) {
      // 回傳值為字串 需要先轉換成 JSON 再作運算
      console.table(JSON.parse(this.response)) 
    }
  }
  // GET 不需要傳送資料
  xhr.send(null) 
  • POST / PUT / PATCH / DELETE 寫法雷同
  • 需注意 POST 外,其他皆需指定 id
  • DELETE不需傳入資料
  const data = { name: 'TEST2', title: 'Title2' }
  const xhr = new XMLHttpRequest()
  
  // PUT、PATCH、DELETE (皆需指定id) ~/users/1
  xhr.open('POST', 'http://localhost:3000/users', true)
  
  // 設定表頭
  xhr.setRequestHeader('Content-type', 'application/json ')
  
  xhr.onload = function () {
    if (xhr.status >= 200 && xhr.status < 400) { 
      // 回傳剛剛送出的結果 回傳值為字串需先轉換成 JSON
      console.table(JSON.parse(this.response)) 
    }
  }
  
  // 需將資料轉成 String
  xhr.send(JSON.stringify(data))
  
  // 如果未轉成 String 資料格式不符合時會噴 400 Bad Request
  xhr.send(data) // Ex: ['test']

Fetch

  • Fetch 並不會自動傳送 Cookie,必須加上表頭開啟

credentials: 'include'

  • GET
fetch('http://localhost:3000/users/2')
  .then(res => res.json()) // 先將其轉成 JSON 檔
  .then((data) => { console.table(data) })
  • GET 使用下面的寫法,不可有 body 會噴錯

TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body

  // PUT、PATCH、DELETE (皆需指定id) ~/users/1
  const postUrl = 'http://localhost:3000/users'
  const newData = { name: 'PATCH', title: 'PATCH Title' }

  function postData(url, data = null) {
    return fetch(url, {
      method: 'POST',
      credentials: 'include', // 需傳送 Cookie 必須開啟
      // header外參數 cache mode redirect referrer
      headers: {
        'Content-Type': 'application/json',
      },
      // 注意若 Method 為 GET 不可有 body 會噴錯
      body: JSON.stringify(data),
      // 回傳仍是 Promise物件
    }).then((res) => {
    
      // Response objects
      console.dir(res)
      
      // 狀態是否為 200-299
      if (res.ok) { res.json() }
      
      // 即使不設定,他也會自動噴出錯誤訊息
      throw new Error('Network response was not ok.')
    })
  }
  postData(postUrl, newData).then(res => res.json())
    .then((data) => { console.table(data) })
    .catch((err) => { console.error(err) })

Response objects

  • status 回傳狀態數字 200
  • statusText 回傳狀態字串 'ok'
  • ok 回傳狀態是否為 200-299 (Boolean值)

Fetch Credentials 參數

  • Credentials 憑證/Cookie
    credentials: 'include' (皆發送)
    credentials: 'same-origin' (同源)
    credentials: 'omit' (預設值,不發送Cookie)

自訂 Request 物件

  • 可自訂一些通用的 Header 屬性不用每次都重寫
  // 有 get / set / has / delete 方法可用
  const myHeaders = new Headers({
    'Content-Type': 'application/json',
  })

  const myInit = {
    method: 'GET',
    headers: myHeaders,
    mode: 'cors',
    cache: 'default', 
  }
  const myRequest = new Request('url', myInit)
  fetch(myRequest)

常見的 Body

  • Blob/File 不可變的二進制資料 (圖檔、音訊)
  • string 文本、陣列、JSON
  • FormData 表單欄位
  // blob 圖檔範例
  const myImage = document.querySelector('.TargetImg')
  fetch('flowers.jpg')
    .then(res => res.blob())
    .then((myBlob) => { 
      let objectURL = URL.createObjectURL(myBlob) 
      myImage.src = objectURL 
    })

FormData 表單欄位

  • 表頭必須設定為 multipart/form-data
  • 表單 欄位 & 值 建立對應的 鍵 & 值
  let formData = new FormData()
  let fileField = document.querySelector("input[type='file']")

  formData.append('username', 'abc123')
  formData.append('avatar', fileField.files[0])

  fetch('https://example.com/profile/avatar', {
    method: 'PUT',
    body: formData,
  })
    .then(res => res.json())
    .catch(err => console.error('Error:', err))
    .then(res => console.log('Success:', res))

Async 小範例

    run()
    async function run() {
      const getUrl = 'http://localhost:3000/users'
      const postUrl = 'http://localhost:3000/users'
      const newData = { name: 'test' }
      const post = await postData(postUrl, newData) // 回傳Promise物件
      const res = await fetch(getUrl)
      const data = await res.json()
      console.table(data)
    }

    function postData(url, data = null) {
      return fetch(url, {
        method: 'POST',
        credentials: 'include', // 需傳送 Cookie 必須開啟
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(data),
      })
    }

參考資料


尚未有邦友留言

立即登入留言