Вопрос

Я изучаю JAX-RS (он же JSR-311), используя 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
    }
}

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

Например:

GET /hello?name=Mark&birthDate=X

404 Not Found

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

Это было полезно?

Решение

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

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

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

Другие советы

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

Джерси выдает исключение 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();
    }
}

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

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

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

Мне тоже нравится Стаксман вероятно, я бы реализовал этот параметр запроса в виде строки, затем обработал преобразование, переназначая по мере необходимости.

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

throw new WebApplicationException(Response.Status.BAD_REQUEST);

Смотрите JavaDoc для javax.ws.rs.core.Ответ.Статус для получения дополнительных опций.

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

" Тип T аннотируемого параметра, поля или свойства должен либо:

1) Быть примитивным типом
2) Иметь конструктор, который принимает один Строковый аргумент
3) Иметь статический метод с именем valueOf или fromString который принимает один строковый аргумент (см., например, Integer.valueOf(строка))
4) Иметь зарегистрированную реализацию javax.ws.rs.ext.ParamConverterProvider JAX-RS extension SPI, которая возвращает экземпляр javax.ws.rs.ext.ParamConverter, способный к преобразованию "из string" для типа.
5) Быть списком, набором или SortedSet, где T удовлетворяет 2, 3 или 4 выше.Результирующий коллекция доступна только для чтения."

Если вы хотите контролировать, какой ответ отправляется пользователю, когда параметр запроса в строковой форме не может быть преобразован в ваш тип 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, просто используйте Dropwizard Параметр DateTimeParam ( дата - время ).

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

Хорошая статья от Кода Хейла по этому поводу находится по адресу 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();
    }
}

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

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

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

Просто в качестве дополнения к ответу @Steven Lavine на случай, если вы захотите открыть окно входа в браузер.Мне было трудно правильно вернуть ответ (Аутентификация MDN HTTP) из Фильтра на случай, если пользователь еще не прошел аутентификацию

Это помогло мне создать Ответ на принудительный вход в систему через браузер, обратите внимание на дополнительную модификацию заголовков.Это установит код состояния на 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"); }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top