2009-02-24 4 views
199

Я изучаю JAX-RS (ака, JSR-311), используя Джерси. Я успешно создал корневой ресурс, и я играл с параметрами:JAX-RS/Jersey, как настроить обработку ошибок?

@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 
    } 
} 

Это прекрасно работает, и обрабатывает любой формат в текущей локали, который понимается по дате (String) Конструктор (например, YYYY/мм/дд и мм/дд/ГГГГ). Но если я поставлю значение, которое недействительно или не понято, я получаю ответ 404.

Например:

GET /hello?name=Mark&birthDate=X 

404 Not Found 

Как я могу настроить это поведение? Может быть, другой код ответа (возможно, «400 Bad Request»)? Как насчет регистрации ошибки? Возможно, добавьте описание проблемы («формат плохой даты») в пользовательский заголовок, чтобы помочь устранить неполадки? Или вернуть весь ответ об ошибке с подробной информацией вместе с кодом состояния 5xx?

ответ

253

Существует несколько подходов к настройке поведения обработки ошибок с помощью 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"); 
} 

Обратите внимание, что вам не нужно объявить исключение в кидает положение, потому что 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 клиенту.

Конечно, это простой пример. Вы можете сделать Exception намного более сложным, если это необходимо, и вы сможете генерировать код ответа HTTP, который вам нужен.

Другой подход заключается в том, чтобы обернуть существующее Exception, возможно ObjectNotFoundException, с небольшим классом-оболочкой, который реализует интерфейс ExceptionMapper, аннотированный аннотацией @Provider. Это говорит о времени выполнения JAX-RS, что если обернутое исключение возникает, верните код ответа, определенный в ExceptionMapper.

+3

В вашем примере, вызов супер() должно быть немного иначе:.. супер (Response.status (Status.UNAUTHORIZED) объект (сообщение) .type ("текст/обычный") построить()); Спасибо за понимание. –

+0

Я думаю, что этот метод не будет выполнен в его примере. – deamon

+61

В сценарии, упомянутом в вопросе, вы не получите шанс выбросить исключение, так как Джерси добавит исключение, так как он не сможет создать экземпляр объекта Date из входного значения. Есть ли способ перехватить исключение Джерси? Существует один интерфейс ExceptionMapper, однако он также перехватывает исключения, вызванные этим методом (get в этом случае). –

11

Одно очевидное решение: взять строку, конвертировать в Date самостоятельно. Таким образом, вы можете определить желаемый формат, перехватить исключения и либо повторно бросить, либо настроить отправляемую ошибку. Для синтаксического анализа SimpleDateFormat должен работать нормально.

Я уверен, что есть способы манипулировать обработчиками и для типов данных, но, возможно, немного простого кода - это все, что вам нужно в этом случае.

5

Я тоже, как StaxMan, вероятно, реализует этот QueryParam как String, а затем обрабатывает преобразование, перестраивая при необходимости.

Если локаль определенное поведение является желаемым и ожидаемым поведением, можно использовать следующее, чтобы вернуть ошибку 400 BAD REQUEST:

throw new WebApplicationException(Response.Status.BAD_REQUEST);

Смотрите JavaDoc для javax.ws.rs.core.Response.Status больше вариантов.

26

Вы также можете написать многоразовый класс для QueryParam-аннотированного переменных

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 ответа), используя этот класс, вы можете в любом случае отказаться от обработки параметров, которые могут включать регистрацию и т. д.

+0

Я пытаюсь добавить обработчик параметров пользовательского запроса в Джерси (переход с CXF), это выглядит очень похоже на то, что я делаю, но я не знаю, как установить/создать нового поставщика. Ваш класс выше не показывает мне это. Я использую объекты JodaTime DateTime для QueryParam и не имею провайдера для их декодирования. Это так же просто, как подклассификация, создание конструктора String и управление этим? –

+1

Просто создайте класс, похожий на 'DateParam', над которым стоит' org.joda.time.DateTime' вместо 'java.util.Calendar'. Вы используете это с помощью '@ QueryParam', а не' DateTime'. –

+1

Если вы используете Joda DateTime, джерси поставляется с DateTimeParam, чтобы вы могли использовать его напрямую. Не нужно писать самостоятельно. См. Https://github.com/dropwizard/dropwizard/blob/master/dropwizard-jersey/src/main/java/io/dropwizard/jersey/params/DateTimeParam.java – Srikanth

