Spring Cloud 零基础实战(六):Rest Api 开发

0、REST介绍

还是先从百度百科的 rest 词条开始吧!


百度百科的 rest 词条

正因为 REST 是目前比较流行的软件架构,所以网上有大量介绍及开发设计规范等的文章,而我此篇文章基于实战的目的,对 REST 关键的几个方面做一个简单的总结,当然和网上的其它文章一样,都是仁者见仁、智者见智,仅供参考!
本文将从下面几个方面入手:

  • URI命名
  • HTTP方法
  • HTTP 状态码
  • 返回数据封装格式
  • 接口文档

1、 URI 命名

1.1、命名规范

RFC 3986 定义了通用的URI语法:
URI = scheme “://” authority “/” path [ “?” query ][ “#” fragment ]
scheme: 指底层用的协议,如http、https、ftp
host: 服务器的IP地址或者域名
port: 端口,http中默认80
path: 访问资源的路径,就是咱们各种web 框架中定义的route路由
query: 为发送给服务器的参数
fragment: 锚点,定位到页面的资源,锚点为资源id

  • 网址中只能有名词,不能用动词
    在 RESTful 架构中,每个网址代表一种资源( resource ),所以网址中不能有动词,只能有名词(特殊情况可以使用动词),而且所用的名词往往与数据库的表格名对应
    示例:
// 正确规范:
/users // 表示列举所有用户

// 错误规范:
/listUsers   // 有动词
/getUsers    // 有动词
  • 不用大写
// 正确规范:
/users

// 错误规范:
/Users   // URI 不能有大写字母
  • 用中杠 - 不用下杠 _
// 正确规范:
/user-orders // 用户订单列表

// 错误规范:
/user_orders // 不使用下划线 _
  • URI 中的名词表示资源集合,使用复数形式
// 正确规范:
/users                 // 复数表示集合
/users/1               // id 为 1 的用户信息
/users/1/orders        // id 为 1 的用户的订单列表

错误规范:
/user                 // 不能用单数表示单个资源,需要用 id 来指定,参考 /user/1
  • 避免层级过深的 URI
    过深的 URI 不易维护,如 /users/1/orders/2/products/3 ,尽量使用查询参数代替路径中的实体导航,如 /products?user=1&order=1;
  • API 版本号要放入到 URI 中
// 正确规范:
/v1/users    // 表示 API 版本为 v1
  • URL结尾不应该包含斜杠“/”
    REST API不允许一个尾部的斜杠,不应该将它们包含在提供给客户端的链接的结尾处。
// 错误规范:
/v1/users/   // 末尾不能为“/”
  • 资源过滤
    在获取资源的时候,有可能需要获取某些“过滤”后的资源,例如指定前10行数据,如:
    http://api.test.com/v1/users?page=1&page-size=10
    下面是一些常用的过滤参数:
    ?limit=10:指定返回记录的数量
    ?offset=10:指定返回记录的开始位置。
    ?page=2&per_page=100:指定第几页,以及每页的记录数。
    ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
    ?animal_type_id=1:指定筛选条件

1.2、编写代码

为了简化,我们采用前面编写的演示工程的基础上进行代码编写。
用 Idea 打开 edu 演示工程,打开控制器 UseController 的代码:


定位到 UserController 代码

查、增、改、删,四个基本操作

上图四个框内的代码分别对应查、增、改、删四个操作的代码,下面进行 URI 规范的修改

  • 增加版本
    在都拿顶部的 @RestController 注解后面增加 @RequestMapping 注解,该注解对 UserController 控制器的所有 http 方法都起作用。假设 API 版本为 v1,修改结果如下:
    为 UserController 控制器添加版本

1.2.1、修改“查询” URI

  • 修改“查询” URI
    修改前

    上图中, @GetMapping 注解表示 http 方法为 GET ,注解的值为“/list",不规范,改为"/users"。
    修改后
  • 测试
    运行项目 UserApplication,用 Postman 测试访问,采用 GET 方法:
    测试:查询数据 - GET 方法

    OK!

