I use Jersey API for my REST service. My question is: Is there a more elegant way of returning exceptions in a JSON form? Is it better to concern myself with creating a json object myself and attaching it to the response directly?
This is a simplified example of one of the methods in the service. As you see, I use HashMap only because the method may throw an exception, in which case I need to return information about It.
@Path("/admin")
public class AdminService {
@Context
HttpServletRequest request;
@POST
@Produces(MediaType.APPLICATION_JSON)
public Map<Integer, String> createCompany(Company company){
Map<Integer, String> map = new HashMap<>();
try{
AdminFacade adminFacade = (AdminFacade)Utility.getFacade(request);
adminFacade.createCompany(company);
map.put(1,"success");
} catch (ExceptionREST e) {
map.put(e.getErrorNumber(), e.getMessage());
} finally {
return map;
}
}
}
To deal with exceptions, the recommended practice is to follow the sequence outlined below: Determine whether the REST API request succeeded or failed, based on the HTTP status response code returned. If the REST API request failed and the response is application/json, serialize the model.
Go to the flow of the REST API method or the callback (such as OnAuthentication or OnRequest) where you want to throw the error and add a Raise Error element. Set the Exception property to the User Exception you have created. Set the Exception Message property to your custom error message.
Is it valid for a function to return exception objects? Absolutely. Here is a short list of examples where this may be appropriate: An exception factory.
Altogether, the most common way is to use @ExceptionHandler on methods of @ControllerAdvice classes so that the exception handling will be applied globally or to a subset of controllers. ControllerAdvice is an annotation introduced in Spring 3.2, and as the name suggests, is “Advice” for multiple controllers.
You can create a class like the one below to represent an error,
@JsonPropertyOrder({ "code", "field", "message" })
public class ErrorInfo {
private String code;
private String field;
private String message;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
You can create a class which extends an exception like this,
public class InvalidInputException extends RuntimeException {
private static final long serialVersionUID = -5027121014723838738L;
private List<ErrorInfo> errors;
public List<ErrorInfo> getErrors() {
return this.errors;
}
public InvalidInputException(List<ErrorInfo> errors) {
super();
this.errors = errors;
}
public InvalidInputException(String message, List<ErrorInfo> errors) {
super(message);
this.errors = errors;
}
}
And have a exception mapper, where you can convert the List to json and return to the user with http status code 400 (Bad Request).
@Provider
public class InvalidInputExceptionMapper implements ExceptionMapper<InvalidInputException> {
@Override
@Produces(MediaType.APPLICATION_JSON)
public Response toResponse(InvalidInputException e) {
ResponseBuilder rb = Response.status(Status.BAD_REQUEST);
rb.entity(e.getErrors());
return rb.build();
}
}
Http Response will be,
HTTP/1.1 400 BAD REQUEST
{
"errors": [{
"error": {
"code": "100",
"field": null,
"message": "Name is required"
},
"error": {
"code": "100",
"field": null,
"message": "Age is required"
}
}]
}
I believe it is quite popular that people use http response status code to handle the error. E.g. 404 status is not found 5xx is server internal error e.t.c. You can easily set the error code by using the Response object. Instead of returning a map, return a Response object.
@Path("/admin")public class AdminService {
@Context
HttpServletRequest request;
@POST
@Produces(MediaType.APPLICATION_JSON)
public Response createCompany(Company company){
Map<Integer, String> map = new HashMap<>();
try{
AdminFacade adminFacade = (AdminFacade)Utility.getFacade(request);
Company commpany=adminFacade.createCompany(company);//this entity annotated by XmlRootElement
Response response=Response.ok().entity(company).build();
} catch (ExceptionREST e) {
response=Response.status(404).build();
} return response;
}}
To make the Restful api more robust, some will return an OK response to prevent "smart redirect" from the server and output some weird html. you can refer here for a list of http status code and what it mean. For Java EE Response class, you can refer the official javadoc
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With