Подтвердить что ты не робот

Обработка ошибок в REST API с помощью JAX-RS

Задача: Вместо того, чтобы получать общий HTTP 500 Internal Server Error в моем стеке и такой же ужасной stacktrace на стороне клиента, я хочу увидеть свое настроенное сообщение с другим кодом состояния (например, t21 > ), что разработчику будет намного понятнее, что произошло. И добавьте сообщение пользователю об исключении.

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

СЕРВЕРНАЯ ЧАСТЬ:

AppException.class - все мои исключения ответа сервера (перед возвратом клиенту) Я хочу преобразовать это исключение. Стандартный класс сущности класса

public class AppException extends WebApplicationException {

Integer status;

/** application specific error code */
int code;

/** link documenting the exception */
String link;

/** detailed error description for developers */
String developerMessage;

public AppException(int status, int code, String message, String developerMessage, String link) {
    super(message);
    this.status = status;
    this.code = code;
    this.developerMessage = developerMessage;
    this.link = link;
}

public int getStatus() {
    return status;
}

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

public int getCode() {
    return code;
}

public void setCode(int code) {
    this.code = code;
}

public String getDeveloperMessage() {
    return developerMessage;
}

public void setDeveloperMessage(String developerMessage) {
    this.developerMessage = developerMessage;
}

public String getLink() {
    return link;
}

public void setLink(String link) {
    this.link = link;
}

public AppException() {
}

public AppException(String message) {
    super("Something went wrong on the server");
}
}

ÀppExceptionMapper.class - mapps my AppException для JAX-RS Runtime, вместо стандартного исключения клиент получает AppException.

    @Provider
public class AppExceptionMapper implements ExceptionMapper<AppException> {

    @Override
    public Response toResponse(AppException exception) {
        return Response.status(403)
                .entity("toResponse entity").type("text/plain").build();
    }


}

ApplicationService.class - мой класс службы, который вызывает AppException

 @Path("/applications")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public interface ApplicationService {


    @DELETE
    @Path("/deleteById")
    void deleteById(@NotNull Long id) throws AppException;
}

ЧАСТЬ КЛИЕНТА:

ErrorHandlingFilter.class - мой утилита отзыва AppException. Здесь я хочу преобразовать каждое исключение ответа в другое исключение в зависимости от состояния.

@Provider
public class ErrorHandlingFilter implements ClientResponseFilter {

    private static ObjectMapper _MAPPER = new ObjectMapper();

    @Override
    public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
        if (responseContext.getStatus() != Response.Status.OK.getStatusCode()) {
            if(responseContext.hasEntity()) {
                Error error = _MAPPER.readValue(responseContext.getEntityStream(), Error.class);
                String message = error.getMessage();

                Response.Status status = Response.Status.fromStatusCode(responseContext.getStatus());
                AppException clientException;

                switch (status) {

                case INTERNAL_SERVER_ERROR:
                    clientException = new PermissionException(message);
                    break;


                case NOT_FOUND:
                    clientException = new MyNotFoundException(message);
                    break;

                default:
                    clientException =  new WhatEverException(message);
                }
                    throw clientException;
        }
    }
    }
}

PermissionException.class - исключение в том, что я хочу преобразовать AppException, если он содержит 500 статусных кодов.

public class PermissionException extends AppException{

        public PermissionException(String message) {
    super("403 - Forbidden. You dont have enough rights to delete this Application");

}

Integer status;

/** application specific error code */
int code;

/** link documenting the exception */
String link;

/** detailed error description for developers */
String developerMessage;

public PermissionException(int status, int code, String message, String developerMessage, String link) {
    super(message);
    this.status = status;
    this.code = code;
    this.developerMessage = developerMessage;
    this.link = link;
}

public int getStatus() {
    return status;
}

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

public int getCode() {
    return code;
}

public void setCode(int code) {
    this.code = code;
}

public String getDeveloperMessage() {
    return developerMessage;
}

public void setDeveloperMessage(String developerMessage) {
    this.developerMessage = developerMessage;
}

public String getLink() {
    return link;
}

public void setLink(String link) {
    this.link = link;
}

public PermissionException() {}


}

ApplicationPresenter.class - часть логики пользовательского интерфейса, где я хочу что-то делать с PermissionException, созданным ErrorHandlingFilter.

@SpringPresenter
public class ApplicationPresenter implements ApplicationView.Observer {

@Resource
    private ApplicationService applicationService;

