スターに設定して、毎日少し進歩してください!
序文
モノマーSpringBootプロジェクトでは、我々はグローバルな例外をキャプチャする必要がある唯一のプロジェクトで設定する必要がある@RestControllerAdvice
と@ExceptionHandler
フロントの包装の再統一後に呼び出し元に戻り、異なる種類の統一された治療のために異常であることができます。
@Slf4j
@RestControllerAdvice
public class RestExceptionHandler {
/**
* 默认全局异常处理。
* @return ResultData
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResultData<String> exception(Exception e) {
log.error("全局异常信息 ex={}", e.getMessage(), e);
return ResultData.fail(ReturnCode.RC500.getCode(),e.getMessage());
}
}
ただし、この時点でアプリケーション層のシステム障害(ゲートウェイ層のjwtトークン解析例外、サービスのオフラインなど)を呼び出すビジネスゲートウェイなどのマイクロサービスアーキテクチャでは、この時点では@RestControllerAdvice
有効になりません。アプリケーション層に流れません。
以下では、それぞれ2つのシナリオをシミュレートし、すべての人に体験してもらいます。
-
jwt解析の例外
誤ったトークンを故意に書き込んで解析できないようにします。バックエンドから返されるデータは次のとおりです。
{
"timestamp": "2020-12-22T02:32:03.143+0000",
"path": "/account-service/account/test/jianzh5",
"status": 500,
"error": "Internal Server Error",
"message": "Cannot convert access token to JSON",
"requestId": "7043b1f8-1"
}
-
オフラインサービス
バックエンドサービスを停止します。バックエンドから返されるデータは次のとおりです。
{
"timestamp": "2020-12-22T02:36:13.281+0000",
"path": "/account-service/account/getByCode/jianzh5",
"status": 503,
"error": "Service Unavailable",
"message": "Unable to find instance for account-service",
"requestId": "7043b1f8-6"
}
フロントエンドとバックエンドが分離されているプロジェクトでは、通常、プロジェクトの全体的なリターン形式について合意する必要があり、フロントエンドは返されたデータに基づいてページロジックを決定する必要があります。プロジェクトの例では、合意された応答形式は次のとおりです。
@Data
@ApiModel(value = "统一返回结果封装",description = "接口返回统一结果")
public class ResultData<T> {
/** 结果状态 ,具体状态码参见ResultData.java*/
@ApiModelProperty(value = "状态码")
private int status;
@ApiModelProperty(value = "响应信息")
private String message;
@ApiModelProperty(value = "后端返回结果")
private T data;
@ApiModelProperty(value = "后端响应状态")
private boolean success;
@ApiModelProperty(value = "响应时间戳")
private long timestamp ;
public ResultData (){
this.timestamp = System.currentTimeMillis();
}
...
}
明らかに、これらの場合に返される異常なデータは、予想される形式を満たしていないため、ゲートウェイから返されるデータを変更する必要があります。
理由分析
DefaultErrorWebExceptionHandler
例外を処理するためのデフォルトのSpringCloudゲートウェイ。このクラスは、ErrorWebFluxAutoConfiguration
を与えるように構成できます。
ではDefaultErrorWebExceptionHandler
、次のように処理ロジックをデフォルトクラスの例外であります:
public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
...
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(this.acceptsTextHtml(), this::renderErrorView).andRoute(RequestPredicates.all(), this::renderErrorResponse);
}
...
}
リクエストヘッダーに従って返されるリソース形式を確認します。
返されるコンテンツデータはDefaultErrorAttributes
、クラスから作成されます。
public class DefaultErrorAttributes implements ErrorAttributes {
...
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap();
errorAttributes.put("timestamp", new Date());
errorAttributes.put("path", request.path());
Throwable error = this.getError(request);
MergedAnnotation<ResponseStatus> responseStatusAnnotation = MergedAnnotations.from(error.getClass(), SearchStrategy.TYPE_HIERARCHY).get(ResponseStatus.class);
HttpStatus errorStatus = this.determineHttpStatus(error, responseStatusAnnotation);
errorAttributes.put("status", errorStatus.value());
errorAttributes.put("error", errorStatus.getReasonPhrase());
errorAttributes.put("message", this.determineMessage(error, responseStatusAnnotation));
errorAttributes.put("requestId", request.exchange().getRequest().getId());
this.handleException(errorAttributes, this.determineException(error), includeStackTrace);
return errorAttributes;
}
...
}
これを読むと、上記のデータ形式が返される理由がわかります。次に、返される形式を書き直す必要があります。
解決
ここでは、CustomErrorWebExceptionHandler
クラスをカスタマイズしてを継承DefaultErrorWebExceptionHandler
し、フロントエンドに応じて生成された論理データを変更できます。次に、クラス構成を定義します。参照を書き込むことができます。ErrorWebFluxAutoConfiguration
例外クラスをCustomErrorWebExceptionHandler
クラスに置き換えるだけです。
この方法は自分で勉強してください。基本的にはコードをコピーするだけで、書き直しは複雑ではありません。この方法については説明しません。別の書き方を次に示します。
カスタム応答クラスメソッドを返す代わりに、GlobalErrorWebExceptionHandler
トップレベルのインターフェイスErrorWebExceptionHandler
書き換えhandler()
メソッドを直接実装できるグローバル例外クラスを定義しますhandler()
。ただし、実装クラスのオーバーライド優先度はResponseStatusExceptionHandler
、エラー処理に対応する取得応答クラスコードを介して組み込みよりも低くする必要があることに注意してください。
コードは次のように表示されます。
/**
* 网关全局异常处理
* @author javadaily
*/
@Slf4j
@Order(-1)
@Configuration
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class GlobalErrorWebExceptionHandler implements ErrorWebExceptionHandler {
private final ObjectMapper objectMapper;
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
if (response.isCommitted()) {
return Mono.error(ex);
}
// 设置返回JSON
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if (ex instanceof ResponseStatusException) {
response.setStatusCode(((ResponseStatusException) ex).getStatus());
}
return response.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
try {
//返回响应结果
return bufferFactory.wrap(objectMapper.writeValueAsBytes(ResultData.fail(500,ex.getMessage())));
}
catch (JsonProcessingException e) {
log.error("Error writing response", ex);
return bufferFactory.wrap(new byte[0]);
}
}));
}
}
試験結果
期待した結果に沿って、ゲートウェイ層の異常な傍受が実現しました!
上記、お役に立てば幸いです。
これは皆へのささやかな贈り物です。公式アカウントに従って、次のコードを入力してください。Baiduネットワークディスクアドレスを取得できます。ルーチンはありません。
001:「プログラマーにとって必読の本」
002:「中小規模のインターネット企業向けのバックエンドサービスアーキテクチャと運用および保守アーキテクチャをゼロから構築する」
003:「インターネット企業向けの高同時実行ソリューション」
004:「インターネットアーキテクチャ教育ビデオ "
006:
注文システムのSpringBoot実現" 007: "SpringSecurity実際の戦闘ビデオ"
008: "Hadoop実際の戦闘教育ビデオ"
009: "Tencent 2019 Techo Developer Conference PPT"
010:WeChat交換グループ