dubbo~全域性異常攔截器的使用與設計缺陷~續

张占岭發表於2024-06-11

上一次的介紹,主要圍繞如何統一去捕獲異常,以及為每一種異常新增自己的Mapper實現,並且我們知道,當在ExceptionMapper中返回非200的Response,不支援application/json的響應型別,而是寫死的text/plain型別。

Filter為二方包異常手動捕獲

參考:https://blog.csdn.net/2401_84048290/article/details/138105184

我們來看看dubbo的原始碼進行分析,如果Dubbo的provider端 丟擲異常(Throwable),則會被 provider端 的ExceptionFilter攔截到,執行以下invoke方法,裡面有個實現Listener類,重寫了onResponse,我們可以自定義filter來覆蓋原來的ExceptionFilter,把自定義的異常透過RuntimeException進行包裹,然後在Mapper中進行統一的捕獲。

  • 新增CustomExceptionFilter型別,實現Filter和BaseFilter.Listener,重寫onResponse方法,新增自定義程式碼,如下:
public class CustomExceptionFilter implements Filter, BaseFilter.Listener {
    public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
        	exception = appResponse.getException();
				String className = exception.getClass().getName();

				// 本專案的異常也直接丟擲
				if (className.startsWith("com.myself.")) {
					appResponse.setException(new RuntimeException(exception));
					return;
				}
				// 其它原來ExceptionFilter中的程式碼
    }
}
  • META-INF中註冊這個過濾器resources/META-INF/dubbo/org.apache.dubbo.rpc.Filter
customExceptionFilter=com.xxx.register.exception.filter.CustomExceptionFilter
  • 配置中文中註冊,並移除預設的resources/application.properties
# 自定義過濾器,上面-exception就是dubbo預設的處理異常的filter,前面-號就代表去除,注意:不需要加雙引號
dubbo.provider.filter=customExceptionFilter,-exception

一個Mapper處理所有自定義異常

  • 配置檔案中指定mapper,resources/application.properties
dubbo.protocols.http.extension=com.xxx.register.exception.mapper.CustomExceptionMapper
  • mapper原始碼如下
@Provider
public class DbViolationExceptionMapper implements ExceptionMapper<RuntimeException> {

	@Override
	public Response toResponse(RuntimeException exception) {
		Map<String, String> map = MapUtil.<String, String>builder().put("error", exception.getMessage()).build();
		if (exception.getCause() instanceof ForbiddenException) {
			return Response.status(Response.Status.FORBIDDEN).entity(map).type(MediaType.APPLICATION_JSON).build();
		}
		if (exception.getCause() instanceof CustomException) {
			return Response.status(Response.Status.BAD_REQUEST).entity(map).type(MediaType.APPLICATION_JSON).build();
		}
		if (exception.getCause() instanceof IdentityBrokerException) {
			return Response.status(Response.Status.UNAUTHORIZED).entity(map).type(MediaType.APPLICATION_JSON).build();
		}
		if (exception.getCause() instanceof UniqueException) {
			return Response.status(Response.Status.SERVICE_UNAVAILABLE).entity(map).type(MediaType.APPLICATION_JSON)
					.build();
		}
		return Response.status(Response.Status.SERVICE_UNAVAILABLE)
				.entity(MapUtil.builder().put("error", exception.getMessage()).build()).type(MediaType.APPLICATION_JSON)
				.encoding("utf-8").build();// 非200的請求,這個type無效,一直是text/plain

	}

}

未解決的問題

  • 目前非200的請求,toResponse時,響應型別還是text/plain

相關文章