Джерси 2.27 - не получать JSON в случае обработки ошибок с использованием ExceptionMapper - PullRequest
0 голосов
/ 14 декабря 2018

Боб-компонент для формы ввода учетных данных

  public class UserCredentials {

    private String username;
    private String password;

    public UserCredentials() {
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

ресурс

@Path("auth")
   public class AuthenticationResource {
    @POST
    @Consumes( MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    @PermitAll
    public Response authenticateUserName(UserCredentials credentials)  {

        EmployeeDao dao = new EmployeeDaoImpl();
        Employee email = dao.loadEmployeeByFieldStr("email", credentials.getUsername());

        if(email == null){
            throw new AccessDeniedException("Non found!");
        }

        credentials.setUsername(email.getEmail());
        Response response = Response.ok(credentials).build();

        return response;
    }

модель для хранения сведений об ошибках

@JsonInclude( JsonInclude.Include.NON_NULL)
public class ApiErrorDetails {

    private Integer status;
    private String title;
    private String message;
    private String path;

    public ApiErrorDetails() {
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public void setTitle(String title) {
        this.title = title;
    }

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

    public void setPath(String path) {
        this.path = path;
    }


}

ExceptionMapper

     @Provider
       public class AccessDeniedExceptionMapper 
              implements   ExceptionMapper<AccessDeniedException> {

        @Context
        private UriInfo uriInfo;

        @Override
        public Response toResponse(AccessDeniedException exception) {


            Response.Status status = Response.Status.UNAUTHORIZED; 

            ApiErrorDetails errorDetails = new ApiErrorDetails();

            errorDetails.setStatus(status.getStatusCode());
            errorDetails.setTitle(status.getReasonPhrase());
            errorDetails.setMessage(exception.getMessage());


            errorDetails.setPath(uriInfo.getAbsolutePath().getPath());

            Response.ResponseBuilder statusResponse = Response.status(status);
            Response.ResponseBuilder entity =  
                                        statusResponse.entity(errorDetails);
            Response.ResponseBuilder type = 
                              entity.type(MediaType.APPLICATION_JSON);
            Response response = type.build();

            return response;
        }
    }

конфигурация регистрации

@ApplicationPath("/api/*")
public class JerseyConfig  extends ResourceConfig {

    public JerseyConfig() {

        packages("com.skillsimprover.restexamples.rest");
        register(AuthenticationResource.class);
        register(AccessDeniedExceptionMapper.class);
        register(AuthenticationExceptionMapper.class);
        register(DataNotFoundExceptionMapper.class);
    }
}

web.xml

<servlet>
        <servlet-name>Jersey REST Service</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.skillsimprover.restexamples.rest</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>Jersey REST Service</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>

pom.xml

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql.version}</version>
    </dependency>


    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet</artifactId>
        <version>2.27</version>
    </dependency>


    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>${jersey.rest.version}</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.jersey.inject</groupId>
        <artifactId>jersey-hk2</artifactId>
        <version>2.26</version>     
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>${servlets.version}</version>
        <scope>provided</scope>
    </dependency>

</dependencies>

Когда RuntimeException поражает, оно должно быть перехвачено классами Джерси, которые реализуют ExceptionMapper interface.

Во время отладки я наблюдал, как формировался ответ с подробными сведениями об ошибках в классе AccessDeniedExceptionMapper .Но на стороне клиента я получаю только код состояния, который полностью установлен, и вместо объекта JSON с подробной информацией об ошибках здесь приведены пустые скобки {}.

Почему?

Ответы [ 2 ]

0 голосов
/ 17 декабря 2018
                             Solution

Ресурс

@Provider
@Produces( MediaType.APPLICATION_JSON )
@Consumes( MediaType.APPLICATION_JSON )
@Path("auth")
public class AuthenticationResource {

    @POST
    @PermitAll
    @Path("username")
    public Response authenticateUserName(UserCredentials credentials)  {

        EmployeeDao dao = new EmployeeDaoImpl();
        Employee email = dao.loadEmployeeByFieldStr("email", credentials.getUsername());

        if(email == null){
            throw new DataNotFoundException("An object not found!!!!!!");
        }

        credentials.setUsername(email.getEmail());
        Response response = Response.ok(credentials).build();

        return response;
    }
...

исключение класса

public class DataNotFoundException extends RuntimeException {

    public DataNotFoundException(String message) {
        super(message);
    }
}
  • изменить библиотеку jersey-media-json-jackson и вместо него поставить библиотеку jackson-jaxrs-json-provider .

  • Класс детализации ошибки должен бытьесть все сеттеры и геттеры.

и

 @JsonInclude(JsonInclude.Include.NON_NULL)
 public class ApiErrorDetails {

    private Integer status;
    private String title;
    private String message;
    private String path;

    public ApiErrorDetails() {
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public void setTitle(String title) {
        this.title = title;
    }

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

    public void setPath(String path) {
        this.path = path;
    }

    public Integer getStatus() {
        return status;
    }

    public String getTitle() {
        return title;
    }

    public String getMessage() {
        return message;
    }

    public String getPath() {
        return path;
    }
}

JacksonJsonProvider.class должен быть зарегистрирован

import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import org.glassfish.jersey.server.ResourceConfig;

import javax.ws.rs.ApplicationPath;

@ApplicationPath("api")
public class JerseyConfig  extends ResourceConfig {

    public JerseyConfig() {

        packages("com.skillsimprover.restexamples.rest");
        register(JacksonJsonProvider.class);

    }
}

Помните, что вы должны поместить аннотацию @ Provider в пользовательские ExceptionMappers , которые вы используете.

@Provider
public class DataNotFoundExceptionMapper <E> implements ExceptionMapper<DataNotFoundException> {

    @Context
    private UriInfo uriInfo;

    @Override
    public Response toResponse(DataNotFoundException exception) {
        return getResponseException(Response.Status.NOT_FOUND, exception);
    }

    private <E> Response  getResponseException(Response.Status status, E exceptionSource ){

        Throwable exception = (Throwable) exceptionSource;

        ApiErrorDetails errorDetails = new ApiErrorDetails();

        errorDetails.setStatus(status.getStatusCode());
        errorDetails.setTitle(status.getReasonPhrase());
        errorDetails.setMessage(exception.getMessage());

        errorDetails.setPath(uriInfo.getAbsolutePath().getPath());

        Response.ResponseBuilder statusResponse = Response.status(status);
        Response.ResponseBuilder entity = statusResponse.entity(errorDetails);
        Response.ResponseBuilder type = entity.type(MediaType.APPLICATION_JSON);
        Response response = type.build();

        return response;
    }
}

Метод getResponseException () можно записатьв другой класс, например ClassUtils.

Если вы знаете, как сделать лучше, напишите здесь

введите описание изображения здесь

0 голосов
/ 17 декабря 2018

В классе ExceptionMapper:

Response.Status status = Response.Status.UNAUTHORIZED; 

status - статическая конечная переменная перечисления, установленная как Response.Status.UNAUTHORIZED.Он устанавливается как одно значение.Вы не можете использовать методы get для такого значения, так как оно не имеет методов получения.Я подозреваю, что следующие несколько строк:

ApiErrorDetails errorDetails = new ApiErrorDetails();

            errorDetails.setStatus(status.getStatusCode());
            errorDetails.setTitle(status.getReasonPhrase());
            errorDetails.setMessage(exception.getMessage());

на самом деле ничего не делают.status.getStatusCode(), status.getReasonPhrase(), status.getMessage() ничего не возвращают, так как приведенные выше методы получения являются не частью класса Response.status, а ApiErrorDetails.Вы должны использовать Response методы получения и выбрать, какие данные будут подходящими для установки.

...