花五分鐘看這篇之前,你才發現你不懂RESTful

語言: CN / TW / HK

原創公眾號:bigsai 轉載請聯繫bigsai

文章收藏在回車課堂

前言

在學習RESTful 風格接口之前,即使你不知道它是什麼,但你肯定會好奇它能解決什麼問題?有什麼應用場景?聽完下面描述我想你就會明白:

在互聯網並沒有完全流行的初期,移動端也沒有那麼盛行,頁面請求和併發量也不高,那時候人們對接口的要求沒那麼高,一些動態頁面(jsp)就能滿足絕大多數的使用需求。

image-20201204001441126

但是隨着互聯網和移動設備的發展,人們對Web應用的使用需求也增加,傳統的動態頁面由於低效率而漸漸被HTML+JavaScript(Ajax)的前後端分離所取代,並且安卓、IOS、小程序等形式客户端層出不窮,客户端的種類出現多元化,而客户端和服務端就需要接口進行通信,但接口的規範性就又成了一個問題:

image-20201204001702612

所以一套結構清晰、符合標準、易於理解、擴展方便讓大部分人都能夠理解接受的接口風格就顯得越來越重要,而RESTful風格的接口(RESTful API)剛好有以上特點,就逐漸被實踐應用而變得流行起來。

image-20201204001618944

現在,RESTful是目前最流行的接口設計規範,在很多公司有着廣泛的應用,其中Github 的API設計就是很標準的RESTful API,你可以參考學習。

在開發實踐中我們很多人可能還是使用傳統API進行請求交互,很多人其實並不特別瞭解RESTful API,對RESTful API的認知可能會停留在:

  • 面向資源類型的
  • 是一種風格
  • (誤區)接口傳遞參數使用斜槓(/)分割而不用問號(?)傳參。

而其實一個很大的誤區不要認為沒有查詢字符串就是RESTful API,也不要認為用了查詢字符串就不是RESTful API,更不要認為用了JSON傳輸的API就是RESTful API。

本教程將帶你瞭解RESTful並用SpringBoot實戰RESTful API,在實現本案例前,你需要保證你的電腦上

  • 擁有IDEA用來編寫項目代碼
  • 擁有Postman模擬請求進行測試
  • 擁有關係數據庫MySQL 5.7
  • 擁有navicat對MySQL進行管理

一、REST介紹

REST涉及一些概念性的東西可能比較多,在實戰RESTful API之前,要對REST相關的知識有個系統的認知。

REST的誕生

REST(英文:Representational State Transfer,簡稱REST,直譯過來表現層狀態轉換)是一種軟件架構風格、設計風格,而不是標準,只是提供了一組設計原則和約束條件。它主要用於客户端和服務器交互類的軟件。基於這個風格設計的軟件可以更簡潔,更有層次,更易於實現緩存等機制。

它首次出現在 2000 年 Roy Thomas Fielding 的博士論文中,這篇論文定義並詳細介紹了表述性狀態轉移(Representational State Transfer,REST)的架構風格,並且描述了 如何使用 REST 來指導現代 Web 架構的設計和開發。用他自己的原話説:

我寫這篇文章的目的是:在符合架構原理前提下,理解和評估基於網絡的應用軟件的架構設計,得到一個功能強、性能好、適宜通信的架構。

需要注意的是REST並沒有一個明確的標準,而更像是一種設計的風格,滿足這種設計風格的程序或接口我們稱之為RESTful(從單詞字面來看就是一個形容詞)。所以RESTful API 就是滿足REST架構風格的接口。

在這裏插入圖片描述

Fielding博士當時提出的是REST架構在很久的時間內並沒有被關注太多,而近些年REST在國內才變得越來越流行。下面開始詳細學習REST架構特徵。

REST架構特徵

既然知道REST和RESTful的聯繫和區別,現在就要開始好好了解RESTful的一些約束條件和規則,RESTful是一種風格而不是標準,而這個風格大致有以下幾個主要特徵

以資源為基礎 :資源可以是一個圖片、音樂、一個XML格式、HTML格式或者JSON格式等網絡上的一個實體,除了一些二進制的資源外普通的文本資源更多以JSON為載體、面向用户的一組數據(通常從數據庫中查詢而得到)。 統一接口: 對資源的操作包括獲取、創建、修改和刪除,這些操作正好對應HTTP協議提供的GET、POST、PUT和DELETE方法。換言而知,使用RESTful風格的接口但從接口上你可能只能定位其資源,但是無法知曉它具體進行了什麼操作,需要具體瞭解其發生了什麼操作動作要從其HTTP請求方法類型上進行判斷。具體的HTTP方法和方法含義如下:

  • GET(SELECT):從服務器取出資源(一項或多項)。
  • POST(CREATE):在服務器新建一個資源。
  • PUT(UPDATE):在服務器更新資源(客户端提供完整資源數據)。
  • PATCH(UPDATE):在服務器更新資源(客户端提供需要修改的資源數據)。
  • DELETE(DELETE):從服務器刪除資源。

當然也有很多在具體使用的時候使用PUT表示更新。從請求的流程來看,RESTful API和傳統API大致架構如下: image-20201204001311359

URI指向資源:URI = Universal Resource Identifier 統一資源標誌符,用來標識抽象或物理資源的一個緊湊字符串。URI包括URL和URN,在這裏更多時候可能代指URL(統一資源定位符)。RESTful是面向資源的,每種資源可能由一個或多個URI對應,但一個URI只指向一種資源。

無狀態:服務器不能保存客户端的信息, 每一次從客户端發送的請求中,要包含所有必須的狀態信息,會話信息由客户端保存, 服務器端根據這些狀態信息來處理請求。 當客户端可以切換到一個新狀態的時候發送請求信息, 當一個或者多個請求被髮送之後, 客户端就處於一個狀態變遷過程中。 每一個應用的狀態描述可以被客户端用來初始化下一次的狀態變遷。

REST架構限制條件

Fielding在論文中提出REST架構的6個限制條件,也可稱為RESTful 6大原則, 標準的REST約束應滿足以下6個原則:

客户端-服務端(Client-Server): 這個更專注客户端和服務端的分離,服務端獨立可更好服務於前端、安卓、IOS等客户端設備。

無狀態(Stateless):服務端不保存客户端狀態,客户端保存狀態信息每次請求攜帶狀態信息。

可緩存性(Cacheability) :服務端需回覆是否可以緩存以讓客户端甄別是否緩存提高效率。

統一接口(Uniform Interface):通過一定原則設計接口降低耦合,簡化系統架構,這是RESTful設計的基本出發點。當然這個內容除了上述特點提到部分具體內容比較多詳細瞭解可以參考這篇REST論文內容

分層系統(Layered System):客户端無法直接知道連接的到終端還是中間設備,分層允許你靈活的部署服務端項目。

按需代碼(Code-On-Demand,可選):按需代碼允許我們靈活的發送一些看似特殊的代碼給客户端例如JavaScript代碼。

REST架構的一些風格和限制條件就先介紹到這裏,後面就對RESTful風格API具體介紹。

二、RESTful API設計規範

既然瞭解了RESTful的一些規則和特性,那麼具體該怎麼去設計一個RESTful API呢?要從URL路徑、HTTP請求動詞、狀態碼和返回結果等方面詳細考慮。至於其他的方面例如錯誤處理、過濾信息等規範這裏就不詳細介紹了。

URL設計規範

URL為統一資源定位器 ,接口屬於服務端資源,首先要通過URL這個定位到資源才能去訪問,而通常一個完整的URL組成由以下幾個部分構成:

URI = scheme "://" host  ":"  port "/" path [ "?" query ][ "#" fragment ]
複製代碼

scheme: 指底層用的協議,如http、https、ftp host: 服務器的IP地址或者域名 port: 端口,http默認為80端口 path: 訪問資源的路徑,就是各種web 框架中定義的route路由 query: 查詢字符串,為發送給服務器的參數,在這裏更多發送數據分頁、排序等參數。 fragment: 錨點,定位到頁面的資源

我們在設計API時URL的path是需要認真考慮的,而RESTful對path的設計做了一些規範,通常一個RESTful API的path組成如下:

/{version}/{resources}/{resource_id}
複製代碼

