第五节 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=

三、源码下载

        本章节项目源码:点我下载源代码

        目录贴:跟着大宇学SpringBoot-------目录帖

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;
    }
}

 

posted @ 2022-07-17 12:14  小大宇  阅读(487)  评论(0编辑  收藏  举报