63
@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, вы можете дать свой настраиваемый ответ. Точно так же есть ParamException и т. Д., Которые вам нужно будет отображать для предоставления настраиваемых ответов.

+0

Вы можете использовать утилиты ExceptionMapper , а также для общих исключений – userRaj

+0

Это будет обрабатывать WebApplicationException, созданный клиентом JAX-RS, спрятав происхождение ошибки. Лучше иметь пользовательское исключение (не полученное из WebApplicationException) или бросить WebApplications с полным ответом. WebApplicationExceptions, создаваемые JAX-RS Client, должны обрабатываться непосредственно при вызове, иначе ответ другой службы передается как ответ вашей службы, хотя это необработанная ошибка внутреннего сервера. –

33

Джерси бросает com.sun.jersey.api.ParamException, когда не маршализацию параметры так, одно решение заключается в создании 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(); 
    } 
} 
+0

Где я должен создать этот картограф специально для Джерси, чтобы зарегистрировать его? – Patricio

+1

Все, что вам нужно сделать, это добавить аннотацию @Provider, см. Здесь для получения дополнительной информации: http://stackoverflow.com/questions/15185299/jax-rs-jersey-exceptionmappers-user-defined-exception –

4

@QueryParam документация говорит

" The type T of the annotated parameter, field or property must either:

1) Be a primitive type
2) Have a constructor that accepts a single String argument
3) Have a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String))
4) Have a registered implementation of javax.ws.rs.ext.ParamConverterProvider JAX-RS extension SPI that returns a javax.ws.rs.ext.ParamConverter instance capable of a "from string" conversion for the type.
5) Be List, Set or SortedSet, where T satisfies 2, 3 or 4 above. The resulting collection is read-only. "

Если вы хотите контролировать, какой ответ будет отправлен пользователю, когда параметр запроса в форме String не может быть преобразован в ваш тип T, вы можете выбросить WebApplicationException. Dropwizard поставляется со следующими параметрами, которые вы можете использовать для своих нужд.

BooleanParam, DateTimeParam, IntParam, LongParam, LocalDateParam, NonEmptyStringParam, UUIDParam. См. https://github.com/dropwizard/dropwizard/tree/master/dropwizard-jersey/src/main/java/io/dropwizard/jersey/params

Если вам нужна дата Joda DateTime, просто используйте Dropwizard DateTimeParam.

Если приведенный выше список не подходит для ваших нужд, определите свой собственный, расширив AbstractParam. Переопределить метод анализа. Если вам нужен контроль над телом ошибки, переопределите метод ошибки.

Хорошая статья от Coda Hale на это в 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(); 
    } 
} 

Дата (String аргумент) конструктор является устаревшим. Я бы использовал Java-классы даты 8, если вы на Java 8. В противном случае рекомендуется дата даты joda.

1

Это правильное поведение на самом деле. Джерси попытается найти обработчик для вашего ввода и попытается построить объект из предоставленного ввода. В этом случае он попытается создать новый объект Date со значением X, предоставленным конструктору. Поскольку это неверная дата, по соглашению Джерси вернется 404.

Что вы можете сделать, это переписать и поставить дату рождения в виде строки, затем попытаться разобрать, и если вы не получите то, что хотите, вы бесплатно выкинуть любое исключение, которое вы хотите каким-либо из механизмов отображения исключений (их несколько).

-5
abtrack class Responce 
{ 
private String message ; 
private int code ; 

public String getMessage(){ 
return this.message ; 
} 
public void setMessage(String message){ 
this.message =message ; 
} 


public String getCode(){ 
return this.code ; 
} 
public void setCode(String code){ 
this.code =code ; 
} 

} 

@XmlRootElement(name='MyResponce') 
class MyResponce extends Responce { 

} 

@Path("/hello") 
public class HelloWorldResource { 

    @GET 
    @Produces("text/html") 
    public MyResponce get(
     MyResponce myResponce = new MyResponce(); 
     @QueryParam("name") String name, 
     @QueryParam("birthDate") Date birthDate) throw WSException { 
      try { 
}catch(Exception) 
myResponce.setCode(400); 
myResponce.setMessage("Exception") 
    } 
return myResponce ; 

} 
Смежные вопросы