I created a program that communicates through http requests. I use Postman to send requests. When I make a registration, I invoke the API method. If this method throws some exceptions, I manage it with "HandlerMapping" which catches the exception and sends a personalized message about it.
HandlerMapping class:
#Provider
public class HandlerMapper implements ExceptionMapper<InputValidationException>{
#Override
public Response toResponse(InputValidationException exception) {
return Response.status(exception.getStatus().getStatusCode(), exception.getMessage()).build();
}
}
InputValidationException:
public class InputValidationException extends Exception{
private String errorMessage;
private Response.Status status;
#JsonbCreator
public InputValidationException (#JsonbProperty("message") String message, #JsonbProperty("status") Response.Status status) {
this.errorMessage = "Invalid param entered: " + message;
this.status = status;
}
.........
}
Now when it throws an exception from the API method it works and a customer message is sent as I would like. But if I send a message with the wrong param (for example with the name null), a custom response error is not created as I do with the api method, but the default message is created with 500 Server Error. How can I make a general class to handle errors in a personalized way?
Client Client
public class Client {
private String surname;
private String name;
private String city_of_birth;
.... another parameters ....
#JsonbCreator
public Cliente(#JsonbProperty("surname") String surname, #JsonbProperty("name") String name, #JsonbProperty("city_of_birth") String city_of_birth) throws InputValidationException {
paramValidation(surname, name, city_of_birth, ......... );
this.surname= surname;
this.name= name;
this.city_of_birth= city_of_birth;
.... another parameters ....
}
private void paramValidation(String surname, String name, String city_of_birth) throws InputValidationException{
if( surname == null || surname.isBlank() ){
throw new InputValidationException("surname", Response.Status.METHOD_NOT_ALLOWED);
}
.... other parameter controls ....
}
}
API class
#Path("homeBanking/client/signup")
public class Registration {
private DaoClient daoC = new DaoClient();
#POST
#Produces(MediaType.APPLICATION_JSON)
public Response createClient(Client client) throws InputValidationException {
daoC.insert(client);
return Response.ok().build();
}
}
I think in your context the solution is creating an Exception Mapper to convert the exception message into a JSON object, following this structure: https://docs.jboss.org/resteasy/docs/3.0.6.Final/userguide/html/ExceptionHandling.html
Using the Spring Framework, you can use the ControllerAdvice to create your own error messages. See this article: https://www.baeldung.com/exception-handling-for-rest-with-spring
I hope I've helped!
Related
This question already has an answer here:
Spring POST receives object with null values
(1 answer)
Closed 3 years ago.
I have an Android app contacting a REST API developed with Spring. The app serializes well an object but the Spring application parses every field as null when they aren't null. What can be wrong? what shall I do?
This is the REST API method signature: cambioPassword(#RequestHeader("Authorization") String header, PasswordChange userData)
Note: both the app and the API uses Gson (I'm not using Jackson as default in the Spring application)
I've tried every kind of constructor and I've changed the visibility of the Java class fields
public class PasswordChange {
private String name;
private String currentPassword; // Ciphered field
private String newPassword; // Ciphered field
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCurrentPassword() {
return currentPassword;
}
public void setCurrentPassword(String currentPassword) {
this.currentPassword= currentPassword;
}
public String getNewPassword() {
return newPassword;
}
public void setNewPassword(String newPassword) {
this.newPassword = newPassword;
}
}
The server should be able to deserialize the PasswordChange class and the fields should not be null as they aren't in the original sent JSON.
Could it be that you are missing the #RequestBody annotation, I expect it to look like:
#PostMapping("/cambio")
cambioPassword(#RequestHeader("Authorization") String header, #RequestBody PasswordChange userData){
...
}
See https://www.baeldung.com/spring-request-response-body
I am trying to handle all Types of exceptions using #ExceptionHandler(Exception.class). But it's not handling all types of exception.
When I am trying to access wrong HTTP method from postman/ browser I am not getting any response blank page is coming.
Can please any one tell me why I am not getting any response or tell me if I am doing something wrong in my code?
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(Exception.class)
public ResponseEntity<ExceptionMessage> handleAllExceptionMethod(Exception ex,WebRequest requset,HttpServletResponse res) {
ExceptionMessage exceptionMessageObj = new ExceptionMessage();
exceptionMessageObj.setStatus(res.getStatus());
exceptionMessageObj.setError(ex.getLocalizedMessage());
exceptionMessageObj.setException(ex.getClass().getCanonicalName());
exceptionMessageObj.setPath(((ServletWebRequest) requset).getRequest().getServletPath());
return new ResponseEntity<ExceptionMessage>(exceptionMessageObj, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
}
Either override ResponseEntityExceptionHandler#handleExceptionInternal()or don't extend ResponseEntityExceptionHandler.
#Order(Ordered.HIGHEST_PRECEDENCE) on a #ControllerAdvice should work before ResponseEntityExceptionHandler is invoked as per this answer which suggests that Spring Framework 4.3.7 is needed.
This will handle the exceptions raised from within the controller method.
If you send a request for which there is no mapping the controller method will not be invoked at all thus the #ExceptionHandler will be obsolete in that case.
Maybe this article on creating custom handlers may help: article
Using RequestMapping you can create different responses for every Http code. In this example I show how to control errors and give a response accordingly.
This is the RestController with the service specification
#RestController
public class User {
#RequestMapping(value="/myapp/user/{id}", method = RequestMethod.GET)
public ResponseEntity<String> getId(#PathVariable int id){
if(id>10)
throw new UserNotFoundException("User not found");
return ResponseEntity.ok("" + id);
}
#ExceptionHandler({UserNotFoundException.class})
public ResponseEntity<ErrorResponse> notFound(UserNotFoundException ex){
return new ResponseEntity<ErrorResponse>(
new ErrorResponse(ex.getMessage(), 404, "The user was not found") , HttpStatus.NOT_FOUND);
}
}
Within the getId method there is a little logic, if the customerId < 10 It should response the Customer Id as part of the body message but an Exception should be thrown when the customer is bigger than 10 in this case the service should response with an ErrorResponse.
public class ErrorResponse {
private String message;
private int code;
private String moreInfo;
public ErrorResponse(String message, int code, String moreInfo) {
super();
this.message = message;
this.code = code;
this.moreInfo = moreInfo;
}
public String getMessage() {
return message;
}
public int getCode() {
return code;
}
public String getMoreInfo() {
return moreInfo;
}
}
And finally I'm using an specific Exception for a "Not Found" error
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
I have a Request Mapping -
#RequestMapping("/fetchErrorMessages")
public #ResponseBody int fetchErrorMessages(#RequestParam("startTime") String startTime,#RequestParam("endTime") String endTime) throws Exception
{
if(SanityChecker.checkDateSanity(startTime)&&SanityChecker.checkDateSanity(endTime))
{
return 0;
}
else
{
throw new NotFoundException("Datetime is invalid");
}
}
If the startTime and endTime are invalid, I want to throw a 500 error but return the exception string in JSON. However, I get a HTML Page instead saying
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Wed Dec 20 10:49:37 IST 2017
There was an unexpected error (type=Internal Server Error, status=500).
Datetime is invalid
I instead wanted to return 500 with a JSON
{"error":"Date time format is invalid"}
How do I go about this?
Suppose you have a custom Exception class NotFoundException and its implementations something like this:
public class NotFoundException extends Exception {
private int errorCode;
private String errorMessage;
public NotFoundException(Throwable throwable) {
super(throwable);
}
public NotFoundException(String msg, Throwable throwable) {
super(msg, throwable);
}
public NotFoundException(String msg) {
super(msg);
}
public NotFoundException(String message, int errorCode) {
super();
this.errorCode = errorCode;
this.errorMessage = message;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public String getErrorMessage() {
return errorMessage;
}
#Override
public String toString() {
return this.errorCode + " : " + this.getErrorMessage();
}
}
Now you want to throw some exception from controller. If you throw a exception then you must catch it from a standard Error Handler class, say for example in spring they provide #ControllerAdvice annotation to apply to make a class Standard Error Handler. When it is applied to a class then this spring component (I mean the class you annotated) can catch any exception thrown from controller. But We need to map exception class with proper method. So we defined a method with your exception NotFoundException handler something like below.
#ControllerAdvice
public class RestErrorHandler {
#ExceptionHandler(NotFoundException.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public Object processValidationError(NotFoundException ex) {
String result = ex.getErrorMessage();
System.out.println("###########"+result);
return ex;
}
}
You want to sent http status to internal server error(500), so here we used #ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR). Since you used Spring-boot so you do not need to make a json string except a simple annotation #ResponseBody can do that for you automagically.
Create a custom exception.
public class SecurityException extends RuntimeException {
private static final long serialVersionUID = -7806029002430564887L;
private String message;
public SecurityException() {
}
public SecurityException(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Create a custom response entity.
public class SecurityResponse {
private String error;
public SecurityResponse() {
}
public SecurityResponse(String error) {
this.error = error;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
}
Create a ControllerAdvice with ExceptionHandler for custom exception, it will handle the custom exception, populate and return the custom response as below.
#ControllerAdvice
public class SecurityControllerAdvice {
#ExceptionHandler(SecurityException.class)
#ResponseBody
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public SecurityResponse handleSecurityException(SecurityException se) {
SecurityResponse response = new SecurityResponse(se.getMessage());
return response;
}
}
Throw the custom exception based on your condition.
throw new SecurityException("Date time format is invalid");
Now run and test you app. E.G. :
you can create NotFoundException class with #ResponseStatus annotation like below:
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class NotFoundException extends RuntimeException {
public NotFoundException() {
}
public NotFoundException(String message) {
super(message);
}
}
Javax has a interface name as ExceptionMapper. Please refer the below code snippet, For every RuntimeException in your application it will map it to a Json Response entity.
public class RuntimeExceptionMapper implements ExceptionMapper <RuntimeException> {
#Override
public Response toResponse(RuntimeException exception) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage(exception.getMessage);
if (exception== null) {
logger.error("Exception Details Not found");
} else {
return Response.status(Status.INTERNAL_SERVER_ERROR)
.entity(errorResponse )
.type(MediaType.APPLICATION_JSON)
.header("trace-id", "1234").build();
}
}
}
This is how I did it in my application:
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
#ControllerAdvice
public class ExceptionHandlingControllerAdvice {
#ExceptionHandler(ExecutionRestrictionViolationException.class)
public ResponseEntity<String> handleExecutionRestrictionViolationException(ExecutionRestrictionViolationException ex) {
return response("Invalid Query", ex.getMessage(), HttpStatus.UNPROCESSABLE_ENTITY);
}
private static String createJson(String message, String reason) {
return "{\"error\" : \"" + message + "\"," +
"\"reason\" : \"" + reason + "\"}";
}
private static ResponseEntity<String> response(String message,
String reason,
HttpStatus httpStatus) {
String json = createJson(message, reason);
return new ResponseEntity<>(json, httpStatus);
}
}
Explanation:
You create a controller Advice, mark it with a special annotation and define just like any other bean (in my case it was a java configuration, but it doesn't really matter)
For each Exception you would like to handle like this - define a handler that will generate a response in a format you want
There is a static method createJson - you can use a different way, it also doesn't matter really.
Now this is only one way to work (its available in more recent spring boot versions) - but there are others:
All the methods I'm aware of (and even more) are listed here.
Spring provides a few ways to do this, some more sensible than others depending on your situation.
(Great tutorial here on several options. https://www.baeldung.com/spring-exceptions-json)
My favorite is this one because I want to send back a proper error message and an appropriate http response without creating a superclass or creating helper methods in a utility class or copying boilerplate everywhere.
If you want to inform the caller that the event caused an error (and in proper JSON), use Spring's ResponseStatusException. It gives you access to the httpReponse object so you can also send back a response other than 'ok'.
It wants an exception as one of it's parameters. For one of my scenarios I wanted to inform the caller that they were trying to register a user that already existed. Typically, looking up a user isn't supposed to throw an exception but in this case I created my own exception and I throw it back to the caller in a ResponseStatusException like so:
#PostMapping("/register")
public ResponseEntity register(#RequestBody AccountUserDto user) {
UserDetails userExists = userDetailsService.loadUserByEmail(user.getEmail());
if (userExists != null) {
UserExistsException exc = new UserExistsException("Error: Email address " + user.getEmail() + " is already in use.");
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST, "User Exists", exc);
}
....(fall through and create user)
In our project we are using rest service(Jersey). In one of the requirement I would like to return a list of missing mandatory parameters back to the client.
Right now I am using exception mapper which can return a single error message,
public class MandatoryParameterMissingException extends RuntimeException {
private static final long serialVersionUID = 1L;
public MandatoryParameterMissingException(String message) {
super(message);
}
public class MandatoryParamMissingExceptionMapper implements ExceptionMapper<MandatoryParameterMissingException> {
#Override
public Response toResponse(MandatoryParameterMissingException ex) {
ErrorMessage errorMessage = new ErrorMessage(ex.getMessage(), 404, "Document source:todo");
return Response.status(Status.NOT_FOUND)
.entity(errorMessage)
.build();
}
private String errorMessage;
private int errorCode;
private String documentation;
public ErrorMessage() {
}
public ErrorMessage(String errorMessage, int errorCode, String documentation) {
super();
this.errorMessage = errorMessage;
this.errorCode = errorCode;
this.documentation = documentation;
}
..getter setters
If I find any missing mandatory params, right now I am doing something like,
if (emailID == null) {
throw new MandatoryParameterMissingException("Email id is missing");
}
Could some one please suggest What is the best way to enhance this to take a list of error messages and pass it back to the client?
For this issue i created a class called ValidationException. A ValidationException contains a list of ValidationError.
There are different kind of ValidationError, for example OutOfRangeError, MandatoryParameterError and whatever you need. A ValidationError always contains specific attributes (all what the client needs to create a meaningful messagetext for the user) => A MandatoryParameterError just the name of the field, an OutOfRangeError the name of the field and the allowed range.
On serverside there are validators (in a common package, so the client could do the validation itself)... the validators are creating ValidationErrors during the validation of your data. If there are validation errors, a ValidationException is thrown. That's it. I can give you some codesnippets...
I have a problem with my REST Controller. If I sent a request with a RequestBody (json) some attributes doesn't arrive the controller, although they was sent and defined at model.
I could find out that it look like an old version of files will be used from the local java web server. As I changed the System.out.println Value at Constructor still the old value was outputed.
public RestController_ApiKey_2_0() {
System.out.println("RestController_ApiKey_2_0 init");
}
I tried the following things bootless:
deleted java web server and did a new installation
cleaned the project and started server again
clean install of project
Does anyone have an idea?
Please provide more code, how do you declare a controller, and what params it can take. Also show a sample request.
Here is an example of a simple controller:
A model
public class CustomRequestBody {
private String fieldA;
private String fieldB;
public String getFieldA() {
return fieldA;
}
public void setFieldA(final String fieldA) {
this.fieldA = fieldA;
}
public String getFieldB() {
return fieldB;
}
public void setFieldB(final String fieldB) {
this.fieldB = fieldB;
}
}
Controller:
#Controller
public class MyController {
#RequestMapping(value = "/some-path", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.ACCEPTED)
public ResponseEntity handleSomePath(#RequestBody final CustomRequestBody body, final HttpServletRequest request) {
// Do the job.
}
And request will be:
HTTP POST http://some.server.com/some-path
{
"fieldA":"first value",
"fieldB":"second value"
}
Read more at Spring documentation here