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。

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接口

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接口,可以简化为下面形式:

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格式响应时,会有坑存在,后续讲到时再关注。

package com.juan.demo.api;

import ...

public interface HelloAPI {

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java小卷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值