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

上一篇:Spring Boot 3.x Rest API最佳实践之API设计

下一篇:Spring Boot 3.x Rest API最佳实践之统一响应结构

接着上一篇《API设计》咱们对定义好的API接口做简单的实现,会发现实现controller组件时,我们会将关注点从路径怎么定义、参数怎么定义以及注解怎么编写等等,转向控制器逻辑实现上来。这就是关注点分离,专心的干一件事。如果觉得对你有帮助,记得点赞收藏,关注小卷,后续更精彩!

在这里插入图片描述

controller简单实现

对前面我们定义的三个API接口,实现Controller很简单,只要实现相应的接口,并在类的头部加RestController注解即可。这里我们先对接口做简单的mock实现。

package com.juan.demo.web.controller;

import ...

@RestController
public class HelloController implements HelloAPI {

    @Override
    public String hello() {
        return "hello spring boot3.0";
    }

}

对于后台商品管理API接口的实现,这里我们暂且对保存和删除操作简单打印日志,对查询操作返回构造的数据:

package com.juan.demo.web.controller;

import ...

@Slf4j
@RestController
public class ProductAdminController implements ProductAdminAPI {

    @Override
    public void addProduct(ProductSaveDTO saveDTO) {
        log.info("add product: {}", saveDTO);
    }

    @Override
    public void updateProduct(ProductSaveDTO saveDTO) {
        log.info("update product: {}", saveDTO);
    }

    @Override
    public ProductDetailDTO getProduct(long id) {
        return new ProductDetailDTO(1L, "spring boot3入门", 30000);
    }

    @Override
    public void deleteProduct(long id) {
        log.info("delete product id = {}", id);
    }

    @Override
    public List<ProductResultItemDTO> listProducts() {
        return List.of(
                new ProductResultItemDTO(1L, "spring boot3基础入门", 30000),
                new ProductResultItemDTO(2L, "Vue3入门", 20000)
        );
    }
}

对于添加购物车API接口的实现逻辑,是我们后续要完善的:

package com.juan.demo.web.controller;

import ...

@Slf4j
@RestController
public class CartController implements CartAPI {

    @Override
    public void addCartItem(CartItemDTO cartItemDTO) {
        log.info("添加购物车成功");
    }
}

说下这里的实现逻辑,后台完成添加购物车逻辑后会将更新后的购物车数据列表查出来,序列化为json数据,并写入到客户端Cookie。这里用到了HttpServletResponse对象,为了避免通过对请求响应对象注入的倾入式设计,后续我们会使用RequestContextHolder的静态方法getRequestAttributes(),因此下面的接口定义是不可取的:

...

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

    @PostMapping
    void addCartItem(@RequestBody CartItemDTO cartItemDTO, HttpServletResponse response);

}

购物车接口简单实现

前面我们提到对于HttpServletRequestHttpServletResponse等对象通过API接口方法参数注入的形式是有倾入性的,为此我们可以在一个工具类中封装获取方式:

package com.juan.demo.util;

import ...

public class RequestContextUtil {

    public static HttpServletResponse getResponse() {
        ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attrs != null) return attrs.getResponse();
        return null;
    }

}

这样,我们再来实现下添加购物车的Controller方法逻辑:

@Slf4j
@RestController
public class CartController implements CartAPI {

    @Override
    public void addCartItem(CartItemDTO cartItemDTO) {

        // todo 先写死要写入cookie的json数据
        String cartDataJson = "[{\"p\":10,\"q\":2}]";
        log.info("更新后的用户购物车数据:{}", cartDataJson);

        Cookie cookie = new Cookie("cart_data", URLEncoder.encode(cartDataJson, StandardCharsets.UTF_8));
        Objects.requireNonNull(RequestContextUtil.getResponse()).addCookie(cookie);


        log.info("添加购物车成功");

    }
}

这里我们对构造出来的购物车json数据,通过URL编码后作为入参来构造Cookie对象,并调用之前写好的工具类RequestContextUtil来获取response对象,并写入cookie数据。这里我们通过显式的调用Objects.requireNonNull(...),表明这里获取的对象不应该出现获取为空的情况。

