利用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