[英]Can a JAX-RS client throw a custom exception type?
我希望服務客戶端的使用者比標准的JAX-RS WebApplicationExceptions獲得更多有意義的異常,因此他們可以根據500異常的原因進行重試或不重試。
我在服務端有一個ExceptionMapper,將實際的異常作為響應的實體放入返回的異常中,如下所示:
public Response toResponse(InternalException ex) {
return Response.status(ex.getStatus)
.entity(ex)
.type(MediaType.APPLICATION_JSON_TYPE)
.build();
}
這似乎可行。 問題是在客戶端中,即使我有一個ClientResponseFilter可以從響應中提取InternalException並拋出該異常,JAX-RS也會將其包裝在ProcessingException中,這無濟於事!
Testcase: badRequest_clientCall_modeledExceptionThrown took 0.272 sec
Caused an ERROR
Unexpected exception, expected<not.actual.package.InternalException> but was<javax.ws.rs.ProcessingException>
java.lang.Exception: Unexpected exception, expected<not.actual.package.InternalException> but was<javax.ws.rs.ProcessingException>
Caused by: javax.ws.rs.ProcessingException
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:263)
at org.glassfish.jersey.client.JerseyInvocation$3.call(JerseyInvocation.java:709)
<Incredibly secret stuff ommitted>
Caused by: not.actual.package.InternalException
我如何真正使這個客戶端拋出正確的異常,而又沒有將最終輸出包裝在try / catch中(我不願意這樣做,因為這會使重試邏輯變得復雜)?
您可以使用spring或JavaEE的AOP( JaveEE APO示例 )。 使用@AroundInvoke
調用您的服務方法。 只需在您的服務方法中拋出異常即可。 我建議您創建自己的異常,例如UsernameInvalidException
, ParameterErrorException
這是JavaEE的代碼示例:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import javax.persistence.EntityManager;
import java.io.IOException;
import java.lang.reflect.Field;
@YourExceptionHandler
@Interceptor
public class YourExceptionInterceptor {
@Inject
private Logger logger;
@AroundInvoke
public Object handleException(InvocationContext ctx) {
//logger.debug("hash:{}", System.identityHashCode(this));
Result returnResult = new Result();
Field resultField = null;
Object result = null;
Class<? extends Object> returnType = ctx.getMethod().getReturnType();
try
{
logger.info("method:{},return type:{}", ctx.getMethod(),
ctx.getMethod().getGenericReturnType());
returnType = ctx.getMethod().getReturnType();
result = ctx.proceed(); // this invoke your service
}
catch ( UsernameInvalidException e )
{
try
{
result = returnType.newInstance();
resultField = result.getClass().getDeclaredField("result");
if ( resultField == null )
{
return null;
}
returnResult.setResultType(ResultType.ERROR);
returnResult.setResultCode(e.getErrorCode()); // code you defined
// error text you set in UsernameInvalidException when thrown
returnResult.setText(e.getMessage());
}
catch ( Exception e1 )
{
e1.printStackTrace();
}
}
catch ( Exception e ) // catch other unexpected exceptions
{
e.printStackTrace();
}
try
{
if ( resultField != null )
{
resultField.setAccessible(true);
resultField.set(result, returnResult);
}
}
catch ( Exception e )
{
e.printStackTrace();
}
return result;
}
}
找出您的例外情況:
public class BaseException extends RuntimeException {
protected static final String SCANPAY_EXCEPTION_CODE = "300";
protected static final String BASE_EXCEPTION_CODE = "400";
protected static final String USERNAME_INVALID_EXCEPTION_CODE = "405";
protected static final String DAO_EXCEPTION_CODE = "401";
protected static final String SERVICE_NOT_AVAILABLE_CODE = "414";
protected static final String PARAMETER_ERROR_CODE = "402";
protected String errorCode;
public BaseException() {
initErrorCode();
}
public BaseException(String message) {
super(message);
initErrorCode();
}
public BaseException(Throwable cause) {
super(cause);
initErrorCode();
}
public BaseException(String message, Throwable cause) {
super(message, cause);
initErrorCode();
}
public BaseException(String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
initErrorCode();
}
protected void initErrorCode() {
errorCode = BASE_EXCEPTION_CODE;
}
public String getErrorCode() {
return errorCode;
}
}
您的用戶名InvalidExcepton:
@SuppressWarnings("serial")
public class UsernameInvalidExcepton extends BaseException {
@Override
@SuppressWarnings("static-access")
protected void initErrorCode() {
this.errorCode = this.USERNAME_INVALID_EXCEPTIO_CODE;
}
public UsernameInvalidException(String message) {
super(message);
}
定義自己的ExceptionHandler,它是一個注釋(有關自定義注釋,請參見此處):
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;
@InterceptorBinding
@Inherited
@Target({ TYPE, METHOD })
@Retention(RUNTIME)
@Documented
public @interface YourExceptionHandler {
}
定義一個異常拋出工廠(此工廠不是必需的,它僅用於拋出新的不同異常。您可以在需要拋出異常時添加新異常。):
public abstract class YourExceptionThrowFactory {
private YourExceptionThrowFactory() {
}
public static void throwUsernameInvalidException(String message) {
throw new UsernameInvalidException(message);
}
如何使用它? 在您的服務方法中,使用注釋:
@YourExceptionHandler
public UserInfo getUserInfo(String username) {
// your service code
if (username == null) {
YourExceptionThrowFactory.throwUsernameInvalidException(
"Username is not valid"); // throw your exception with your message.
}
}
然后對於客戶端,它將獲得您的錯誤代碼和錯誤消息。
上面的所有代碼都是針對JavaEE的,如果您使用spring,則會發現相應的注釋和技術。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.