version:API版本號,有些版本號放置在頭信息中也可以,通過控制版本號有利於應用迭代。 resources:資源,RESTful API推薦用小寫英文單詞的複數形式。 resource_id:資源的id,訪問或操作該資源。

當然,有時候可能資源級別較大,其下還可細分很多子資源也可以靈活設計URL的path,例如:

/{version}/{resources}/{resource_id}/{subresources}/{subresource_id}
複製代碼

此外,有時可能增刪改查無法滿足業務要求,可以在URL末尾加上action,例如

/{version}/{resources}/{resource_id}/action
複製代碼

其中action就是對資源的操作。

從大體樣式瞭解URL路徑組成之後,對於RESTful API的URL具體設計的規範如下:

  1. 不用大寫字母,所有單詞使用英文且小寫。
  2. 連字符用中槓"-"而不用下槓"_"
  3. 正確使用 "/" 表示層級關係,URL的層級不要過深,並且越靠前的層級應該相對越穩定
  4. 結尾不要包含正斜槓分隔符"/"
  5. URL中不出現動詞,用請求方式表示動作
  6. 資源表示用複數不要用單數
  7. 不要使用文件擴展名

HTTP動詞

在RESTful API中,不同的HTTP請求方法有各自的含義,這裏就展示GET,POST,PUT,DELETE幾種請求API的設計與含義分析。針對不同操作,具體的含義如下:

GET /collection:從服務器查詢資源的列表(數組)
GET /collection/resource:從服務器查詢單個資源
POST /collection:在服務器創建新的資源
PUT /collection/resource:更新服務器資源
DELETE /collection/resource:從服務器刪除資源
複製代碼

在非RESTful風格的API中,我們通常使用GET請求和POST請求完成增刪改查以及其他操作,查詢和刪除一般使用GET方式請求,更新和插入一般使用POST請求。從請求方式上無法知道API具體是幹嘛的,所有在URL上都會有操作的動詞來表示API進行的動作,例如:query,add,update,delete等等。

而RESTful風格的API則要求在URL上都以名詞的方式出現,從幾種請求方式上就可以看出想要進行的操作,這點與非RESTful風格的API形成鮮明對比。

在談及GET,POST,PUT,DELETE的時候,就必須提一下接口的安全性和冪等性,其中安全性是指方法不會修改資源狀態,即讀的為安全的,寫的操作為非安全的。而冪等性的意思是操作一次和操作多次的最終效果相同,客户端重複調用也只返回同一個結果。

上述四個HTTP請求方法的安全性和冪等性如下:

HTTP Method安全性冪等性解釋
GET安全冪等讀操作安全,查詢一次多次結果一致
POST非安全非冪等寫操作非安全,每多插入一次都會出現新結果
PUT非安全冪等寫操作非安全,一次和多次更新結果一致
DELETE非安全冪等寫操作非安全,一次和多次刪除結果一致

狀態碼和返回數據

服務端處理完成後客户端也可能不知道具體成功了還是失敗了,服務器響應時,包含狀態碼返回數據兩個部分。

狀態碼

我們首先要正確使用各類狀態碼來表示該請求的處理執行結果。狀態碼主要分為五大類:

1xx:相關信息 2xx:操作成功 3xx:重定向 4xx:客户端錯誤 5xx:服務器錯誤

每一大類有若干小類,狀態碼的種類比較多,而主要常用狀態碼羅列在下面:

200 OK - [GET]:服務器成功返回用户請求的數據,該操作是冪等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用户新建或修改數據成功。 202 Accepted - [*]:表示一個請求已經進入後台排隊(異步任務) 204 NO CONTENT - [DELETE]:用户刪除數據成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用户發出的請求有錯誤,服務器沒有進行新建或修改數據的操作,該操作是冪等的。 401 Unauthorized - [*]:表示用户沒有權限(令牌、用户名、密碼錯誤)。 403 Forbidden - [*] 表示用户得到授權(與401錯誤相對),但是訪問是被禁止的。 404 NOT FOUND - [*]:用户發出的請求針對的是不存在的記錄,服務器沒有進行操作,該操作是冪等的。 406 Not Acceptable - [GET]:用户請求的格式不可得(比如用户請求JSON格式,但是隻有XML格式)。 410 Gone -[GET]:用户請求的資源被永久刪除,且不會再得到的。 422 Unprocesable entity - [POST/PUT/PATCH] 當創建一個對象時,發生一個驗證錯誤。 500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用户將無法判斷髮出的請求是否成功。

