有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

JAXRS客户机可以抛出自定义异常类型吗?

我希望我的服务客户机的消费者能够获得比标准JAX-RS WebApplicationException更有意义的异常,这样他们就可以根据异常的原因进行重试或不重试等操作

我在服务端有一个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(我不喜欢这样做,因为这会使重试逻辑复杂化)之外,我实际上如何让这个客户机抛出正确的异常


共 (1) 个答案

  1. # 1 楼答案

    您可以使用spring或JavaEE的AOP(JaveEE APO example)。使用@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;
        }
    
    }
    

    您的用户名InvalideExcepton:

    @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,它是一个注释(请参阅此处的custom annotation):

    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,您将找到相应的注释和技术