Java 使用JAX-RS的RESTAPI中的错误处理

Java 使用JAX-RS的RESTAPI中的错误处理,java,spring,rest,jersey,jax-rs,Java,Spring,Rest,Jersey,Jax Rs,任务:我不想在stacktrace中接收一般的HTTP 500内部服务器错误,也不想在客户端接收同样可怕的stacktrace,我想用另一个statuscode(例如403)查看我的定制消息,这样开发人员会更清楚发生了什么。并向用户添加有关异常的消息 以下是我的应用程序中更改的几个类: 服务器部分: AppException.class-我要转换为此异常的所有服务器响应异常(在返回给客户端之前)。有点像标准的实体类 public class AppException extends WebApp

任务:我不想在stacktrace中接收一般的
HTTP 500内部服务器错误
,也不想在客户端接收同样可怕的stacktrace,我想用另一个statuscode(例如
403
)查看我的定制消息,这样开发人员会更清楚发生了什么。并向用户添加有关异常的消息

以下是我的应用程序中更改的几个类:

服务器部分:

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
-将我的AppException映射到JAX-RS运行时,而不是标准异常,客户端接收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();
    }


}
@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();
    }  
}
客户端部分:

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
-一段UI逻辑,我想在其中处理ErrorHandlingFilter引发的PermissionException

@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) {
                    }
}
}
@SpringPresenter
公共类ApplicationPresenter实现ApplicationView.Observer{
@资源
私有应用服务应用服务;
@资源
私人环境;
@凌驾
public void deleteselected应用程序(BeanItemGrid applicationGrid){
试一试{
applicationService.deleteById(applicationGrid.getSelectedItem().getId());
}捕获(许可例外){
e、 printStackTrace();
e、 getMessage();
}捕获(外观e2除外){
}
}
}
我如何解决我的问题?我仍然收到标准
500 InternalErrorException.


几乎把整个问题又更新了一次

我在这里有不同的方法。您可以在主java方法中启动jetty服务器时尝试此方法

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"}

您可以从

引用当您有一个ExceptionMapper时,您自己并不捕获异常,而是让框架捕获它,在HTTP请求上调用资源方法时。

执行错误处理的正确方法是注册
ExceptionMapper
实例,这些实例知道在发生特定(或通用)异常时应返回什么响应

    @Provider
public class AppExceptionMapper implements ExceptionMapper<AppException> {

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


}
@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();
    }  
}
@Provider
公共类PermissionExceptionHandler实现ExceptionMapper{
@凌驾
公众响应(许可例外情况除外){
//你可以把你需要的逻辑放在这里
返回Response.status(403).entity(yourMessage.build();
}  
}

请查看我的另一个答案以了解更多详细信息:

上面正确地建议,理想的做法是让框架捕获异常,因为您已经实现了一个
异常apper
。 但是,有一点非常重要:如果您需要处理任何未捕获的异常,则需要有一个
异常
类来实现
ExceptionMapper
,该类映射到
Throwable

public class UncaughtExcep implements ExceptionMapper<Throwable>{

   @Override 
   public Response toResponse(Throwable e){

    }
}
公共类UncaughtExcep实现ExceptionMapper{
@凌驾
公众响应(可丢弃的e){
}
}
假设您的类
任何异常都符合这一点。如果没有,那么实施这是一个很好的实践,但是您可以从中提取所需的信息。我只会抛出一个异常,并最终将该异常映射到任何想要的响应

假设您有以下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();
  }
}

当您有一个ExceptionMapper时,您自己不会捕获异常,而是让框架在HTTP请求上调用资源方法时捕获它。(我真的不明白上一个类是做什么的;它是客户机代码吗?@gsl是的,我只是想展示一下我在哪里捕捉到了我的异常。我也试过这么做,但没有得到许可。类,仅限PermissionException。但它不起作用(好吧,你不必在自己的代码中捕捉它。(除了测试程序,但我不明白这有什么意义。)@gsl如果你写你的第一条评论作为对这篇文章的回答,我会接受它作为答案。看来我完全错误地理解了ExcentationMapper的概念,为什么你不处理从ApplicationService/PermissionExceptionApper收到的回复呢。
PermissionException
我自己创建的客户这方面,我有我的
ErrorHandlingFilter
PermissionException
是否也应该有客户端映射器?所有异常映射器都驻留在服务器端。当服务器上的其余端点抛出异常时,异常映射器将截获该异常,并生成将发送的正确响应谢谢。我在我的ÀppExceptionMapper`中使用了你建议的变体。但是我的
Mapper
不想把propper响应扔给我的客户((你能告诉我你的Mapper看起来怎么样,可能是gist或pastebin吗?你也检查过是否调用了Mapper吗?当然-).他从来没有被打过电话,想知道为什么吗?.谢谢你的回复。但是..我已经尝试了一切,我尝试了设置Throwable、Exception、WebApplicationException。没有一个对我有效。问题是我的
ExceptionMapper.class
从未被调用过。JAX-RS运行时只是忽略了我的
ExceptionMapper.class
@Provider
。用于基于WebApplicationXC映射异常