代码之家  ›  专栏  ›  技术社区  ›  itsadok

JAX-RS/Jersey如何定制错误处理?

  •  206
  • itsadok  · 技术社区  · 17 年前

    我正在使用Jersey学习JAX-RS(又名JSR-311)。我已经成功地创建了根资源,正在使用参数:

    @Path("/hello")
    public class HelloWorldResource {
    
        @GET
        @Produces("text/html")
        public String get(
            @QueryParam("name") String name,
            @QueryParam("birthDate") Date birthDate) {
    
             // Return a greeting with the name and age
        }
    }
    

    这非常有效,可以处理日期(字符串)构造函数(如YYYY/mm/dd和mm/dd/YYYY)可以理解的当前区域设置中的任何格式。但是如果我提供了一个无效或不可理解的值,我会得到404响应。

    GET /hello?name=Mark&birthDate=X
    
    404 Not Found
    

    如何自定义此行为?可能是不同的响应代码(可能是“400错误请求”)?记录一个错误怎么样?是否可以在自定义标题中添加问题描述(“错误日期格式”),以帮助进行故障排除?或者返回一个完整的错误响应和详细信息,以及5xx状态代码?

    8 回复  |  直到 13 年前
        1
  •  275
  •   Smile Luc De pauw    6 年前

    有几种方法可以定制JAX-RS的错误处理行为。

    第一种方法是创建一个扩展WebApplicationException的异常类。

    例子:

    public class NotAuthorizedException extends WebApplicationException {
         public NotAuthorizedException(String message) {
             super(Response.status(Response.Status.UNAUTHORIZED)
                 .entity(message).type(MediaType.TEXT_PLAIN).build());
         }
    }
    

    要抛出这个新创建的异常,只需执行以下操作:

    @Path("accounts/{accountId}/")
        public Item getItem(@PathParam("accountId") String accountId) {
           // An unauthorized user tries to enter
           throw new NotAuthorizedException("You Don't Have Permission");
    }
    

    注意,您不需要在throws子句中声明异常,因为WebApplicationException是一个运行时异常。这将向客户端返回401响应。

    第二种也是更简单的方法是简单地构造 WebApplicationException

    @Path("accounts/{accountId}/")
    public Item getItem(@PathParam("accountId") String accountId) {
       // An unauthorized user tries to enter
       throw new WebApplicationException(Response.Status.UNAUTHORIZED);
    }
    

    此代码也会将401返回给客户端。

    另一种方法是包装现有的异常,可能是 ObjectNotFoundException 使用一个实现 ExceptionMapper 带注释的接口 @Provider 注释。这告诉JAX-RS运行时,如果引发包装异常,则返回 例外情况 .

        2
  •  70
  •   ymajoros    13 年前
    @Provider
    public class BadURIExceptionMapper implements ExceptionMapper<NotFoundException> {
    
    public Response toResponse(NotFoundException exception){
    
        return Response.status(Response.Status.NOT_FOUND).
        entity(new ErrorResponse(exception.getClass().toString(),
                    exception.getMessage()) ).
        build();
    }
    }
    

    创建上面的类。这将处理404(NotFoundException),在toResponse方法中,您可以给出自定义响应。类似地,您需要映射参数异常等以提供定制响应。

        3
  •  38
  •   MonoThreaded    10 年前

    Jersey在未能解组参数时抛出com.sun.Jersey.api.ParameterException,因此一种解决方案是创建一个ExceptionMapper来处理这些类型的异常:

    @Provider
    public class ParamExceptionMapper implements ExceptionMapper<ParamException> {
        @Override
        public Response toResponse(ParamException exception) {
            return Response.status(Status.BAD_REQUEST).entity(exception.getParameterName() + " incorrect type").build();
        }
    }
    
        4
  •  27
  •   Charlie Brooking    15 年前

    public class DateParam {
      private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    
      private Calendar date;
    
      public DateParam(String in) throws WebApplicationException {
        try {
          date = Calendar.getInstance();
          date.setTime(format.parse(in));
        }
        catch (ParseException exception) {
          throw new WebApplicationException(400);
        }
      }
      public Calendar getDate() {
        return date;
      }
      public String format() {
        return format.format(value.getTime());
      }
    }
    

    然后像这样使用它:

    private @QueryParam("from") DateParam startDateParam;
    private @QueryParam("to") DateParam endDateParam;
    // ...
    startDateParam.getDate();
    

    尽管在这种情况下错误处理很简单(抛出400响应),但使用此类允许您考虑一般的参数处理,其中可能包括日志记录等。

        5
  •  11
  •   StaxMan    17 年前

    对于解析,SimpleDataFormat应该可以正常工作。

        6
  •  7
  •   Community Mohan Dere    9 年前

    我也喜欢 StaxMan

    如果特定于区域设置的行为是所需和预期的行为,则可以使用以下命令返回400错误请求错误:

    throw new WebApplicationException(Response.Status.BAD_REQUEST);

    有关详细信息,请参阅JavaDoc javax.ws.rs.core.Response.Status

        7
  •  4
  •   Srikanth    11 年前

    @QueryParam文档说

    带注释的参数、字段或属性的类型T必须 要么:

    1) 是原始类型
    字符串参数
    3) 具有名为valueOf或fromString的静态方法
    4) 注册实施 返回一个javax.ws.rs.ext.ParamConverter实例,该实例能够 类型的“字符串”转换。
    5) 是列表、集合或

    当字符串形式的查询参数无法转换为t类型时,若要控制用户的响应,可以抛出WebApplicationException。Dropwizard附带以下*参数类,您可以根据需要使用这些类。

    BooleanParam、DateTimeParam、IntParam、LongParam、LocalDateParam、NoneEmptyStringParam、UUIDParam。看见 https://github.com/dropwizard/dropwizard/tree/master/dropwizard-jersey/src/main/java/io/dropwizard/jersey/params

    如果您需要Joda DateTime,只需使用Dropwizard即可 DateTimeParam .

    http://codahale.com/what-makes-jersey-interesting-parameter-classes/

    import io.dropwizard.jersey.params.AbstractParam;
    
    import java.util.Date;
    
    import javax.ws.rs.core.Response;
    import javax.ws.rs.core.Response.Status;
    
    public class DateParam extends AbstractParam<Date> {
    
        public DateParam(String input) {
            super(input);
        }
    
        @Override
        protected Date parse(String input) throws Exception {
            return new Date(input);
        }
    
        @Override
        protected Response error(String input, Exception e) {
            // customize response body if you like here by specifying entity
            return Response.status(Status.BAD_REQUEST).build();
        }
    }
    

        8
  •  1
  •   ACV    11 年前

    您可以做的是重写并将出生日期作为字符串,然后尝试解析,如果您没有得到您想要的,您可以通过任何异常映射机制(有几种)随意抛出您想要的任何异常。

        9
  •  1
  •   suraj.tripathi    7 年前

    我也面临同样的问题。

    我想在一个中心位置捕获所有错误并进行转换。

    下面是我如何处理它的代码。

    ExceptionMapper 加 @Provider

    推翻 toResponse 方法并返回用自定义数据填充的响应对象。

    //ExceptionMapperProvider.java
    /**
     * exception thrown by restful endpoints will be caught and transformed here
     * so that client gets a proper error message
     */
    @Provider
    public class ExceptionMapperProvider implements ExceptionMapper<Throwable> {
        private final ErrorTransformer errorTransformer = new ErrorTransformer();
    
        public ExceptionMapperProvider() {
    
        }
    
        @Override
        public Response toResponse(Throwable throwable) {
            //transforming the error using the custom logic of ErrorTransformer 
            final ServiceError errorResponse = errorTransformer.getErrorResponse(throwable);
            final ResponseBuilder responseBuilder = Response.status(errorResponse.getStatus());
    
            if (errorResponse.getBody().isPresent()) {
                responseBuilder.type(MediaType.APPLICATION_JSON_TYPE);
                responseBuilder.entity(errorResponse.getBody().get());
            }
    
            for (Map.Entry<String, String> header : errorResponse.getHeaders().entrySet()) {
                responseBuilder.header(header.getKey(), header.getValue());
            }
    
            return responseBuilder.build();
        }
    }
    
    // ErrorTransformer.java
    /**
     * Error transformation logic
     */
    public class ErrorTransformer {
        public ServiceError getErrorResponse(Throwable throwable) {
            ServiceError serviceError = new ServiceError();
            //add you logic here
            serviceError.setStatus(getStatus(throwable));
            serviceError.setBody(getBody(throwable));
            serviceError.setHeaders(getHeaders(throwable));
    
        }
        private String getStatus(Throwable throwable) {
            //your logic
        }
        private Optional<String> getBody(Throwable throwable) {
            //your logic
        }
        private Map<String, String> getHeaders(Throwable throwable) {
            //your logic
        }
    }
    
    //ServiceError.java
    /**
     * error data holder
     */
    public class ServiceError {
        private int status;
        private Map<String, String> headers;
        private Optional<String> body;
        //setters and getters
    }
    
        10
  •  1
  •   Hari Krishna    6 年前

    方法1:通过扩展WebApplicationException类

    通过扩展WebApplicationException创建新异常

    public class RestException extends WebApplicationException {
    
             private static final long serialVersionUID = 1L;
    
             public RestException(String message, Status status) {
             super(Response.status(status).entity(message).type(MediaType.TEXT_PLAIN).build());
             }
    }
    

    现在,在需要时抛出“RestException”。

    public static Employee getEmployee(int id) {
    
             Employee emp = employees.get(id);
    
             if (emp == null) {
                     throw new RestException("Employee with id " + id + " not exist", Status.NOT_FOUND);
             }
             return emp;
    }
    

    link .

    方法2:实现ExceptionMapper

    以下映射程序处理“DataNotFoundException”类型的异常

    @Provider
    public class DataNotFoundExceptionMapper implements
            ExceptionMapper<DataNotFoundException> {
    
        @Override
        public Response toResponse(DataNotFoundException ex) {
            ErrorMessage model = new ErrorMessage(ex.getErrorCode(),
                    ex.getMessage());
            return Response.status(Status.NOT_FOUND).entity(model).build();
        }
    
    }
    

    您可以在此处看到完整的应用程序 link .

        11
  •  0
  •   Omnibyte    7 年前

    作为@Steven Lavine answer的扩展,以防您想打开浏览器登录窗口。我发现很难正确地回复( MDN HTTP Authentication )如果用户尚未通过身份验证,则从筛选器中删除

    这帮助我构建了对强制浏览器登录的响应,注意对标题的额外修改。这会将状态代码设置为401,并设置导致浏览器打开用户名/密码对话框的标题。

    // The extended Exception class
    public class NotLoggedInException extends WebApplicationException {
      public NotLoggedInException(String message) {
        super(Response.status(Response.Status.UNAUTHORIZED)
          .entity(message)
          .type(MediaType.TEXT_PLAIN)
          .header("WWW-Authenticate", "Basic realm=SecuredApp").build()); 
      }
    }
    
    // Usage in the Filter
    if(headers.get("Authorization") == null) { throw new NotLoggedInException("Not logged in"); }
    
    推荐文章