REST

在 REST 术语中,我们将单个数据对象(如应用中的便笺)称为resources。 每个资源都有一个唯一的地址——它的 URL。 根据 json-server 使用的一般约定,我们将能够在资源 URL, 即notes/3上定位某个便笺,其中3是资源的 id。 另一方面, notes url 指向包含所有便笺的资源集合。
通过 HTTP GET 请求从服务器获取资源。 例如,对 URLnotes/3 的 HTTP GET 请求将返回 id 为3的便笺。 对notes URL 的 HTTP GET 请求将返回所有便笺的列表。
根据 json 服务器遵守的 REST 约定,通过向notes URL 发出 HTTP POST 请求来创建、存储新的便笺。 新便笺资源的数据在请求的body 中发送。
Json-server 要求以 JSON 格式发送所有数据。 实际上,这意味着数据必须是格式正确的字符串,并且请求必须包含值为application/json 的Content-Type 请求头。

发送数据到服务器

修改Notes项目中的addNote函数

  1. addNote = event => {
  2. event.preventDefault()
  3. const noteObject = {
  4. content: newNote,
  5. date: new Date(),
  6. important: Math.random() > 0.5,
  7. }
  8. axios
  9. .post('http://localhost:3001/notes', noteObject)
  10. .then(response => {
  11. setNotes(notes.concat(response.data))
  12. setNewNote('')
  13. })
  14. }

此时数据就写入到db.json了, 注意id会自动生成
image.png

Changing the Importance of Notes

存储在 json-server 后端中的各个便笺可以通过对便笺的唯一 URL 发出 HTTP 请求,以两种不同的方式进行修改。 我们可以用 HTTP PUT 请求替换 整个便笺,或者只用 HTTP PATCH 请求更改便笺的一些属性。

  1. const toggleImportanceOf = (id) => {
  2. const url = `http://localhost:3001/notes/${id}`
  3. const note = notes.find(n => n.id === id)
  4. const changedNote = { ...note, important: !note.important }
  5. axios.put(url, changedNote).then(response => {
  6. setNotes(notes.map(note => note.id !== id ? note : response.data))
  7. })
  8. }
  • 第一行根据每个便笺资源的 id 定义其唯一的 url
  • Array.find()返回数组中第一个符合条件的元素
  • changedNote只是浅拷贝

    Extracting communication with the backend into a separate module

    将与后端的通信提取到单独的模块中
    Single-responsibility principle 单一职责原则

创建一个src/services目录,并添加一个名为notes.js 的文件:

  1. import axios from 'axios'
  2. const baseUrl = 'http://localhost:3001/notes'
  3. const getAll = () => {
  4. return axios.get(baseUrl)
  5. }
  6. const create = newObject => {
  7. return axios.post(baseUrl, newObject)
  8. }
  9. const update = (id, newObject) => {
  10. return axios.put(`${baseUrl}/${id}`, newObject)
  11. }
  12. const noteService = {
  13. getAll,
  14. create,
  15. update
  16. }
  17. export default noteService

在App.js中导入并使用

  1. import noteService from './services/notes'
  2. -- snip--
  3. noteService
  4. .getAll()
  5. .then(response => {
  6. setNotes(response.data)
  7. })
  8. -- snip --

如果我们只获得响应数据,而不是整个 HTTP 响应,可以做如下修改
因为.then返回的仍然是一个promise

  1. import axios from 'axios'
  2. const baseUrl = 'http://localhost:3001/notes'
  3. const getAll = () => {
  4. const request = axios.get(baseUrl)
  5. return request.then(response => response.data)
  6. }
  7. const create = newObject => {
  8. const request = axios.post(baseUrl, newObject)
  9. return request.then(response => response.data)
  10. }
  11. const update = (id, newObject) => {
  12. const request = axios.put(`${baseUrl}/${id}`, newObject)
  13. return request.then(response => response.data)
  14. }
  15. const noteService = {
  16. getAll,
  17. create,
  18. update
  19. }
  20. export default noteService

在App.js中的使用则改为

  1. -- snip --
  2. noteService
  3. .getAll()
  4. .then(initialNotes => {
  5. setNotes(initialNotes)
  6. })

Cleaner syntax for defining object literals

用于定义对象字面量的更清晰的语法
上文中

  1. export default { getAll, create, update }

其实是下列的简写, 这是ES6新语法

  1. export default {
  2. getAll: getAll,
  3. create: create,
  4. update: update
  5. }

Promises and Errors

image.png
如果请求修改服务器中不存在的note,则会报错
当 HTTP 请求失败时,相关的promise是rejected
为被拒绝的promise添加处理程序的更常见的方法是使用catch方法

  1. noteService
  2. .update(id, changedNote)
  3. .then(returnedNote => {
  4. setNotes(notes.map(note => note.id !== id ? note : returnedNote))
  5. })
  6. .catch(error => {
  7. alert(
  8. `the note '${note.content}' was already deleted from server`
  9. )
  10. setNotes(notes.filter(n => n.id !== id))
  11. })

Exercises 2.15.-2.18.

phonebook项目实现删除人员

  1. const handleDelete = (item) => {
  2. if (window.confirm(`Delete ${item.name}?`)) {
  3. personService.deletePerson(item.id)
  4. .then(() => {
  5. setPersons(persons.filter(p => p.id !== item.id))
  6. })
  7. .catch(error => console.log(error))
  8. }
  9. }
  • window.confirm弹出窗口确认是否删除
  • Array.filter方法更新删除后的数据

    1. const deletePerson = (id) => {
    2. const request = axios.delete(`${baseUrl}/${id}`)
    3. return request.then(response => response.data)
    4. }
  • axios发送delete请求删除对应id

    1. const handleSubmit = (event) => {
    2. event.preventDefault()
    3. const names = persons.map(item => item.name)
    4. const newObject = {
    5. name: newName,
    6. number: newNumber
    7. }
    8. if (names.indexOf(newName) >= 0) {
    9. if (window.confirm(`${newName} is already added to phonebook, replace the old number with a new one?`)) {
    10. const person = persons.find(item => item.name === newName)
    11. personService.update(person.id, newObject)
    12. .then(response => {
    13. setPersons(persons.map(item => item.name !== newName ? item : response))
    14. })
    15. }
    16. } else {
    17. personService.create(newObject)
    18. .then(response => {
    19. setPersons(persons.concat(response))
    20. setFilterFlag(false)
    21. })
    22. }
    23. }
    1. const update = (id, newObject) => {
    2. const request = axios.put(`${baseUrl}/${id}`, newObject)
    3. return request.then(response => response.data)
    4. }
  • 存在则axios.put更新,不存在则axios.post创建