簡體   English   中英

JAX-RS客戶端可以拋出自定義異常類型嗎?

[英]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調用您的服務方法。 只需在您的服務方法中拋出異常即可。 我建議您創建自己的異常,例如UsernameInvalidExceptionParameterErrorException這是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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM