Spring Boot 3.x Rest API最佳实践之API设计

本系列教程将会以企业中API基础功能封装为目标,用最新的Spring Boot 3.x版本来逐步搭建和完善Rest API项目基础架构,并结合实际电商项目中API的实现需求来进行最佳实践。如果觉得对你有帮助,记得点赞收藏,关注小卷,后续更精彩!

API设计原则

企业最佳实践的API设计会从Controller组件中将API接口剥离出来,以方便对API定义进行单独维护,比如增加各种注解(请求、参数映射注解、swagger在线文档注解、校验注解等等),这样Controller组件将会变的很干净,只需要关注方法实现即可,这就是面向接口编程。

一定要定义API接口吗?我可以直接写Controller吗?

当然可以直接写Controller组件,因为不同于service层,控制器是web框架调用的,抽取出接口貌似显得没有必要。但是如果是大中型的企业项目,即便做了微服务拆分,每个微服务的API或许还是有几十个,接口更方便维护,而且可以借助swagger的API生成工具帮助我们生成API接口。后续我们将演示其用法。

DTO

一般在API接口接收的参数列表长度超出3时,我们就考虑用POJO对象来接收,在表示层携带数据进行传输的对象,我们用DTO。DTO的设计原则:

  • 基本类型用包装类型

    没有逻辑上的默认值,则默认值都为null

  • DTO不要公用

    尽量为每个API的出入参定义自己的DTO,即便里面大多数字段相同,这样在维护时不会相互影响

  • DTO的字段和json字段建立别名的映射

    在DTO进行序列化或反序列化时,与之对应的json字段,一般是下划线的写法;而如果序列化的json需要被存储时尽量把别名起的短些,以减少存储空间

比如,添加购物车CartItemDTO,用作添加购物车接口的入参和出参,作为出参时返回一个列表,代表当前用户的购物车中持久化的所有数据,这些数据会序列化为json字符串同步写入浏览器cookie。

java 复制代码
package com.juan.demo.dto;

import ...

@NoArgsConstructor
@AllArgsConstructor
@Data
public class CartItemDTO {

    @JsonProperty("p")
    private Long productId;

    @JsonProperty("q")
    private Integer quantity;

}

这里对添加的商品id和添加的数量字段使用了@JsonProperty注解的简化别名,这样序列化的json字符串可以保存cookie时有更多的空间。

这里对字段的gettersettertoString()以及无参构造、全参构造都用了Lombok提供的注解来简化开发,后续DTO的定义中也会采用这种形式。

再来看商品相关的信息这里建了3个DTO类:

  • ProductDetailDTO

    用于展示商品详情

  • ProductResultItemDTO

    用于展示查询列表项

  • ProductSaveDTO

    用于接收编辑的商品信息,以进行后续的保存操作

注意

这里为啥不建立一个ProductDTO,而要为每个API接口单独建一个DTO。可能有些小伙伴平时开发一个模块时,会为一个模块的保存和查询API接口建一个公用的DTO,这是不好的习惯,至少要分开建读写操作的DTO,因为在写入时可以对DTO字段增加校验注解,而读取操作返回的DTO不需要,可能会加一些格式化json数据的注解。

API接口

我们要始终遵循面向接口开发的思想,即便只有一种实现。比如这里的Controller实现,如果我们希望扩展出另一个mock数据的Controller实现,你就明白抽取接口的重要性了。

后台商品管理API接口

java 复制代码
package com.juan.demo.api;

import ...

@RequestMapping("admin/products")
public interface ProductAdminAPI {

    @PostMapping
    void addProduct(ProductSaveDTO saveDTO);

    @PutMapping
    void updateProduct(ProductSaveDTO saveDTO);

    @GetMapping("{id}")
    ProductDetailDTO getProduct(@PathVariable("id") long id);

    @DeleteMapping("{id}")
    void deleteProduct(@PathVariable("id") long id);

    @GetMapping
    List<ProductResultItemDTO> listProducts();

}

说明

原先Controller中声明的各种注解,除了RestController,其他的都放到接口中来声明。

这里对商品资源的各种操作API的定义,遵循了Rest API的定义规范,对资源的不同操作,对应了不同的请求方法,这样url得以简化。

还要注意api的命名习惯,最前面是模块,然后是资源,这样方便我们后面拦截模块来做认证和授权。

关于返回值,这里我们会直接返回后台服务操作的实际结果类型,而不是通用的类型,因为通用结构我们后续会通过实现ResponseBodyAdvice接口来实现。

添加购物车API接口,可以简化为下面形式:

java 复制代码
package com.juan.demo.api;

import ...

@RequestMapping("personal/cart")
public interface CartAPI {

    @PostMapping
    void addCartItem(@RequestBody CartItemDTO cartItemDTO);

}

再添加一个直接返回String类型的hello API接口,注意,当直接返回字符串作为json内容的数据域时,spring boot框架默认会响应text/plain格式的数据,这个通过实现ResponseBodyAdvice接口来进行统一json格式响应时,会有坑存在,后续讲到时再关注。

java 复制代码
package com.juan.demo.api;

import ...

public interface HelloAPI {

    @GetMapping("hello")
    String hello();
}

下一小节,我们将对以上设计的API做一个简单的实现,并在此基础上进行Spring BootAPI支持特性的进一步实践。大家加油!

相关推荐
安冬的码畜日常33 分钟前
【JUnit实战3_29】第十八章:REST API 接口测试(上)——RESTful 风格的后端 API 的搭建
测试工具·单元测试·restful·rest api·junit 5
淘源码d1 小时前
什么是医院随访系统?成熟在用的智慧随访系统源码
java·spring boot·后端·开源·源码·随访系统·随访系统框架
Q_Q19632884752 小时前
python+django/flask基于机器学习的就业岗位推荐系统
spring boot·python·django·flask·node.js·php
ArabySide2 小时前
【Spring Boot】REST与RESTful详解,基于Spring Boot的RESTful API实现
spring boot·后端·restful
程序定小飞3 小时前
基于springboot的学院班级回忆录的设计与实现
java·vue.js·spring boot·后端·spring
攀小黑3 小时前
基于若依-内容管理动态修改,通过路由字典配置动态管理
java·vue.js·spring boot·前端框架·ruoyi
郝开4 小时前
Spring Boot 2.7.18(最终 2.x 系列版本)1 - 技术选型:连接池技术选型对比;接口文档技术选型对比
java·spring boot·spring
安冬的码畜日常5 小时前
【JUnit实战3_30】第十八章:REST API 接口测试(下)—— REST API 接口的 MockMvc + JUnit 5 测试实战
测试工具·junit·单元测试·restful·rest api·junit5
知兀5 小时前
【Spring/SpringBoot】SSM(Spring+Spring MVC+Mybatis)方案、各部分职责、与Springboot关系
java·spring boot·spring
汤姆yu5 小时前
基于springboot的民间救援队救助系统
java·spring boot·后端·救援队