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

张占岭發表於2024-06-07

異常攔截器ExceptionMapper

在JAX-RS(Java API for RESTful Web Services)中,ExceptionMapper介面用於將Java異常對映到HTTP響應。透過實現ExceptionMapper介面,你可以自定義如何處理特定型別的異常,並生成相應的HTTP響應。

優先順序和選擇

當有多個ExceptionMapper可用於處理同一型別的異常時,JAX-RS會選擇最具體的那個。例如,如果你有一個處理RuntimeException的ExceptionMapper和一個處理NullPointerException的ExceptionMapper,那麼當丟擲NullPointerException時,會選擇處理NullPointerException的ExceptionMapper。

定義自定義異常

 public class UniqueException extends RuntimeException {

	public UniqueException(Throwable cause) {
		super(cause);
	}

	public UniqueException(String message) {
		super(message);
	}

	public UniqueException(String message, Throwable cause) {
		super(message, cause);
	}

}

實現ExceptionMapper

/**
 * 資料表約束異常處理器.
 *
 * @author lind
 * @date 2024/6/4 10:45
 * @since 1.0.0
 */
@Provider
public class DbViolationExceptionMapper implements ExceptionMapper<UniqueException> {

	@Override
	public Response toResponse(UniqueException exception) {

		return Response.status(Response.Status.BAD_REQUEST)
				.entity(MapUtil.builder().put("error", exception.getMessage()).build()).type(MediaType.APPLICATION_JSON)
				.encoding("utf-8").build();// 非200的請求,這個type無效,一直是text/plain

		/*
		 * return Response.status(Response.Status.OK)
		 * .entity(MapUtil.builder().put("error", exception.getMessage()).build())
		 * .type("application/json; charset=UTF-8").build();//
		 * 200的請求,是可以使用application/json的
		 *
		 */
	}

}

註冊ExceptionMapper

在檔案resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports新增內容

com.xx.exception.handler.DbViolationExceptionMapper

業務程式碼直接丟擲異常

if (getCurrentUserId() == null || !getCurrentUserId().equals(userId)) {
	throw new UniqueException("許可權不足");
 }

相關問題

  • 當在ExceptionMapper中返回的Response物件,狀態碼為200時,可以響應為application/json
  • 當Response物件狀態碼非200時,響應一直是text/plain

經過除錯與排查,發現當非200時,在這個過濾器org.apache.dubbo.rpc.protocol.rest.filter.ServiceInvokeRestFilter裡,它出現了兩個content-type,text/plain不知道是什麼時間被加進去的,如圖

方法執行到這裡時,為響應頭新增了text/plain,事實上,在操作org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse物件的setStatus()方法時,它完成了預設content-type的強編碼

NettyHttpResponse的setStatus()方法如下,完成了強編碼

public void setStatus(int status) {
  if (status > 200) {
        this.addOutputHeaders(RestHeaderEnum.CONTENT_TYPE.getHeader(), MediaType.TEXT_PLAIN.value);
    }

    this.status = status;
}

這也是業務程式碼中,直接報出自定義異常,在ExceptionMapper捕獲返回json沒有生效的原因,這塊感覺dubbo設計的不太好。

相關文章