第五节 SpringBoot统一全局响应
一、基础响应
前后端分离一定会设计到数据传输。因此,每个项目必定都有一个基础的响应类。
我就以我个人的经验编写这个类吧。定义基础:响应码code、响应信息message、传输的数据data、总记录数。
定义两个基础的构造函数,再定义两个通过枚举的构造函数。
package com.zhoutianyu.learnspringboot.response;
import lombok.Data;
@Data
public class BaseResponse<T> {
private int code;
private String message;
private T data;
private Long total;
public BaseResponse(int code, String message) {
this.code = code;
this.message = message;
this.data = null;
}
public BaseResponse(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public BaseResponse(StatusCodeEnum statusCodeEnum) {
this.code = statusCodeEnum.getCode();
this.message = statusCodeEnum.getMessage();
this.data = null;
}
public BaseResponse(StatusCodeEnum statusCodeEnum, T data) {
this.code = statusCodeEnum.getCode();
this.message = statusCodeEnum.getMessage();
this.data = data;
}
}
响应码枚举类,封装一些常用的响应码与响应信息。
package com.zhoutianyu.learnspringboot.response;
import lombok.Getter;
@Getter
public enum StatusCodeEnum {
SUCCESS(200, "SUCCESS"), ERROR(500, "Business Exception"), INVALID(-1, "Illegal Argument");
private int code;
private String message;
StatusCodeEnum(int code, String message) {
this.code = code;
this.message = message;
}
}
我们的目标就是让前端同事,收到传给他们的的code、message、data 数据。
编写如下测试类。
package com.zhoutianyu.learnspringboot.test;
import com.zhoutianyu.learnspringboot.exception.FieldInvalidException;
import com.zhoutianyu.learnspringboot.response.BaseResponse;
import com.zhoutianyu.learnspringboot.response.StatusCodeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SomethingController {
private static final Logger LOGGER = LoggerFactory.getLogger(SomethingController.class);
@GetMapping(value = "/response/test")
public BaseResponse buy(String param) {
LOGGER.info("paramType is {}", param);
return new BaseResponse<>(StatusCodeEnum.SUCCESS, param);
}
}
重启项目并访问:http://localhost:8081/study/springboot/response/test?param=abc
可以访问到上述方法。方法按照预期的响应。
二、统一响应模板
刚才编写的方法,返回的就是一个BaseResponse对象。但是,如果某个方法返回的是一个字符串,那么这不符合前端接收的规则。为了减少与前端同事不必要的撕逼,我们后端应该对这些单个返回的数据进行处理。
让数据从 "我是数据" 这种格式 ,修改为 { code : 200 , message : SUCCESS , data : 我是数据 }。
比如现在有如下方法,在浏览器访问:
http://localhost:8081/study/springboot/response/test/string?param=l_am_data
@GetMapping(value = "/response/test/string")
public String buyTwo(String param) {
LOGGER.info("paramType is {}", param);
return param;
}
如何处理呢。这里用到了AOP的思想。在返回给前端之前,用切面对返回值做一次数据处理。
SpringBoot对切面的支持真的非常的友好。我们这个需求只需要实现一个叫做ResponseBodyAdvice的接口。Advice结尾就知道是一个AOP的通知。@ResponseBody注解的功能就是将返回数据映射成JSON格式。而我们就在他映射之前,修改掉返回数据。
下面的代码的大致思路是:看看后台返回的数据,是不是符合标准响应(BaseResponse或者是ExceptionResponse)。如果是标准响应,那么就不做处理。如果不是上述的标准响应,那么就将返回数据处理成标准响应。
package com.zhoutianyu.learnspringboot.response;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object data, MethodParameter methodParameter,
MediaType mediaType, Class aClass,
ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (isResponseType(data)) {
return data;
}
return new BaseResponse<>(StatusCodeEnum.SUCCESS, data);
}
private boolean isResponseType(Object data) {
return data instanceof BaseResponse;
}
}
还需要为系统添加一个配置,否则上述切面无法正常工作。原因不详:我搜集到的参考链接:
https://www.jianshu.com/p/ed55bbf676fb
https://www.cnblogs.com/xingzc/p/8656088.html
package com.zhoutianyu.learnspringboot.config;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CustomWebConfigure implements WebMvcConfigurer {
@Bean
public HttpMessageConverters customConverters() {
return new HttpMessageConverters(new FastJsonHttpMessageConverter());
}
}
<!-- fast json-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
还会有乱码的问题。在application.yml中加入下面的参数即可。
spring:
http:
encoding:
charset: UTF-8
force: true
enabled: true
messages:
encoding: UTF-8
banner:
charset: UTF-8
最后的测试:http://localhost:8081/study/springboot/response/test/string?param=
三、源码下载
本章节项目源码:点我下载源代码
2020年1月4日适配分页
package com.zhoutianyu.learnspringboot.response;
import com.zhoutianyu.learnspringboot.interceptor.PageHelperThreadLocal;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object data, MethodParameter methodParameter,
MediaType mediaType, Class aClass,
ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (isResponseType(data)) {
return data;
}
//构造适配数据
BaseResponse response = new BaseResponse<>(StatusCodeEnum.SUCCESS, data);
//适配数量
if (null != PageInfo.getRowBounds().getTotal()) {
response.setTotal(PageInfo.getRowBounds().getTotal());
}
return response;
}
private boolean isResponseType(Object data) {
return data instanceof BaseResponse || data instanceof ExceptionResponse;
}
}