返回結果

針對不同操作,服務器向用户返回數據,而各個團隊或公司封裝的返回實體類也不同,但都返回JSON格式數據給客户端。

第三關 一個RESTful API案例

上面講了RESTful理論知識,下面動手實現一個小案例吧!

預備

在本案例的實戰中,我們訪問的RESTful接口都是對數據庫真實的操作,新建數據庫,創建一個數據庫和表(根據自己喜好)。

選擇Maven依賴的時候,只需要勾選其中Spring的Web模塊、MySQL驅動以及MyBatis框架。

本案例的POJO創建Dog.java實體對象,其具體構造為:

package com.restfuldemo.pojo;

public class Dog {
    private int id;//唯一id標識
    private String name;//名稱
    private  int age;//年齡
    //省略get set
}

複製代碼

上面創建好了項目,我們就開始構建RESTful風格的API。在具體構建RESTful API的時候,需要對各種請求有更細緻的認知,當然,本案例在實現各種請求的時候為了演示的便捷並沒有完全遵循RESTful API規範,例如版本號等信息這裏就不添加了,案例更側重於使用SpringBoot實現這個接口。

本案例實現對dog資源的增刪改查,如下是非RESTful 和RESTful接口對比:

API name非 RESTfulRESTful
獲取dog/dogs/query/{dogid}GET: /dogs/{dogid}
插入dog/dogs/addPOST: /dogs
更新dog/dogs/update/{dogid}PUT:/dogs/{dogid}
刪除dog/dods/delete/{dogid} DELETE:/dogs/{dogid}

另外在使用postman進行發送請求的時候,有三種常用的文件類型傳遞到後端:

在這裏插入圖片描述 form-data : 就是form表單中的multipart/form-data,會將表單數據處理為一條信息,用特定標籤符將一條條信息分割開,而這個文件類型通常用來上傳二進制文件。

x-www-form-urlencoded:就是application/x-www-form-urlencoded,是form表單默認的encType,form表單會將表單內的數據轉換為鍵值對,這種格式不能上傳文件。

raw:可以上傳任意格式的文本,可以上傳Text,JSON,XML等,但目前大部分還是上傳JSON格式數據。當後端需要接收JSON格式數據處理的時候,可以採用這種格式來測試。

因為GET請求查詢參數在URL上,其他類型請求使用x-www-form-urlencoded方式向後端傳值。

GET POST PUT DELETE請求

GET請求用來獲取資源:GET請求會向數據庫發索取數據的請求,從而來獲取資源,該請求就像數據庫的select操作一樣,只是用來查詢數據,不會影響資源的內容。無論進行多少次操作,結果都是一樣的。

並且GET請求會把請求的參數附加在URL後面,但是不同的瀏覽器對其有不同的大小長度限制。

在本案例中,我們設計兩個GET請求的API。 GET /dogs :用來返回dog資源的列表。 GET /dogs/{dogid} :用來查詢此id的單個dog資源。

POST請求用來新增一個資源 : POST請求向服務器發送數據,但是該請求會改變數據的內容(新添),就像數據庫的insert操作一樣,會創建新的內容。且POST請求的請求參數都是請求體中,其大小是沒有限制的。

在本案例中,我們設計以下POST請求的API。 POST /dogs :服務端新增一個dog資源。

PUT請求用來更新資源,PUT請求是向服務器端發送數據的, 與POST請求不同的是,PUT請求側重於數據的修改 ,就像數據庫中update一樣,而POST請求側重於數據的增加。

在本案例中,我們設計以下POST請求的API。 PUT /dogs/{dogid} :用來更新此id的單個dog資源。

DELETE 請求用來刪除資源,DELETE請求用途和它字面意思一致,用來刪除資源。和數據庫中delete相對應。

在本案例中,我們設計以下DELETE請求的API。 DELETE /dogs/{dogid} :用來刪除此id的單個dog資源。

對應的Mapper文件為:

package com.restfuldemo.mapper;

