java 使用 JAX-RS 处理 REST API 中的错误
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38971609/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Error handling in REST API with JAX-RS
提问by t_sologub
The task:Instead of receiving general HTTP 500 Internal Server Error
in my stacktrace and the same horrible stacktrace on the client side I want to see my customized message with another statuscode (403
for example), that it will be much clearer for the developer, what has happend. And add some message to User about the Exception.
任务:不是HTTP 500 Internal Server Error
在我的堆栈跟踪中接收一般信息和在客户端接收同样可怕的堆栈跟踪,我希望看到我的自定义消息和另一个状态码(403
例如),这对开发人员来说会更清楚,发生了什么。并向用户添加一些关于异常的消息。
Here are couple of changed classes from my application:
以下是我的应用程序中的几个更改类:
SERVER PART:
服务器部分:
AppException.class
- all my Server Response exceptions (before giving back to client) I want to transform into this exception. Kinda standard entity class
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 to the JAX-RS Runtime, instead standard exception, client receives AppException.
à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();
}
}
ApplicationService.class
- my Service class that throws AppException
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;
}
CLIENT PART:
客户部分:
ErrorHandlingFilter.class
- my Response catcher of the AppException. Here I want to transform each Response exception to another exception depending on the status.
ErrorHandlingFilter.class
- 我的 AppException 的响应捕捉器。在这里,我想根据状态将每个 Response 异常转换为另一个异常。
@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
- exception in what I want to transform AppException, if it came with 500 status code.
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
- piece of UI logic, where I want something to do with PermissionException thrown by the ErrorHandlingFilter.
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) {
}
}
}
How can I resolve my problem? I am still receiving standard 500 InternalErrorException.
我该如何解决我的问题?我还在接收标准500 InternalErrorException.
UPDATED ALMOST THE WHOLE QUESTION ONE MORE TIME!
更新了几乎整个问题一次!
回答by gsl
When you have an ExceptionMapper, you don't catch the exception yourself, but have the framework catch it, when the resource method is invoked on an HTTP request.
当您有一个 ExceptionMapper 时,当资源方法在 HTTP 请求上被调用时,您不会自己捕获异常,而是让框架捕获它。
回答by Svetlin Zarev
The proper way to perform error handling is by registering ExceptionMapper
instances that know what response should be returned in case of specific (or generic) exception.
执行错误处理的正确方法是注册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();
}
}
Please take a look at my other answer for more details: https://stackoverflow.com/a/23858695/2588800
有关更多详细信息,请查看我的其他答案:https: //stackoverflow.com/a/23858695/2588800
回答by hiaclibe
This is a Jersey example, but you can extract the needed informations from here. I would only throw an exception and map this exception to any wanted response in the end.
这是一个Jersey 示例,但您可以从此处提取所需的信息。我只会抛出一个异常并将这个异常映射到最后任何想要的响应。
Lets assume you have following ressource method, thowing the exception:
假设您有以下资源方法,抛出异常:
@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;
}
Create your exception class:
创建您的异常类:
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());
}
}
Now add your exception mapper:
现在添加您的异常映射器:
@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();
}
}
In the end you have to register your exception mapper, so it can be used in your application. Here is some pseudo-code:
最后,您必须注册您的异常映射器,以便在您的应用程序中使用它。下面是一些伪代码:
register(new EntityNotFoundMapper());
//or
register(EntityNotFoundMapper.class);
回答by Young Emil
I have a different approach here. You can try this when starting your jetty server in the main java method
我在这里有不同的方法。您可以在主 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("\"}");
}
}
So you can get an output like this
所以你可以得到这样的输出
{"message":"HTTP ERROR 500"}
You can Reference from here
你可以从这里参考
回答by Akash Mishra
Suggested correctly above, ideal practice is to let the framework catch the Exception for you now that you have implemented an ExceptionMapper
.
However, one important point overviewing the phenomena which you are executing: if you need to handle any uncaught exceptions, you need to have an Exception
class implementing ExceptionMapper
which maps to Throwable
上面正确建议,理想的做法是让框架为您捕获异常,因为您已经实现了ExceptionMapper
. 但是,概述您正在执行的现象的一个重要点:如果您需要处理任何未捕获的异常,您需要有一个Exception
实现ExceptionMapper
映射到的类Throwable
public class UncaughtExcep implements ExceptionMapper<Throwable>{
@Override
public Response toResponse(Throwable e){
}
}
Assuming your class WhatEverException
caters to that. If not, then its a good practice to implement
假设你的班级WhatEverException
迎合了这一点。如果没有,那么实施它是一个很好的做法