Пользовательский ответ HTTP-статуса с JAX-RS (Джерси) и @RolesAllowed
С моей очень простой службой JAX-RS я использую Tomcat с областью JDBC для аутентификации, поэтому я работаю над аннотациями JSR 250.
Дело в том, что я хочу вернуть тело пользовательского сообщения в ответе статуса HTTP. Код состояния (403) должен оставаться неизменным. Например, моя служба выглядит следующим образом:
@RolesAllowed({ "ADMIN" })
@Path("/users")
public class UsersService {
@GET
@Produces(MediaType.TEXT_PLAIN)
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public String getUsers() {
// get users ...
return ...;
}
}
Если пользователь с другой ролью, чем "ADMIN", обращается к службе, я хочу изменить ответное сообщение на что-то подобное (в зависимости от типа носителя [xml/json]):
<error id="100">
<message>Not allowed.</message>
</error>
В настоящий момент Джерси возвращает следующее тело:
HTTP Status 403 - Forbidden
type Status report
message Forbidden
description Access to the specified resource (Forbidden) has been forbidden.
Apache Tomcat/7.0.12
Как я могу изменить тело сообщения по умолчанию? Есть ли способ обработать исключение (возможно, брошенное) для создания моего собственного ответа HTTP-статуса?
Ответы
Ответ 1
Самый простой способ справиться с подобным делом - это выбросить исключение и зарегистрировать преобразователь исключений для преобразования в тип сообщения, которое вы хотите отправить в этом случае. Итак, предположим, что вы выбрали AccessDeniedException
, тогда у вас будет такой обработчик (с полными именами классов в местах для ясности):
@javax.ws.rs.ext.Provider
public class AccessDeniedHandler
implements javax.ws.rs.ext.ExceptionMapper<AccessDeniedException> {
public javax.ws.rs.core.Response toResponse(AccessDeniedException exn) {
// Construct+return the response here...
return Response.status(403).type("text/plain")
.entity("get lost, loser!").build();
}
}
То, как вы регистрируете конфигуратор исключений, зависит от используемой структуры, но для Джерси вам все равно, просто используя @Provider
. Я дам вам понять, как вы хотите генерировать нужные документы ошибок, но я рекомендую обрабатывать отказы как некорректные коды ошибок HTTP (это больше RESTful...)
Ответ 2
С созданием ExceptionMapper
(отображение исключений WebApplicationException
) можно "уловить" определенные исключения, вызванные приложением:
@Provider
public class MyExceptionMapper implements ExceptionMapper<WebApplicationException> {
@Override
public Response toResponse(WebApplicationException weException) {
// get initial response
Response response = weException.getResponse();
// create custom error
MyError error = ...;
// return the custom error
return Response.status(response.getStatus()).entity(error).build();
}
}
Вам также необходимо добавить пакет в ваше приложение web.xml для регистрации поставщика:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>
com.myapp.userservice; // semi-colon seperated
com.myapp.mappedexception
</param-value>
</init-param>
Ответ 3
REST основан на HTTP, поэтому вам не нужно изменять поведение по умолчанию при неудаче аутентификации. Ошибка 403 при доступе к ресурсу достаточно для того, чтобы клиент мог четко понимать, что добавляет.
Чем больше ваших ресурсов соответствует HTTP, тем больше других это может понять.