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支持特性的进一步实践。大家加油!

相关推荐
用户9083246027324 分钟前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
用户83071968408220 小时前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解21 小时前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解21 小时前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记1 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者2 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840822 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解2 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
初次攀爬者3 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺3 天前
搞懂@Autowired 与@Resuorce
java·spring boot·后端