实际添加逻辑,我们将采用一个CartService接口的实现来封装,这里定义一个service接口:

package com.juan.demo.service;

import ...

public interface CartService {

    /**
     * 添加购物车:对指定的商品添加一定的数量
     * @param addItemDTO
     * @return 更新后的购物车数据
     */
    List<CartItemDTO> addCartItem(CartItemDTO addItemDTO);

}

实现类CartServiceImpl被用@Service注解为一个service组件类,这里我们将要实现的逻辑写下来,方便后续进一步实现,这里先简单返回构造的数据:

package com.juan.demo.service.impl;

import ...

@Service
public class CartServiceImpl implements CartService {
    @Override
    public List<CartItemDTO> addCartItem(CartItemDTO addItemDTO) {

        /* todo
         * 1. 判断productId是否添加过,未添加则插入一条记录
         * 2. 否则,更新购物车条目数量
         * 3. 查询更新后的购物车条目列表
         */
        return List.of(
                new CartItemDTO(1L, 2),
                new CartItemDTO(2L, 3)
        );
    }
}

CartController中注入相关组件,并完善添加购物车的调用和处理逻辑:

package com.juan.demo.web.controller;

import ...

...
public class CartController implements CartAPI {

    @Resource
    private CartService cartService;

    @Resource
    private ObjectMapper objectMapper;

    @SneakyThrows
    @Override
    public void addCartItem(CartItemDTO cartItemDTO) {


        // 实现后台添加购物车,返回添加后的购物车数据
        List<CartItemDTO> cartData = cartService.addCartItem(cartItemDTO);

        // 实现数据json序列化后,写入cookie
        String cartDataJson = objectMapper.writeValueAsString(cartData);
        
        // 写入cookie代码省略
		...
    }
}

这里我们注入了两个组件:CartServiceObjectMapper,用前者调用添加购物车的方法,再用后者进行购物车数据列表的json字符串序列化操作,注意,这里会抛出受检查异常,我们采用方法头部的@SneakyThrows注解进行了优雅的抛出处理。

API客户端测试

对于前面编写好的API,要进行测试,传统的方式是采用类似于PostMan的工具进行填写和调用,这里更推荐idea自带的测试工具,支持.http后缀的客户端工具。

我们在项目中新建一个test-http目录,新建几个.http后缀的模块测试文件

在这里插入图片描述

hello.http

### 测试hello api的字符串请求
GET http://localhost:8080/hello

cart.http

### 添加购物车
POST http://localhost:8080/personal/cart
Content-Type: application/json

{
  "p": 2,
  "q": 4
}

product.http

### 测试商品详情查询
GET http://localhost:8080/admin/products/1

### 查询商品列表
GET http://localhost:8080/admin/products

### 删除商品
DELETE http://localhost:8080/admin/products/1

### 新增商品
POST http://localhost:8080/admin/products
Content-Type: multipart/form-data;boundary=WebAppBoundary

--WebAppBoundary
Content-Disposition: form-data; name="name"
Content-Type: text/plain

Spring Boot 3.0实战
--WebAppBoundary
Content-Disposition: form-data; name="price"
Content-Type: text/plain

16800
--WebAppBoundary

### 修改商品
PUT http://localhost:8080/admin/products
Content-Type: multipart/form-data;boundary=WebAppBoundary

--WebAppBoundary
Content-Disposition: form-data; name="id"
Content-Type: text/plain

1
--WebAppBoundary
Content-Disposition: form-data; name="price"
Content-Type: text/plain

12800
--WebAppBoundary

测试截图:

在这里插入图片描述

读者可以基于上述http调用脚本,对开发的服务进行自行测试,这里给出spring boot控制台输出的日志:

在这里插入图片描述

测试发现的问题:

  • 对于void,没有响应体
  • 返回的结构不统一

下一小节,我们将采用spring boot提供的统一响应API的拦截处理特性来完成Rest API正常响应格式的统一输出,大家加油!

  • 25
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Java小卷

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

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

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

打赏作者

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

抵扣说明:

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

余额充值