springboot项目requestId设置、统一responsebody封装以及切面

发布于:2024-06-28 ⋅ 阅读:(18) ⋅ 点赞:(0)

利用filter设置requestId

import cn.hutool.core.lang.UUID;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Slf4j
@Component
public class RequestIdRelayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1.生成 RequestId
        String requestId = UUID.randomUUID().toString(true);
        // 2.保存到日志变量池
        MDC.put(REQUEST_ID_HEADER, requestId); //  "requestId"
        // 3.更新请求头,添加标示
        String path = exchange.getRequest().getPath().toString();
        exchange = exchange.mutate().request(b -> {
                    // 3.1.添加请求id标示
                    b.header(REQUEST_ID_HEADER, requestId);
                    // 3.2.添加网关标示
                    if (!path.startsWith("/ps/notify")) {
                        b.header(REQUEST_FROM_HEADER, GATEWAY_ORIGIN_NAME); 
                        // "x-request-from"  "gateway"
                    }
                }
        ).build();

        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

响应对象R

@Data
@ApiModel(description = "通用响应结果")
public class R<T> {
    @ApiModelProperty(value = "业务状态码,200-成功,其它-失败")
    private int code;
    @ApiModelProperty(value = "响应消息", example = "OK")
    private String msg;
    @ApiModelProperty(value = "响应数据")
    private T data;
    @ApiModelProperty(value = "请求id", example = "1af123c11412e")
    private String requestId;

    public static R<Void> ok() {
        return new R<Void>(SUCCESS, OK, null);
    }

    public static <T> R<T> ok(T data) {
        return new R<>(SUCCESS, OK, data);
    }

    public static <T> R<T> error(String msg) {
        return new R<>(FAILED, msg, null);
    }

    public static <T> R<T> error(int code, String msg) {
        return new R<>(code, msg, null);
    }

    public R() {
    }

    public R(int code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
        this.requestId = MDC.get(Constant.REQUEST_ID_HEADER);
    }

    public boolean success(){
        return code == SUCCESS;
    }

    public R<T> requestId(String requestId) {
        this.requestId = requestId;
        return this;
    }
}

处理Responsebody切面

参考https://blog.csdn.net/weixin_45734473/article/details/133343637

import org.slf4j.MDC;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

@RestControllerAdvice
public class WrapperResponseBodyAdvice implements ResponseBodyAdvice<Object> {

//Spring MVC的ResponseBodyAdvice接口中的supports方法主要是用于判断是否符合处理条件。
//这个方法的返回值是一个布尔值,当返回值为true时,会调用beforeBodyWrite方法对返回值进行处理,否则不进行处理。
    @Override
    public boolean supports(MethodParameter returnType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
        return returnType.getParameterType() != R.class && WebUtils.isGatewayRequest();
    }
//    上面的WebUtils.isGatewayRequest()
//    public static boolean isGatewayRequest() {
//        String originName = getHeader(Constant.REQUEST_FROM_HEADER); // "x-request-from"
//        return Constant.GATEWAY_ORIGIN_NAME.equals(originName); // "gateway"
//    }
    
    @Override
    public Object beforeBodyWrite(
            Object body, @NonNull MethodParameter returnType, @NonNull MediaType selectedContentType,
            @NonNull Class<? extends HttpMessageConverter<?>> selectedConverterType,
            @NonNull ServerHttpRequest request, @NonNull ServerHttpResponse response) {
        // swagger2默认的url后缀
        if (request.getURI().getPath().equals("/v2/api-docs")){
            return body;
        }
        if (body == null) {
            return R.ok().requestId(MDC.get(Constant.REQUEST_ID_HEADER));
            // "requestId"
        }
        if(body instanceof R){
            return body;
        }
        return R.ok(body).requestId(MDC.get(Constant.REQUEST_ID_HEADER)); //"requestId"
    }
}

MDC实现日志的链路追踪

详情参考https://mp.weixin.qq.com/s?__biz=MzAxMjY5NDU2Ng==&mid=2651854053&idx=2&sn=495849a51b126ce157fe7d034f82a4f1&chksm=804951acb73ed8ba8ea6fb11688655cbe76c20913a8e464acc11d02f05ee48af65afed59a9b6&scene=27

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到