1.2.2、修改“增加” URI

  • 修改“增加” URI

    修改前

    上图中, @PostMapping 注解表示 http 方法为 POST ,注解的值为“/addUser",不规范,改为"/users"。
    修改后

  • 测试
    重新运行 UserApplication ,用 Postman 测试访问,采用 POST 方法,并传递 username 和 password 两个参数:

    测试:增加数据 - POST 方法

1.2.3、修改“修改” URI

  • 修改“修改” URI
    修改前

    上图中, @PutMapping 注解表示 http 方法为 PUT ,注解的值为“/updUser/{id}",不规范,改为"/users/{id}"。
    修改后
  • 测试
    重新运行 UserApplication ,用 Postman 测试访问,采用 PUT 方法,并传递 username 和 password 两个参数:
    测试:修改数据 - PUT方法

    OK!

1.2.4、修改“删除” URI

  • 修改“删除” URI
    修改前

    上图中, @DeleteMapping 注解表示 http 方法为 DELETE,注解的值为“/delUser/{id}",不规范,改为"/users/{id}"。
    image.png
  • 测试
    重新运行 UserApplication ,用 Postman 测试访问,采用 DELETE 方法:
    测试: 删除数据 - DELETE 方法

    OK!

2、HTTP方法

常用的方法有 GET , POST , PUT , DELETE ,此外有时还会用到HEAD、PATCH和OPTION。前面说我们系统的设计是以资源为核心的,URI的命名要使用名词,那我们要如何来表示一个操作动作呢?就是依靠HTTP方法,也叫HTTP动词。也就是通过这种动名词的组合来表示一次操作。
从上面修改 URI 的示例里,已经基本讲述和测试了 GET , POST , PUT , DELETE 四种方法:
GET - 获取数据集合,用到的 URI 为:/v1/users
POST - 新增数据,用到的 URI 为:/v1/users,和 GET 方法的 URI 相同
PUT - 修改数据,用到的 URI 为:/v1/users/{id}
DELETE - 删除数据,用到的 URI 为:/v1/users/{id},和 PUT 方法的 URI 相同
通过列举这四种方法,可以看到,采用规范后的 URI , 简单、明晰了很多!
注:其它 http 方法用得很少,如果有兴趣可自行搜索及练习,此处不再赘述!

3、HTTP 状态码

服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。

  • 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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
    更多 HTTP 状态码,请到网上搜索。

4、返回数据封装格式

RestAPI 返回的封装格式有很多种,网上也分化出很多阵营,公说公有理、婆说婆有理,所以还是要自己多练习、感受,做出和项目规模适应的封装格式。

  • 最简格式
    我们刚才在例子里用的就是最简格式,对于调试或较简单的接口,可以这么用,但不推荐。
[
    {
        "id": 1,
        "username": "金庸",
        "password": "123456"
    },
    {
        "id": 2,
        "username": "古龙",
        "password": "654321"
    }
]
  • 自定义业务状态码封装格式
    该模式和业务联系紧密,可灵活定制,推荐。
{
    "status": 0,
    "message": "操作成功!",
    "data": [
        {
            "id": 1,
            "username": "金庸",
            "password": "123456"
        },
        {
            "id": 2,
            "username": "古龙",
            "password": "654321"
        }
    ],
    extra: {
        type: 1,
        desc: "附加操作"
    }
}
  • http 状态码 + 自定义业务状态码封装形式
    该模式分为两层状态码,最外层是 http 状态码,里面一层是自定义业务状态码。在 http rest 模式下,该数据封装形式既可以满足 http 状态码的统一性,有可以兼顾到业务逻辑状态码的灵活定制、描述的准确性。我更推荐使用该模式。
{
    "code": 200,
    "msg": "OK",
    {
        "status": 0,
        "message": "操作成功!",
        "data": [
            {
                "id": 1,
                "username": "金庸",
                "password": "123456"
            },
            {
                "id": 2,
                "username": "古龙",
                "password": "654321"
            }
        ],
        extra: {
            type: 1,
            desc: "附加操作"
        }
    }
}

