什么是 RESTful API

官网地址:https://restfulapi.cn/

  • REST 是 Representational State Transfer 的缩写,如果一个架构符合 REST 原则,就称它为 RESTful 架构;
  • RESTful 架构可以充分的利用 HTTP 协议的各种功能,是 HTTP 协议的最佳实践;
  • RESTful API 是一种软件架构风格、设计风格,可以让软件更加清晰,更简洁,更有层次,可维护性更好;

API 请求

HTTP 动词

GET: 读取(Read)POST:新建(Create)PUT: 更新(Update)PATCH: 更新(Update),通常是部分更新DELETE:删除(Delete)

请求设计

请求 = 动词 + 宾语

  • 动词 使用五种 HTTP 方法,对应 CRUD 操作;
  • 宾语 URL 应该全部使用名词复数,可以有例外,比如搜索可以使用更加直观的 search;
  • 过滤信息(Filtering) 如果记录数量很多,API 应该提供参数,过滤返回结果。 ?limit=10 指定返回记录的数量 ?offset=10 指定返回记录的开始位置;

API 响应

状态码

五大类状态码,总共100多种,覆盖了绝大部分可能遇到的情况。每一种状态码都有约定的解释,客户端只需查看状态码,就可以判断出发生了什么情况。API 不需要1xx状态码;

1xx:相关信息2xx:操作成功3xx:重定向4xx:客户端错误5xx:服务器错误

响应设计

服务器回应数据

  • 客户端的每一次请求,服务器都必须给出回应。回应包括 HTTP 状态码和数据两部分;

  • 客户端请求时,要明确告诉服务器,接受 JSON 格式,请求的 HTTP 头的 ACCEPT 属性要设成 application/json

  • 服务端返回的数据,不应该是纯文本,而应该是一个 JSON 对象。服务器回应的 HTTP 头的 Content-Type 属性要设为 application/json;

SpringBoot 快速实践 RESTful

注解实现

Srping Boot 提供了与 Rest 操作方式(GET、POST、PUT、DELETE)对应的注解:

HTTP 动词Srping Boot 注解说明
GET@GetMapping读取
POST@PostMapping新建
PUT@PutMapping更新
PATCH@PatchMapping更新,通常是部分更新
DELETE@DeleteMapping删除

实践 RESTful

根据 RESTful 风格设计编写用户操作的 RESTful API。

1.定义用户类

package com.csp.mingyue.api.model;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;import lombok.ToString;/** @author Strive */@Data@ToString@Builder@NoArgsConstructor@AllArgsConstructorpublic class MingYueUser {private Long userId;private String username;}

2.编写 Service

package com.csp.mingyue.api.service;import cn.hutool.core.map.MapUtil;import com.csp.mingyue.api.model.MingYueUser;import java.util.Map;import org.springframework.stereotype.Service;/** @author Strive */@Servicepublic class MingYueUserService {/** 模拟用户存储 */private static final Map<Long, MingYueUser> USER_MAP = MapUtil.newHashMap();static {USER_MAP.put(1L, MingYueUser.builder().userId(1L).username("mingyue").build());}/** * 根据用户ID查询用户信息 * * @param userId 用户ID * @return 用户信息 */public MingYueUser queryUserById(Long userId) {return USER_MAP.get(userId);}/** * 添加用户 * * @param user 用户信息 * @return 新用户ID */public Long addUser(MingYueUser user) {USER_MAP.put(2L, user);return user.getUserId();}/** * 更新用户 * * @param user 用户信息 */public String updateUser(MingYueUser user) {MingYueUser mingYueUser = USER_MAP.get(user.getUserId());USER_MAP.put(user.getUserId(), user);return mingYueUser.getUsername() + " 更新为:" + user.getUsername();}/** * 根据用户ID删除用户 * * @param userId 用户ID */public String deleteUser(Long userId) {int size = USER_MAP.size();USER_MAP.remove(userId);return "原" + size + "用户,删除后还有" + USER_MAP.size();}}

3.编写 Controller

package com.csp.mingyue.api.controller;import com.csp.mingyue.api.model.MingYueUser;import com.csp.mingyue.api.service.MingYueUserService;import lombok.RequiredArgsConstructor;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.DeleteMapping;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.PutMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** @author Strive */@RestController@RequiredArgsConstructor@RequestMapping("/user")public class MingYueUserController {private final MingYueUserService mingYueUserService;@GetMapping("/{userId}")public ResponseEntity<MingYueUser> queryUserById(@PathVariable Long userId) {return ResponseEntity.ok(mingYueUserService.queryUserById(userId));}@PostMappingpublic ResponseEntity<Long> addUser(@RequestBody MingYueUser user) {return ResponseEntity.ok(mingYueUserService.addUser(user));}@PutMappingpublic ResponseEntity<String> updateUser(@RequestBody MingYueUser user) {return ResponseEntity.ok(mingYueUserService.updateUser(user));}@DeleteMapping("/{userId}")public ResponseEntity<String> deleteUser(@PathVariable Long userId) {return ResponseEntity.ok(mingYueUserService.deleteUser(userId));}}

4.测试接口

下面的 Json 数据复制进入 Json 文件,Apifox 导入类型选择 Openapi/Swagger 即可测试接口,或者直接码云下载 mingyue-springboot-api Demo

{"openapi": "3.0.1","info": {"title": "mingyue-springboot-learning","description": "","version": "1.0.0"},"tags": [{"name": "mingyue-springboot-api"}],"paths": {"/user/{userId}": {"get": {"summary": "根据用户ID获取用户信息","x-apifox-folder": "mingyue-springboot-api","x-apifox-status": "developing","deprecated": false,"description": "","tags": ["mingyue-springboot-api"],"parameters": [{"name": "userId","in": "path","description": "","required": true,"example": "1","schema": {"type": "string"}}],"responses": {"200": {"description": "成功","content": {"application/json": {"schema": {"type": "object","properties": {}},"examples": {}}}}}},"delete": {"summary": "根据用户ID删除用户","x-apifox-folder": "mingyue-springboot-api","x-apifox-status": "developing","deprecated": false,"description": "","tags": ["mingyue-springboot-api"],"parameters": [{"name": "userId","in": "path","description": "","required": true,"example": "1","schema": {"type": "string"}}],"responses": {"200": {"description": "成功","content": {"application/json": {"schema": {"type": "object","properties": {}},"examples": {}}}}}}},"/user": {"put": {"summary": "更新用户","x-apifox-folder": "mingyue-springboot-api","x-apifox-status": "developing","deprecated": false,"description": "","tags": ["mingyue-springboot-api"],"parameters": [{"name": "Content-Type","in": "header","description": "","required": false,"example": "{{'application/json'}}","schema": {"type": "string"}}],"requestBody": {"content": {"text/plain": {"schema": {"type": "string"},"example": "{\"userId\":2,\"username\":\"Strive Update\"}"}}},"responses": {"200": {"description": "成功","content": {"application/json": {"schema": {"type": "object","properties": {}},"examples": {}}}}}},"post": {"summary": "添加用户","x-apifox-folder": "mingyue-springboot-api","x-apifox-status": "developing","deprecated": false,"description": "","tags": ["mingyue-springboot-api"],"parameters": [{"name": "Content-Type","in": "header","description": "","required": false,"example": "{{'application/json'}}","schema": {"type": "string"}}],"requestBody": {"content": {"text/plain": {"schema": {"type": "string"},"example": "{\"userId\":2,\"username\":\"Strive\"}"}}},"responses": {"200": {"description": "成功","content": {"application/json": {"schema": {"type": "object","properties": {}},"examples": {}}}}}}}},"components": {"schemas": {}}}