    @Resource
    private UiEnvironment uiEnvironment;

@Override
    public void deleteSelectedApplication(BeanItemGrid<Application> applicationGrid) {

        try {
applicationService.deleteById(applicationGrid.getSelectedItem().getId());
                    } catch (PermissionException e) {
                        e.printStackTrace();
                        e.getMessage();
                    } catch (AppException e2) {
                    }
}
}

Как я могу решить свою проблему? Я все еще получаю стандартный 500 InternalErrorException.

ОБНОВЛЕНО ПОЧТИ ВО ВСЕГО ВРЕМЕНИ!

4b9b3361

Ответ 1

Если у вас есть ExceptionMapper, вы сами не поймаете это исключение, но можете его поймать, когда метод ресурса вызывается по HTTP-запросу.

Ответ 2

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

@Provider
public class PermissionExceptionHandler implements ExceptionMapper<PermissionException>{
    @Override
    public Response toResponse(PermissionException ex){
        //You can place whatever logic you need here
        return Response.status(403).entity(yourMessage).build();
    }  
}

Пожалуйста, взгляните на мой другой ответ для более подробной информации: fooobar.com/questions/228502/...

Ответ 3

У меня здесь другой подход. Вы можете попробовать это при запуске вашего сервера причала в основном java-методе

public static void main(String[] args) throws UnknownHostException, JSONException, IOException, Exception {

        MyMain myMain = new MyMain();

        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");

        Server jettyServer = new Server(5550);
        jettyServer.setHandler(context);
        context.setErrorHandler(new ErrorHandler());
        // default error handler for resources out of "context" scope
        jettyServer.addBean(new ErrorHandler());

        ServletHolder jerseyServlet = context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/*");
        jerseyServlet.setInitOrder(0);

        // Tells the Jersey Servlet which REST service/class to load.
        jerseyServlet.setInitParameter("jersey.config.server.provider.classnames",
                ControllerInn.class.getCanonicalName() );

        try {
            jettyServer.start();            
            jettyServer.join();

        } catch (Exception ex) {
            Logger.getLogger(ControllerInn.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            jettyServer.destroy();
        }
    }
    /**
     * Dummy error handler that disables any error pages or jetty related messages and returns our
     * ERROR status JSON with plain HTTP status instead. All original error messages (from our code) are preserved
     * as they are not handled by this code.
     */
    static class ErrorHandler extends ErrorPageErrorHandler {
        @Override
        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
            response.getWriter()
            .append("{\"message\":\"HTTP ERROR ")
            .append(String.valueOf(response.getStatus()))
            .append("\"}");
        }
    }

Итак, вы можете получить такой вывод

{"message":"HTTP ERROR 500"}

Вы можете ссылаться на здесь

Ответ 4

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

public class UncaughtExcep implements ExceptionMapper<Throwable>{

   @Override 
   public Response toResponse(Throwable e){

    }
}

Предполагая, что ваш класс WhatEverException подходит к этому. Если нет, то это хорошая практика для реализации

Ответ 5

Это пример Джерси, но вы можете извлечь необходимую информацию из . Я бы только бросил исключение и наметил это исключение на любой желаемый ответ в конце.

Предположим, что у вас есть следующий метод ressource, исключающий исключение:

@Path("items/{itemid}/")
public Item getItem(@PathParam("itemid") String itemid) {
  Item i = getItems().get(itemid);
  if (i == null) {
    throw new CustomNotFoundException("Item, " + itemid + ", is not found");
  }

  return i;
}

Создайте свой класс исключения:

public class CustomNotFoundException extends WebApplicationException {

  /**
  * Create a HTTP 404 (Not Found) exception.
  */
  public CustomNotFoundException() {
    super(Responses.notFound().build());
  }

  /**
  * Create a HTTP 404 (Not Found) exception.
  * @param message the String that is the entity of the 404 response.
  */
  public CustomNotFoundException(String message) {
    super(Response.status(Responses.NOT_FOUND).
    entity(message).type("text/plain").build());
  }
}

Теперь добавьте свой блок отображения:

@Provider
public class EntityNotFoundMapper implements ExceptionMapper<CustomNotFoundException> {
  public Response toResponse(CustomNotFoundException  ex) {
    return Response.status(404).
      entity("Ouchhh, this item leads to following error:" + ex.getMessage()).
      type("text/plain").
      build();
  }
}

В конце концов вы должны зарегистрировать свой блок отображения исключений, чтобы он мог использоваться в вашем приложении. Вот несколько псевдокодов:

register(new EntityNotFoundMapper());
//or
register(EntityNotFoundMapper.class);