Customize gateway error http response #331

This commit is contained in:
hongqiaowei
2021-10-09 10:19:11 +08:00
committed by GitHub
parent 1d71a059bf
commit 3bdf053701
8 changed files with 135 additions and 38 deletions

View File

@@ -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

View File

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

View File

@@ -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 {

View File

@@ -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);

View File

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

View File

@@ -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\":";

View File

@@ -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();
}

View File

@@ -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();
}
}