import com.restfuldemo.pojo.Dog;
import org.apache.ibatis.annotations.*;
import java.util.List;

@Mapper
public interface DogMapper {

    @Select("select * from dog")
    List<Dog> getAllDog();

    @Select("select * from dog where id=#{id}")
    Dog getDogById(@Param("id") int id);

    @Insert("insert into dog (name,age) values (#{name},#{age})")
    boolean addDog(Dog dog);

    @Update("update dog set name=#{name},age=#{age} where id=#{id}")
    boolean updateDog(Dog dog);

    @Delete("delete  from dog where id=#{id}")
    boolean deleteDogById(int id);
}
複製代碼

對應controller文件為:

package com.restfuldemo.controller;

import com.restfuldemo.mapper.DogMapper;
import com.restfuldemo.pojo.Dog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Arrays;
import java.util.List;

@RestController
public class TestController {

    @Autowired(required = false)
    DogMapper dogMapper;

    @GetMapping("dogs")
    public List<Dog> getDogs()
    {
        return  dogMapper.getAllDog();
    }

    @GetMapping("dogs/{id}")
    public Dog getDogById(@PathVariable("id") int id)
    {
        Dog dog=dogMapper.getDogById(id);
        return  dog;
    }
    @PostMapping("dogs")
    public boolean addDog(Dog dog)
    {
        return dogMapper.addDog(dog);
    }
    @PutMapping("dogs/{id}")
    public boolean updateDog(@PathVariable("id")int id,@RequestParam("name")String name,@RequestParam("age")int age)
    {

        Dog dog=dogMapper.getDogById(id);
        dog.setName(name);
        dog.setAge(age);
        return  dogMapper.updateDog(dog);
    }

    @DeleteMapping("dogs/{id}")
    public boolean deleteDog(@PathVariable("id") int id)
    {
        return  dogMapper.deleteDogById(id);
    }
}
複製代碼

經過筆者測試一切都是ok的,如果要項目源文件請聯繫筆者發你哈!

總結

RESTful風格的API 固然很好很規範,但大多數互聯網公司並沒有按照或者完全按照其規則來設計,因為REST是一種風格,而不是一種約束或規則,過於理想的RESTful API 會付出太多的成本。

比如RESTful API也有一些缺點

  • 比如操作方式繁瑣,RESTful API通常根據GET、POST、PUT、DELETE 來區分操作資源的動作,而HTTP Method 本身不可直接見,是隱藏的,而如果將動作放到URL的path上反而清晰可見,更利於團隊的理解和交流。
  • 並且有些瀏覽器對GET,POST之外的請求支持不太友好,還需要特殊額外的處理。
  • 過分強調資源,而實際業務API可能有各種需求比較複雜,單單使用資源的增刪改查可能並不能有效滿足使用需求,強行使用RESTful風格API只會增加開發難度和成本。

所以,當你或你們的技術團隊在設計API的時候,如果使用場景和REST風格很匹配,那麼你們可以採用RESTful 風格API。但是如果業務需求和RESTful風格API不太匹配或者很麻煩,那也可以不用RESTful風格API或者可以借鑑一下,畢竟無論那種風格的API都是為了方便團隊開發、協商以及管理,不能墨守成規。

在這裏插入圖片描述

到這裏RESTful API的介紹和實戰就結束啦,本篇首先從RESTful的一些特點進行介紹,再到SpringBoot實戰RESTful API,最後也説了一些RESTful API並不完美的地方,相信睿智的你對RESTful 一定有了很深刻的理解。在以後項目的API設計上定能有所優化。

不同的人對RESTful API可能有着不同的理解,但存在即合理,RESTful API有着其鮮明的優勢和特點,目前也是一種API設計的主要選型之一,所以掌握和理解RESTful API還是相當重要的! 在這裏插入圖片描述

原創不易,bigsai我請你幫兩件事幫忙一下:

  1. 點贊支持一下, 您的肯定是我在掘金社區創作的源源動力。

  2. 微信搜索「bigsai」,關注我的公眾號(新人求支持),不僅免費送你電子書,我還會第一時間在公眾號分享知識技術。加我還可拉你進力扣打卡羣一起打卡LeetCode。

如有幫助收穫,還請點個贊!