diff --git a/fizz-bootstrap/src/main/resources/application.yml b/fizz-bootstrap/src/main/resources/application.yml index c96df6a..7c74337 100644 --- a/fizz-bootstrap/src/main/resources/application.yml +++ b/fizz-bootstrap/src/main/resources/application.yml @@ -106,10 +106,16 @@ refresh-local-cache: fizz: aggregate: writeMapNullValue: false + error: + response: + http-status.enable: true # default + code-field: "msgCode" # default + message-field: "message" # default fizz-trace-id: - header: X-Trace-Id # default - value-strategy: requestId # default, or can be uuid - value-prefix: fizz # default + header: X-Trace-Id # default + value-strategy: requestId # default, or can be uuid + value-prefix: fizz # default cors: true # CORS switch, default true + diff --git a/fizz-core/src/main/java/we/config/SystemConfig.java b/fizz-core/src/main/java/we/config/SystemConfig.java index 764cb21..a69fe1b 100644 --- a/fizz-core/src/main/java/we/config/SystemConfig.java +++ b/fizz-core/src/main/java/we/config/SystemConfig.java @@ -46,13 +46,19 @@ public class SystemConfig { private static final Logger log = LoggerFactory.getLogger(SystemConfig.class); - public static final String DEFAULT_GATEWAY_PREFIX = "/proxy"; + public static final String DEFAULT_GATEWAY_PREFIX = "/proxy"; - public static final String DEFAULT_GATEWAY_TEST_PREFIX = "/_proxytest"; + public static final String DEFAULT_GATEWAY_TEST_PREFIX = "/_proxytest"; - public static final String DEFAULT_GATEWAY_TEST = "_proxytest"; + public static final String DEFAULT_GATEWAY_TEST = "_proxytest"; - public static final String DEFAULT_GATEWAY_TEST_PREFIX0 = "/_proxytest/"; + public static final String DEFAULT_GATEWAY_TEST_PREFIX0 = "/_proxytest/"; + + public static boolean FIZZ_ERR_RESP_HTTP_STATUS_ENABLE = true; + + public static String FIZZ_ERR_RESP_CODE_FIELD = "msgCode"; + + public static String FIZZ_ERR_RESP_MSG_FIELD = "message"; private String gatewayPrefix = DEFAULT_GATEWAY_PREFIX; @@ -78,6 +84,21 @@ public class SystemConfig { @Value("${fizz-trace-id.value-prefix:fizz}") private String fizzTraceIdValuePrefix; + @Value("${fizz.error.response.http-status.enable:true}") + public void setFizzErrRespHttpStatusEnable(boolean fizzErrRespHttpStatusEnable) { + FIZZ_ERR_RESP_HTTP_STATUS_ENABLE = fizzErrRespHttpStatusEnable; + } + + @Value("${fizz.error.response.code-field:msgCode}") + public void setFizzErrRespCodeField(String fizzErrRespCodeField) { + FIZZ_ERR_RESP_CODE_FIELD = fizzErrRespCodeField; + } + + @Value("${fizz.error.response.message-field:message}") + public void setFizzErrRespMsgField(String fizzErrRespMsgField) { + FIZZ_ERR_RESP_MSG_FIELD = fizzErrRespMsgField; + } + public String fizzTraceIdHeader() { return fizzTraceIdHeader; } diff --git a/fizz-core/src/main/java/we/filter/FilterExceptionHandlerConfig.java b/fizz-core/src/main/java/we/filter/FilterExceptionHandlerConfig.java index c0b9437..e00395e 100644 --- a/fizz-core/src/main/java/we/filter/FilterExceptionHandlerConfig.java +++ b/fizz-core/src/main/java/we/filter/FilterExceptionHandlerConfig.java @@ -27,9 +27,11 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebExceptionHandler; import reactor.core.publisher.Mono; +import we.config.SystemConfig; import we.exception.ExecuteScriptException; import we.exception.RedirectException; import we.exception.StopAndResponseException; @@ -48,11 +50,22 @@ import java.net.URI; public class FilterExceptionHandlerConfig { public static class FilterExceptionHandler implements WebExceptionHandler { + private static final Logger log = LoggerFactory.getLogger(FilterExceptionHandler.class); private static final String filterExceptionHandler = "filterExceptionHandler"; + @Override public Mono handle(ServerWebExchange exchange, Throwable t) { + String traceId = WebUtils.getTraceId(exchange); ServerHttpResponse resp = exchange.getResponse(); + if (SystemConfig.FIZZ_ERR_RESP_HTTP_STATUS_ENABLE) { + if (t instanceof ResponseStatusException) { + resp.setStatusCode( ((ResponseStatusException) t).getStatus() ); + } else { + resp.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + if (t instanceof StopAndResponseException) { StopAndResponseException ex = (StopAndResponseException) t; if (ex.getData() != null) { @@ -68,39 +81,41 @@ public class FilterExceptionHandlerConfig { return Mono.empty(); } } - String rid = WebUtils.getTraceId(exchange); + if (t instanceof ExecuteScriptException) { ExecuteScriptException ex = (ExecuteScriptException) t; resp.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); RespEntity rs = null; if (ex.getStepContext() != null && ex.getStepContext().returnContext()) { - rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), rid, ex.getStepContext()); + rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), traceId, ex.getStepContext()); return resp.writeWith(Mono.just(resp.bufferFactory().wrap(JacksonUtils.writeValueAsString(rs).getBytes()))); } else { - rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), rid); + rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), traceId); return resp.writeWith(Mono.just(resp.bufferFactory().wrap(rs.toString().getBytes()))); } } + if (t instanceof FizzRuntimeException) { FizzRuntimeException ex = (FizzRuntimeException) t; - log.error(ex.getMessage(), LogService.BIZ_ID, rid, ex); + log.error(ex.getMessage(), LogService.BIZ_ID, traceId, ex); resp.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); RespEntity rs = null; if (ex.getStepContext() != null && ex.getStepContext().returnContext()) { - rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), rid, ex.getStepContext()); + rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), traceId, ex.getStepContext()); return resp.writeWith(Mono.just(resp.bufferFactory().wrap(JacksonUtils.writeValueAsString(rs).getBytes()))); } else { - rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), rid); + rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), traceId); return resp.writeWith(Mono.just(resp.bufferFactory().wrap(rs.toString().getBytes()))); } } + Mono vm; Object fc = exchange.getAttribute(WebUtils.FILTER_CONTEXT); if (fc == null) { // t came from flow control filter StringBuilder b = ThreadContext.getStringBuilder(); WebUtils.request2stringBuilder(exchange, b); - log.error(b.toString(), LogService.BIZ_ID, rid, t); - String s = RespEntity.toJson(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), rid); + log.error(b.toString(), LogService.BIZ_ID, traceId, t); + String s = WebUtils.jsonRespBody(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), traceId); resp.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); vm = resp.writeWith(Mono.just(resp.bufferFactory().wrap(s.getBytes()))); } else { diff --git a/fizz-core/src/main/java/we/filter/FlowControlFilter.java b/fizz-core/src/main/java/we/filter/FlowControlFilter.java index 231a904..b678cb0 100644 --- a/fizz-core/src/main/java/we/filter/FlowControlFilter.java +++ b/fizz-core/src/main/java/we/filter/FlowControlFilter.java @@ -107,9 +107,10 @@ public class FlowControlFilter extends FizzWebFilter { } if (flowControlFilterProperties.isFlowControl() && !adminReq && !proxyTestReq) { - LogService.setBizId(exchange.getRequest().getId()); + String traceId = WebUtils.getTraceId(exchange); + LogService.setBizId(traceId); if (!apiConfigService.serviceConfigMap.containsKey(service)) { - String json = RespEntity.toJson(HttpStatus.FORBIDDEN.value(), "no service " + service, exchange.getRequest().getId()); + String json = WebUtils.jsonRespBody(HttpStatus.FORBIDDEN.value(), "no service " + service, traceId); return WebUtils.buildJsonDirectResponse(exchange, HttpStatus.FORBIDDEN, null, json); } String app = WebUtils.getAppId(exchange); @@ -125,9 +126,9 @@ public class FlowControlFilter extends FizzWebFilter { if (result != null && !result.isSuccess()) { String blockedResourceId = result.getBlockedResourceId(); if (BlockType.CONCURRENT_REQUEST == result.getBlockType()) { - log.info("exceed {} flow limit, blocked by maximum concurrent requests", blockedResourceId, LogService.BIZ_ID, exchange.getRequest().getId()); + log.info("exceed {} flow limit, blocked by maximum concurrent requests", blockedResourceId, LogService.BIZ_ID, traceId); } else { - log.info("exceed {} flow limit, blocked by maximum QPS", blockedResourceId, LogService.BIZ_ID, exchange.getRequest().getId()); + log.info("exceed {} flow limit, blocked by maximum QPS", blockedResourceId, LogService.BIZ_ID, traceId); } ResourceRateLimitConfig c = resourceRateLimitConfigService.getResourceRateLimitConfig(ResourceRateLimitConfig.NODE_RESOURCE); diff --git a/fizz-core/src/main/java/we/filter/RouteFilter.java b/fizz-core/src/main/java/we/filter/RouteFilter.java index 2b68a07..e21c51f 100644 --- a/fizz-core/src/main/java/we/filter/RouteFilter.java +++ b/fizz-core/src/main/java/we/filter/RouteFilter.java @@ -80,14 +80,18 @@ public class RouteFilter extends FizzWebFilter { Mono resp = WebUtils.getDirectResponse(exchange); if (resp == null) { // should not reach here ServerHttpRequest clientReq = exchange.getRequest(); - String rid = WebUtils.getTraceId(exchange); + String traceId = WebUtils.getTraceId(exchange); String msg = pfr.id + " fail"; if (pfr.cause == null) { - log.error(msg, LogService.BIZ_ID, rid); + log.error(msg, LogService.BIZ_ID, traceId); } else { - log.error(msg, LogService.BIZ_ID, rid, pfr.cause); + log.error(msg, LogService.BIZ_ID, traceId, pfr.cause); } - return WebUtils.buildJsonDirectResponseAndBindContext(exchange, HttpStatus.OK, null, RespEntity.toJson(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg, rid)); + HttpStatus s = HttpStatus.INTERNAL_SERVER_ERROR; + if (!SystemConfig.FIZZ_ERR_RESP_HTTP_STATUS_ENABLE) { + s = HttpStatus.OK; + } + return WebUtils.buildJsonDirectResponseAndBindContext(exchange, s, null, WebUtils.jsonRespBody(s.value(), msg, traceId)); } else { return resp; } @@ -97,7 +101,7 @@ public class RouteFilter extends FizzWebFilter { private Mono doFilter0(ServerWebExchange exchange, WebFilterChain chain) { ServerHttpRequest req = exchange.getRequest(); - String rid = WebUtils.getTraceId(exchange); + String traceId = WebUtils.getTraceId(exchange); // ApiConfig ac = WebUtils.getApiConfig(exchange); Route route = WebUtils.getRoute(exchange); @@ -120,17 +124,21 @@ public class RouteFilter extends FizzWebFilter { String uri = ThreadContext.getStringBuilder().append(route.nextHttpHostPort) .append(route.getBackendPathQuery()) .toString(); - return fizzWebClient.send(rid, route.method, uri, hdrs, req.getBody()).flatMap(genServerResponse(exchange)); + return fizzWebClient.send(traceId, route.method, uri, hdrs, req.getBody()).flatMap(genServerResponse(exchange)); } else if (route.type == ApiConfig.Type.DUBBO) { return dubboRpc(exchange, route); } else { - String err = "cant handle api config type " + route.type; + String msg = "cant handle api config type " + route.type; StringBuilder b = ThreadContext.getStringBuilder(); WebUtils.request2stringBuilder(exchange, b); - log.error(b.append(Consts.S.LF).append(err).toString(), LogService.BIZ_ID, rid); - return WebUtils.buildJsonDirectResponseAndBindContext(exchange, HttpStatus.OK, null, RespEntity.toJson(HttpStatus.INTERNAL_SERVER_ERROR.value(), err, rid)); + log.error(b.append(Consts.S.LF).append(msg).toString(), LogService.BIZ_ID, traceId); + HttpStatus s = HttpStatus.INTERNAL_SERVER_ERROR; + if (!SystemConfig.FIZZ_ERR_RESP_HTTP_STATUS_ENABLE) { + s = HttpStatus.OK; + } + return WebUtils.buildJsonDirectResponseAndBindContext(exchange, s, null, WebUtils.jsonRespBody(s.value(), msg, traceId)); } } diff --git a/fizz-core/src/main/java/we/legacy/RespEntity.java b/fizz-core/src/main/java/we/legacy/RespEntity.java index 8ee707d..ece10e0 100644 --- a/fizz-core/src/main/java/we/legacy/RespEntity.java +++ b/fizz-core/src/main/java/we/legacy/RespEntity.java @@ -24,8 +24,10 @@ import we.util.ThreadContext; /** * @author hongqiaowei + * @deprecated use WebUtils.jsonRespBody instead. */ +@Deprecated public class RespEntity { private static final String f0 = "{\"msgCode\":"; diff --git a/fizz-core/src/main/java/we/plugin/PluginFilter.java b/fizz-core/src/main/java/we/plugin/PluginFilter.java index f988ad1..50c6397 100644 --- a/fizz-core/src/main/java/we/plugin/PluginFilter.java +++ b/fizz-core/src/main/java/we/plugin/PluginFilter.java @@ -23,6 +23,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; +import we.config.SystemConfig; import we.filter.FilterResult; import we.flume.clients.log4j2appender.LogService; import we.legacy.RespEntity; @@ -58,15 +59,18 @@ public abstract class PluginFilter implements FizzPluginFilter { return doFilter(exchange, config, fixedConfig); } else { if (WebUtils.getDirectResponse(exchange) == null) { // should not reach here - ServerHttpRequest clientReq = exchange.getRequest(); - String rid = clientReq.getId(); + String traceId = WebUtils.getTraceId(exchange); String msg = pfr.id + " fail"; if (pfr.cause == null) { - log.error(msg, LogService.BIZ_ID, rid); + log.error(msg, LogService.BIZ_ID, traceId); } else { - log.error(msg, LogService.BIZ_ID, rid, pfr.cause); + log.error(msg, LogService.BIZ_ID, traceId, pfr.cause); } - return WebUtils.buildJsonDirectResponseAndBindContext(exchange, HttpStatus.OK, null, RespEntity.toJson(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg, rid)); + HttpStatus s = HttpStatus.OK; + if (SystemConfig.FIZZ_ERR_RESP_HTTP_STATUS_ENABLE) { + s = HttpStatus.INTERNAL_SERVER_ERROR; + } + return WebUtils.buildJsonDirectResponseAndBindContext(exchange, s, null, WebUtils.jsonRespBody(s.value(), msg, traceId, null)); } else { return Mono.empty(); } diff --git a/fizz-core/src/main/java/we/util/WebUtils.java b/fizz-core/src/main/java/we/util/WebUtils.java index 3040cd6..83a9595 100644 --- a/fizz-core/src/main/java/we/util/WebUtils.java +++ b/fizz-core/src/main/java/we/util/WebUtils.java @@ -26,6 +26,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.lang.Nullable; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @@ -82,6 +83,8 @@ public abstract class WebUtils { private static final String app = "app"; + private static final String respbT = "respbT"; + public static final String TRACE_ID = "traid@"; public static final String BACKEND_SERVICE = "bs@"; @@ -472,7 +475,7 @@ public abstract class WebUtils { // } // ); // } - String rid = getTraceId(exchange); + String traceId = getTraceId(exchange); // Schedulers.parallel().schedule(() -> { StringBuilder b = ThreadContext.getStringBuilder(); request2stringBuilder(exchange, b); @@ -482,9 +485,9 @@ public abstract class WebUtils { b.append(Consts.S.LINE_SEPARATOR); b.append(filter).append(Consts.S.SPACE).append(code).append(Consts.S.SPACE).append(msg); if (t == null) { - log.error(b.toString(), LogService.BIZ_ID, rid); + log.error(b.toString(), LogService.BIZ_ID, traceId); } else { - log.error(b.toString(), LogService.BIZ_ID, rid, t); + log.error(b.toString(), LogService.BIZ_ID, traceId, t); Throwable[] suppressed = t.getSuppressed(); if (suppressed != null && suppressed.length != 0) { log.error(StringUtils.EMPTY, suppressed[0]); @@ -498,10 +501,14 @@ public abstract class WebUtils { transmitFailFilterResult(exchange, filter, t); } } + HttpStatus s = HttpStatus.OK; + if (SystemConfig.FIZZ_ERR_RESP_HTTP_STATUS_ENABLE) { + s = HttpStatus.resolve(code); + } if (bindContext) { - return buildJsonDirectResponseAndBindContext(exchange, HttpStatus.OK, null, RespEntity.toJson(code, msg, rid)); + return buildJsonDirectResponseAndBindContext(exchange, s, null, jsonRespBody(code, msg, traceId)); } else { - return buildJsonDirectResponse(exchange, HttpStatus.OK, null, RespEntity.toJson(code, msg, rid)); + return buildJsonDirectResponse(exchange, s, null, jsonRespBody(code, msg, traceId)); } } @@ -579,4 +586,37 @@ public abstract class WebUtils { } return id; } + + private static final String s0 = "{\""; + private static final String s1 = "\":"; + private static final String s2 = ",\""; + private static final String s3 = "\":\""; + private static final String s4 = "\""; + private static final String s5 = ",\"traceId\":\""; + private static final String s6 = ",\"context\":\""; + private static final String s7 = "}"; + + public static String jsonRespBody(int code, @Nullable String msg) { + return jsonRespBody(code, msg, null, null); + } + + public static String jsonRespBody(int code, @Nullable String msg, @Nullable String traceId) { + return jsonRespBody(code, msg, traceId, null); + } + + public static String jsonRespBody(int code, @Nullable String msg, @Nullable String traceId, @Nullable Object context) { + StringBuilder b = ThreadContext.getStringBuilder(respbT); + b.append(s0).append(SystemConfig.FIZZ_ERR_RESP_CODE_FIELD).append(s1).append(code); + if (StringUtils.isNotBlank(msg)) { + b.append(s2).append(SystemConfig.FIZZ_ERR_RESP_MSG_FIELD).append(s3).append(msg).append(s4); + } + if (traceId != null) { + b.append(s5).append(traceId).append(s4); + } + if (context != null) { + b.append(s6).append(context).append(s4); + } + b.append(s7); + return b.toString(); + } }