I am new to this, trying to achieve reading some docs but its not working, please bear with me.
I have created a UserNotFoundMapper using ExceptionMappers like this:
public class UserNotFoundMapper implements ExceptionMapper<UserNotFoundException> {
#Override
public Response toResponse(UserNotFoundException ex) {
return Response.status(404).entity(ex.getMessage()).type("text/plain").build();
}
}
This in my service:
#GET
#Path("/user")
public Response getUser(#QueryParam("id") String id) throws UserNotFoundException{
//Some user validation code with DB hit, if not found then
throw new UserNotFoundException();
}
The UserNotFoundException is an User-Defined Exception.
I tried this:
public class UserNotFoundException extends Exception {
//SOME block of code
}
But when I invoke the service, the UserDefinedExceptionMapper is not getting invoked. It seems I might be missing something in the UserDefinedException. How to define this exception then?
Please let me know how to define the UserNotFoundException.
You need to annotate your exception mapper with #Provider, otherwise it will never get registered with the JAX-RS runtime.
#Provider
public class UserNotFoundMapper implements
ExceptionMapper<UserNotFoundException> {
#Override
public Response toResponse(UserNotFoundException ex) {
return Response.status(404).entity(ex.getMessage()).type("text/plain")
.build();
}
}
What I usually do when creating APIs is create my own exception that extends from RuntimeException so I don't necessarily have to catch my exception.
Here's an example:
NOTE: I'm using JAX-RS with Jersey
First: create my own Exception that extends from RuntimeException.
public class ExceptionName extends RuntimeException {
private int code;
private String message;
public int getCode(){
return code;
}
public String getMessage(){
return message;
}
public ExceptionName(int code, String message) {
this.code = code;
this.message = message;
}
}
Also implement a ExceptionMapper
#Provider
public class ExceptionName implements ExceptionMapper<ExceptionName>{
#Override
public Response toResponse(ExceptionName exception) {
return Response.status(exception.getCode()).entity(exception.getMessage()).build();
}
}
And every time that I want to throw an exception I just do it like this anywhere, the exception mapper will take care of returning a response to the client consuming the API
throw new ExceptionName(500,"there was an error with something here");
One small remark , try to Use Response.Status.NOT_FOUND rather than using 404 etc. Code will be more readable and less prone to typos , the same goes for "text/plain". Below is the code that will handle exception as you mentioned.
Oh and one more thing remember to annotate your method #Produces(MediaType.TEXT_PLAIN) in your interface
public class UserNotFoundException extends Exception {
//...
}
public class UserServiceImpl implements UserService {
#Override
public Response getUser(#QueryParam("id") String id) {
final Response response;
try{
// call user method
//if everything is ok
response = Response.status(Response.Status.OK).entity(whateverYouWant).type(MediaType.TEXT_PLAIN).build();
} catch(UserNotFoundException ex) {
response = new UserNotFoundMapper().toResponse(ex);
}
return response;
}
}
In client slide you can check
public static boolean isUserExists(final Response serverResp) {
return serverResp != null && serverResp.getStatus() == Response.Status.NOT_FOUND.getStatusCode();
}
Related
I have around 20 to 30 different types of different Exceptions all extending Exception class in Java.
one example is:
public class NoHandlerFoundException extends Exception {
private static final long serialVersionUID = -9079454849611061074L;
public NoHandlerFoundException() {
super();
}
public NoHandlerFoundException(final String message) {
super(message);
}
}
Other example is:
public class ResourceNotFoundException extends Exception{
private static final long serialVersionUID = -9079454849611061074L;
public ResourceNotFoundException() {
super();
}
public ResourceNotFoundException(final String message) {
super(message);
}
}
and many more.
As you can see most of the code is repeated and then I use ControllerAdvice like (I know code in ControllerAdvice argument exception class should be proper):
#ExceptionHandler({NoHandlerFoundException.class, ResourceNotFoundException.class})
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public #ResponseBody ExceptionResponse handleResourceNotFound(final NoHandlerFoundException exception,
final HttpServletRequest request) {
ExceptionResponse error = new ExceptionResponse();
error.setErrorMessage(exception.getMessage());
error.callerURL(request.getRequestURI());
return error;
}
So I want to know if we have any way in which I can optimize above exceptions and not write individual classes doing almost same job n times of times but still want to differentiate between them.
Thank you.
You can use below approach to reduce code redundancy.
#ExceptionHandler(value = {NoHandlerFoundException.class, ResourceNotFoundException.class})
protected ResponseEntity handleInvalidDataException(RuntimeException exception, WebRequest request) {
ExceptionResponse error = new ExceptionResponse();
error.setErrorMessage(exception.getMessage());
error.callerURL(request.getRequestURI());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
I have a test case where am throwing exception incase of some basic validation. but ExceptionMapper is not being invoked. But if i run from postman to hit the service it is working fine.
Do Junit test have to run differently for ExceptionMapper ?
Test case :
#Test
public void itShouldHavePersonNumber() {
RestAuthController controller = new RestAuthController();
Response response = controller.insertGuid(null, "m012");
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> {controller.insertGuid(null, "m012");});
assertThat(response.getStatus()).isEqualTo(Status.BAD_REQUEST.getStatusCode());
}
Controller:
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response insertGuid(#QueryParam("personNumber") Integer personNumber, #QueryParam("guId") String guId ) throws ValidationException {
if(guId == null || guId.isEmpty()) {
throw new ValidationException("guId is Required");
}
}
Exception Mapper :
#Provider
public class ValidationMapper implements ExceptionMapper<ValidationException> {
#Override
public Response toResponse(ValidationException ex) {
return Response.status(Response.Status.BAD_REQUEST).entity(ex.getMessage()).type(MediaType.TEXT_PLAIN).build();
}
}
Exception:
public class ValidationException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public ValidationException() {
super();
}
public ValidationException(String message, Throwable cause) {
super(message, cause);
}
public ValidationException(String message) {
super(message);
}
}
Why do you think the exception mapper should be called? It is not an integration test. All you are doing is instantiating the class and then calling a method. There is nothing magical in Java that will make the exception mapper be called. You need to run an integration test with the Jersey application running (and the mapper registered) if you want the mapper to be called.
One way to run an integration test with Jersey is to use it's Test Framework. Below is an example.
public class ValidationExceptionTest extends JerseyTest {
public static class ValidationException extends RuntimeException {}
public static class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {
#Override
public Response toResponse(ValidationException e) {
return Response.status(400).entity("boo boo").build();
}
}
#Path("echo-name")
public static class EchoNameResource {
#GET
public String echoName(#QueryParam("name") String name) {
if (name == null || name.isEmpty()) {
throw new ValidationException();
}
return name;
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(EchoNameResource.class)
.register(ValidationExceptionMapper.class);
}
#Test
public void testResponseOkWithQueryParam() {
final Response response = target("echo-name")
.queryParam("name", "peeskillet")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.readEntity(String.class)).isEqualTo("peeskillet");
}
#Test
public void testResponseBadRequestWithNoQueryParam() {
final Response response = target("echo-name")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(400);
}
}
I have created a new exception class in my Dropwizard service that extends BadRequestException.
public class CustomBadRequestException extends BadRequestException {
private static final long serialVersionUID = 1L;
private List<ValidationFailureDto> validationFailures;
public CustomBadRequestException() {
super();
}
public CustomBadRequestException(final List<ValidationFailureDto> validationFailures) {
super();
this.validationFailures = validationFailures;
}
#ApiModelProperty(value = "List of validationFailures")
public List<ValidationFailureDto> getValidationFailures() {
return validationFailures;
}
}
When I throw that exception at first I was only getting back the deserialised BadRequestException, minus the additional property (validationFailures)
{
code: "400",
message: "Bad request"
}
This is because Dropwizard's internals have a default exception mapper that allows Jetty/Jackson to understand domain exceptions and how to send the appropriate HTTP response.
To overcome this you can implement your own ExceptionMapper class and register it with Dropwizard.
public class CustomBadRequestExceptionMapper implements ExceptionMapper<SamplePackOrderBadRequestException> {
/**
* Allows jackson to deserialise custom exceptions and its properties to JSON response
*
* #param exception exception
* #return response object
*/
#Override
public Response toResponse(final SamplePackOrderBadRequestException exception) {
if (exception instanceof SamplePackOrderBadRequestException) {
SamplePackOrderBadRequestException samplePackOrderBadRequestException
= (SamplePackOrderBadRequestException) exception;
return Response
.status(400)
.entity(samplePackOrderBadRequestException)
.build();
}
return Response.status(400).build();
}
}
However this issue with this is that it deserializes super (Throwable), so you get every single inherited property added in the response which I do not want.
To combat this I tried adding Jackson annotations like so:
#JsonIgnoreProperties(value = "stackTrace")
This is not an optimal solution as there are several properties other than stackTrace that I will need to ignore.
So to summarise, how can I get Dropwizard to properly deserialize my CustomException class without all the additional clutter that I do not need?
I think the easier option is to transform exception to a Error bean and return it as shown below.
public class CustomBadRequestExceptionMapper implements ExceptionMapper<SamplePackOrderBadRequestException> {
#Override
public Response toResponse(final SamplePackOrderBadRequestException exception) {
if (exception instanceof SamplePackOrderBadRequestException) {
SamplePackOrderBadRequestException ex
= (SamplePackOrderBadRequestException) exception;
return Response
.status(400)
.entity(new ErrorBean(400,ex.getMessage,ex.getgetValidationFailures()))
.build();
}
return Response.status(400).build();
}
}
And ErrorBean.java
public static class ErrorBean{
private int code;
private String message;
private List<ValidationFailureDto> failures;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public List<ValidationFailureDto> getFailures() {
return failures;
}
public void setFailures(List<ValidationFailureDto> failures) {
this.failures = failures;
}
}
I have a controller like below
#Controller("myController")
#RequestMapping("api")
public class MyController {
#RequestMapping(method = RequestMethod.GET, value = "/get/info/{id}", headers = "Accept=application/json")
public #ResponseBody
Student getInfo(#PathVariable String info) {
.................
}
#ExceptionHandler(Throwable.class)
#ResponseStatus( HttpStatus.EXPECTATION_FAILED)
#ResponseBody
public String handleIOException(Throwable ex) {
ErrorResponse errorResponse = errorHandler.handelErrorResponse(ex);
return errorResponse.toString();
}
}
The controller has an error handling mechanism, in the error handling option it always return expectation fail status code 417. But I need to set a dynamic error Http status code like 500, 403 etc depending on type of error. How do I do this?
You need to change the type of the output value ResponseEntity. Answer here:
How to respond with HTTP 400 error in a Spring MVC #ResponseBody method returning String?
I get a solution and going to share this and also like to know any good suggestions.
#Controller("myController")
#RequestMapping("api")
public class MyController {
#RequestMapping(method = RequestMethod.GET, value = "/get/info/{id}", headers = "Accept=application/json")
public #ResponseBody
Student getInfo(#PathVariable String info) {
// ...
}
}
// ...
#ExceptionHandler(Throwable.class)
//#ResponseStatus( HttpStatus.EXPECTATION_FAILED)<<remove this line
#ResponseBody
public String handleIOException(HttpServletResponse httpRes,Throwable ex){ // <<Change this
if (some condition) {
httpRes.setStatus(HttpStatus.BAD_GATEWAY.value());
} else {
httpRes.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
}
ErrorResponse errorResponse = errorHandler.handleErrorResponse(ex);
return errorResponse.toString();
}
Expected out in rest client :
502 Bad Gateway
{
"status":"BAD_GATEWAY",
"error":"java.lang.UnsupportedOperationException",
"message":"Some error message"
}
Thanks for your replies. I still need pointers for good practices.
Going by the code above, you need to be more careful about which exceptions you are throwing and handling. Setting up an exception handler for Throwable seems overly broad.
The way I do this is to create an ErrorMessage class with my XML/JSON marshalling annotations.
#XmlRootElement(name = "error")
public class ErrorMessage {
private Throwable exception;
private String message;
public ErrorMessage() {
this.message = "";
}
public ErrorMessage(String message) {
this.message = message;
}
public ErrorMessage(Throwable exception) {
this.exception = exception;
this.message = exception.getLocalizedMessage();
}
#XmlTransient
#JsonIgnore
public Throwable getException() {
return exception;
}
public void setException(Throwable exception) {
this.exception = exception;
}
#XmlElement(name = "message")
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
With that in place, I tend to create my own application exceptions and then create my exception handler methods such as:
#ExceptionHandler(ResourceNotFoundException.class)
#ResponseBody
#ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorMessage handleResourceNotFoundException(ResourceNotFoundException e, HttpServletRequest req) {
return new ErrorMessage(e);
}
#ExceptionHandler(InternalServerErrorException.class)
#ResponseBody
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorMessage handleInternalServerErrorException(InternalServerErrorException e, HttpServletRequest req) {
return new ErrorMessage(e);
}
With those in place, I just need to throw appropriate exceptions from my controller methods. For instance, if I throw a ResourceNotFoundException, then Spring will redirect that to my handleResourceNotFoundException method, which returns a 404, and that will also return JSON or XML representing the error.
You can use an Aspect for your API. If you define an #Around interceptor for your service, you can change the response content.
In Jersey, how can we 'replace' the status string associated with a known status code?
e.g.
return Response.status(401).build();
generates a HTTP response that contains:
HTTP/1.1 401 Unauthorized
I (not me, but the client application) would like to see the response as:
HTTP/1.1 401 Authorization Required
I tried the following approaches but in vain:
1) This just adds the String in the body of the HTTP response
return Response.status(401).entity("Authorization Required").build();
2) Same result with this too:
ResponseBuilder rb = Response.status(401);
rb = rb.tag("Authorization Required");
return rb.build();
Appreciate your help!
-spd
To do this in Jersey you have the concept of WebApplicationException class. One method is to simply extend this class and all one of the methods to set the error text that is returned. In your case this would be:
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.core.Response.*;
public class UnauthorizedException extends WebApplicationException {
/**
* Create a HTTP 401 (Unauthorized) exception.
*/
public UnauthorizedException() {
super(Response.status(Status.UNAUTHORIZED).build());
}
/**
* Create a HTTP 404 (Not Found) exception.
* #param message the String that is the entity of the 404 response.
*/
public UnauthorizedException(String message) {
super(Response.status(Status.UNAUTHORIZED).entity(message).type("text/plain").build());
}
}
Now in your code that implements the rest service you would simply throw a new exception of this type, passing in the text value in the constructor e.g.
throw new UnauthorizedException("Authorization Required");
That can create a class like this for each of your web exceptions and throw in a similar fashion.
This is also explained in the Jersey user guide - although the code is actually slightly incorrect:
https://jersey.github.io/nonav/documentation/latest/user-guide.html/#d4e435
I'm not sure JSR 339: JAX-RS 2.0: The Java API for RESTful Web Services already covered this or not.
You might have to extend the Response.StatusType for this.
public abstract class AbstractStatusType implements StatusType {
public AbstractStatusType(final Family family, final int statusCode,
final String reasonPhrase) {
super();
this.family = family;
this.statusCode = statusCode;
this.reasonPhrase = reasonPhrase;
}
protected AbstractStatusType(final Status status,
final String reasonPhrase) {
this(status.getFamily(), status.getStatusCode(), reasonPhrase);
}
#Override
public Family getFamily() { return family; }
#Override
public String getReasonPhrase() { return reasonPhrase; }
#Override
public int getStatusCode() { return statusCode; }
public ResponseBuilder responseBuilder() { return Response.status(this); }
public Response build() { return responseBuilder().build(); }
public WebApplicationException except() {
return new WebApplicationException(build());
}
private final Family family;
private final int statusCode;
private final String reasonPhrase;
}
And here are some extended statust types.
public class BadRequest400 extends AbstractStatusType {
public BadRequest400(final String reasonPhrase) {
super(Status.BAD_REQUEST, reasonPhrase);
}
}
public class NotFound404 extends AbstractStatusType {
public NotFound404(final String reasonPhrase) {
super(Status.NOT_FOUND, reasonPhrase);
}
}
This is how I do.
#POST
public Response create(final MyEntity entity) {
throw new BadRequest400("bad ass").except();
}
#GET
public MyEntity read(#QueryParam("id") final long id) {
throw new NotFound404("ass ignorant").except();
}
// Disclaimer
// I'm not a native English speaker.
// I don't know what 'bad ass' or 'ass ignorant' means.