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

相关推荐
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭2 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
AskHarries4 小时前
Spring Cloud OpenFeign快速入门demo
spring boot·后端
isolusion5 小时前
Springboot的创建方式
java·spring boot·后端
Yvemil76 小时前
《开启微服务之旅:Spring Boot Web开发举例》(一)
前端·spring boot·微服务
星河梦瑾7 小时前
SpringBoot相关漏洞学习资料
java·经验分享·spring boot·安全
计算机学长felix8 小时前
基于SpringBoot的“交流互动系统”的设计与实现(源码+数据库+文档+PPT)
spring boot·毕业设计
.生产的驴8 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
顽疲8 小时前
springboot vue 会员收银系统 含源码 开发流程
vue.js·spring boot·后端
撒呼呼9 小时前
# 起步专用 - 哔哩哔哩全模块超还原设计!(内含接口文档、数据库设计)
数据库·spring boot·spring·mvc·springboot
因我你好久不见9 小时前
springboot java ffmpeg 视频压缩、提取视频帧图片、获取视频分辨率
java·spring boot·ffmpeg