问题
I have implemented a custom exception mapper in order to throw the bad request in my application. Here's the code:
CustomFilterBadRequest:
package com.test.exceptions;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import java.util.Date;
public class CustomFilterBadRequest extends Exception implements
ExceptionMapper<CustomFilterBadRequest> {
private String uriInfo;
public CustomFilterBadRequest() {
super("Invalid Request. Please try again with the valid request");
}
public CustomFilterBadRequest(String uriInfo, String message) {
super(message);
this.uriInfo = uriInfo;
}
@Override
public Response toResponse(CustomFilterBadRequest exception) {
return Response.status(400).entity(new ErrorDetails(new Date(),
400, "bad request", exception.getMessage(),this.uriInfo)).type(MediaType.APPLICATION_JSON).build();
}
}
Issue:
Note: In this example, I have an instance variable called uriInfo. From another service, I do :
throw new CustomFilterBadRequest("uri","message")
Immediately, the constructor gets called and my uriInfo is set to the value "uri". After this, toResponse method gets called.My uriInfo instance variable is reset. why? Whereas, when I made the instance variable to be static, the value is retained.
I am unable to get any documentation on this. Please help.
*****UPDATE*****
When the REST End point related to this exception is invoked, the default constructor of CustomFilterBadRequest is invoked creating an object with state uriInfo initialized to null. Let's call this object as object1.
when we do throw new CustomFilterBadRequest("uri","msg"), an object is created with state uriInfo initialized to the value uri. Let's call this object as object2.
JAX-RS Runtime maps the CustomFilterBadRequest exception to the exception mapper which is also CustomFilterBadRequest here. Hence toResponse method gets called. It gets uriInfo from object1. (which is obviously null here)
And there's no necessity that we should use only class variables inside Custom exception mappers.
This kind of implementation is quite confusing. I have updated with the straightforward implementation in the answer below.
回答1:
The problem is that the toResponse(...)
method is called on the instance of CustomFilterBadRequest
that is registered with the JAX-RS runtime. If the no-arg constructor is used in registration that is why uriInfo always has it's default value of null.
I would recommend that you have your exception defined in one class, and you Exception handler defined in another.
Exception:
public class CustomBadRequest extends Exception {
private String uriInfo;
public CustomBadRequest() {
super("Invalid Request. Please try again with the valid request");
}
public CustomBadRequest(String uriInfo, String message) {
super(message);
this.uriInfo = uriInfo;
}
public String getUriInfo() {
return uriInfo;
}
}
ExceptionMapper:
@Provider
public class CustomFilterBadRequest implements ExceptionMapper<CustomBadRequest> {
@Override
public Response toResponse(CustomBadRequest exception) {
return Response.status(400).entity(new ErrorDetails(new Date(),
400, "bad request", exception.getMessage(),exception.getUriInfo())).type(MediaType.APPLICATION_JSON).build();
}
}
回答2:
I understood the reason behind the issue.
Whenever we hit the end point, default constructors of exception mappers registered with the jersey is called and objects of each type are created with state initialized to default values of its type.
When the custom exception is thrown, the custom exception object is created and JAX-RS runtime searches for the exception mapper to map the exception. If none is found, it just maps to the default exception mapper. If the custom exception mapper is found, toResponse method is called. In this case, we should get the states of customexception from its object itself inside the toResponse method.
I needed this to obtain uriInfo inside exceptionmapper.
Here's the working code:
CustomFilterBadRequest.java
public class CustomFilterBadRequest extends Exception {
public CustomFilterBadRequest(String message) {
super(message);
}
}
CustomFilterBadRequestExceptionMapper.java
public class CustomFilterBadRequestExceptionMapper implements ExceptionMapper<CustomFilterBadRequest> {
private UriInfo uriInfo;
public CustomFilterBadRequestExceptionMapper(@Context UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
@Override
public Response toResponse(CustomFilterBadRequest exception) {
return Response.status(400).entity(new ErrorDetails(Instant.now(),
400, "bad request", exception.getMessage(),this.uriInfo.getPath())).type(MediaType.APPLICATION_JSON).build();
}
}
Thanks a lot everyone for the response.
回答3:
Im pretty sure this is all you have to do:
CustomFilterBadRequest:
package com.test.exceptions;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import java.util.Date;
public class CustomFilterBadRequest extends Exception implements
ExceptionMapper<CustomFilterBadRequest> {
private String uriInfo;
public CustomFilterBadRequest() {
super("Invalid Request. Please try again with the valid request");
}
public CustomFilterBadRequest(String uriInfo, String message) {
super(message);
this.uriInfo = uriInfo;
}
@Override
public Response toResponse(CustomFilterBadRequest exception) {
return Response.status(400).entity(new ErrorDetails(new Date(),
400, "bad request", exception.getMessage(),exception.uriInfo)).type(MediaType.APPLICATION_JSON).build();
}
}
I just changed this.uriInfo
to exception.uriInfo
Basically, when you throw a CustomFilterBadRequestException, JAX-RS creates a new instance to map the exception you threw. This new instance has a default value of uriInfo. So thus it is not saved.
Whatever you do, don’t make uriInfo
static. That would cause huge concurrency issues.
来源:https://stackoverflow.com/questions/49670142/why-instance-variable-inside-custom-exception-mapper-has-to-be-static