5、 API 接口文档

在此,我们将使用 Springfox(Swagger) 来实现 ApiDoc 接口文档, Springfox 的前身是 Swagger-SpringMVC ,是一个开源的 API doc 框架,可以将我们的Controller的方法以文档的形式展现,基于Swagger。官网地址:http://springfox.github.io/springfox/
下面我们继续在演示项目 edu 上面来进行 API 接口文档的实现,如果这是您没看本系列前面的文章,请快速浏览一下前面的文章。

5.1、增加 Maven 依赖

我们在 microsvc 项目的 pom.xml 文件里添加下面的依赖:

<!--springfox-->
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger2</artifactId>
   <version>2.4.0</version>
</dependency>
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger-ui</artifactId>
   <version>2.4.0</version>
</dependency>
在 microsvc 项目 pom.xml 文件中添加 swagger 依赖

5.2、修改 user 微服务的启动程序文件 - UserApplication.java

import 导入:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.request.async.DeferredResult;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

添加注解:

@EnableSwagger2

在启动类 UserApplication 里的 main 函数后面添加下面的代码:

    @Bean
    public Docket userApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("user")
                .genericModelSubstitutes(DeferredResult.class)
                //  .genericModelSubstitutes(ResponseEntity.class)
                .useDefaultResponseMessages(false)
                .forCodeGeneration(false)
                .pathMapping("/")
                .select()
                .paths(PathSelectors.regex("/v1.*"))//过滤的接口
                .build()
                .apiInfo(userApiInfo());
    }

    private ApiInfo userApiInfo() {
        Contact contact = new Contact("檀布施", "https://www.baidu.com", "tanbushi@qq.com");
        ApiInfo apiInfo = new ApiInfo("用户 API 接口",//大标题
                "用户 REST 风格 API",//小标题
                "v1",//版本
                "www.baidu.com",
                contact,//作者
                "主页",//链接显示文字
                ""//网站链接
        );
        return apiInfo;
    }

修改完成后,文件内容如下图:


UserApplication 的内容

5.3、修改 user 微服务的控制器文件 - UserController.java

import 导入:

import com.tanbushi.edu.microsvc.user.entity.UserEntity;
import com.tanbushi.edu.microsvc.user.repository.UserRepository;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;

在 UserController 类里的每一个 mapping 后面追加一句,下图红框里的内容:


UserController 内容修改
    @GetMapping(value = "/users")
    @ApiOperation(value = "获取用户列表", notes = "获取当前条件下的用户列表", produces = "application/json")
    @PostMapping(value = "/users")
    @ApiOperation(value = "添加用户记录", notes = "添加一条用户记录,username、password 必填", produces = "application/json")
    @PutMapping(value = "/users/{id}")
    @ApiOperation(value = "修改用户记录", notes = "修改一条用户记录,usernme、password必填", produces = "application/json")
    @DeleteMapping(value = "/users/{id}")
    @ApiOperation(value = "删除用户记录", notes = "删除一条用户记录", produces = "application/json")

5.4、运行

重新编译运行 UserApplication,成功后,在浏览器里输入:http://127.0.0.1:8100/swagger-ui.html,可以访问到 API 文档了:

API doc 界面

点击红框内的 user-controller, 就可以看的具体的接口说明了:
接口说明

6、其它

  • 消息体格式 - 推荐尽量采用 JSON 格式
  • 鉴权 - 采用 Autho2.0
    在此不再赘述

上一篇:Spring Cloud 零基础实战(五):访问 MySQL 数据库
下一篇:待续...
所属文集:Spring Cloud 零基础实战


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342