Customize gateway error http response #331
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<Void> 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<Void> 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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -80,14 +80,18 @@ public class RouteFilter extends FizzWebFilter {
|
||||
Mono<Void> 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<Void> 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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\":";
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user