Существует несколько вариантов использования для установки кодов состояния HTTP в веб-службе REST, и, по крайней мере, один из них недостаточно документирован в существующих ответах (т. Е. Когда вы используете автоматическую магическую сериализацию JSON / XML с использованием JAXB, и вы хотите вернуть сериализуемый объект, но также код состояния, отличный от значения по умолчанию 200).
Итак, позвольте мне попытаться перечислить различные варианты использования и решения для каждого из них:
1. Код ошибки (500, 404, ...)
Наиболее распространенный вариант использования, когда вы хотите вернуть код состояния, отличный от случая, 200 OK
когда произошла ошибка.
Например:
- сущность запрашивается, но она не существует (404)
- запрос семантически неверен (400)
- пользователь не авторизован (401)
- есть проблема с подключением к базе данных (500)
- и т.д..
а) бросить исключение
В этом случае, я думаю, что самый чистый способ решения проблемы - это исключение. Это исключение будет обработано ExceptionMapper
, которое преобразует исключение в ответ с соответствующим кодом ошибки.
Вы можете использовать значение по умолчанию, ExceptionMapper
которое предварительно сконфигурировано на Джерси (и я полагаю, что то же самое с другими реализациями) и выбросить любой из существующих подклассов javax.ws.rs.WebApplicationException
. Это предопределенные типы исключений, которые предварительно сопоставлены с различными кодами ошибок, например:
- BadRequestException (400)
- InternalServerErrorException (500)
- NotFoundException (404)
И т.д. Вы можете найти список здесь: API
Кроме того, вы можете определить свои собственные исключения и ExceptionMapper
классы и добавить эти сопоставители в Джерси с помощью @Provider
аннотации ( источник этого примера ):
public class MyApplicationException extends Exception implements Serializable
{
private static final long serialVersionUID = 1L;
public MyApplicationException() {
super();
}
public MyApplicationException(String msg) {
super(msg);
}
public MyApplicationException(String msg, Exception e) {
super(msg, e);
}
}
Провайдер:
@Provider
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException>
{
@Override
public Response toResponse(MyApplicationException exception)
{
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
Примечание: вы также можете написать ExceptionMappers для существующих типов исключений, которые вы используете.
б) Используйте построитель ответов
Другой способ установить код состояния - использовать Response
построитель для создания ответа с намеченным кодом.
В этом случае тип возврата вашего метода должен быть javax.ws.rs.core.Response
. Это описано в различных других ответах, таких как принятый ответ hisdrewness, и выглядит так:
@GET
@Path("myresource({id}")
public Response retrieveSomething(@PathParam("id") String id) {
...
Entity entity = service.getById(uuid);
if(entity == null) {
return Response.status(Response.Status.NOT_FOUND).entity("Resource not found for ID: " + uuid).build();
}
...
}
2. Успех, но не 200
Другой случай, когда вы хотите установить статус возврата, это когда операция прошла успешно, но вы хотите вернуть код успеха, отличный от 200, вместе с содержимым, которое вы возвращаете в теле.
Частый случай использования - это когда вы создаете новую сущность ( POST
запрос) и хотите вернуть информацию об этой новой сущности или, возможно, о самой сущности вместе с201 Created
кодом состояния.
Одним из подходов является использование объекта ответа, как описано выше, и установка тела запроса самостоятельно. Однако, делая это, вы теряете возможность использовать автоматическую сериализацию в XML или JSON, предоставляемую JAXB.
Это оригинальный метод, возвращающий объектный объект, который будет сериализован в JSON JAXB:
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user){
User newuser = ... do something like DB insert ...
return newuser;
}
Это вернет JSON-представление вновь созданного пользователя, но статус возврата будет 200, а не 201.
Теперь проблема в том, что если я хочу использовать Response
конструктор для установки кода возврата, я должен вернуть Response
объект в моем методе. Как мне все еще вернуть User
объект для сериализации?
а) Установите код в ответе сервлета
Один из подходов к решению этой проблемы состоит в том, чтобы получить объект запроса сервлета и вручную установить код ответа, как показано в ответе Гаретт Уилсон:
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user, @Context final HttpServletResponse response){
User newUser = ...
//set HTTP code to "201 Created"
response.setStatus(HttpServletResponse.SC_CREATED);
try {
response.flushBuffer();
}catch(Exception e){}
return newUser;
}
Метод по-прежнему возвращает объект сущности, а код состояния будет 201.
Обратите внимание, что для того, чтобы это сработало, мне пришлось сбросить ответ. Это неприятный всплеск низкоуровневого кода API сервлета в нашем замечательном ресурсе JAX_RS, и, что еще хуже, после этого заголовки становятся неизменяемыми, поскольку они уже были отправлены по сети.
б) Используйте объект ответа с сущностью
Лучшее решение в этом случае состоит в том, чтобы использовать объект Response и установить сериализацию объекта для этого объекта ответа. Было бы неплохо сделать объект Response универсальным для указания типа объекта полезной нагрузки в этом случае, но в настоящее время это не так.
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response addUser(User user){
User newUser = ...
return Response.created(hateoas.buildLinkUri(newUser, "entity")).entity(restResponse).build();
}
В этом случае мы используем созданный метод класса построителя Response, чтобы установить код состояния на 201. Мы передаем объект сущности (пользователя) в ответ через метод entity ().
В результате получается, что HTTP-код равен 401, как мы и хотели, и тело ответа является точно таким же JSON, как и раньше, когда мы только что вернули объект User. Он также добавляет заголовок местоположения.
Класс Response имеет несколько методов построения для различных статусов (stati?), Таких как:
Response.accepted () Response.ok () Response.noContent () Response.notAcceptable ()
Примечание: объект hateoas - это вспомогательный класс, который я разработал, чтобы помочь генерировать URI ресурсов. Вам нужно будет придумать свой собственный механизм здесь;)
Вот и все.
Надеюсь, этот длинный ответ кому-нибудь поможет :)