© Balaji Varanasi and Maxim Bartkov 2022
B. Varanasi, M. BartkovSpring RESThttps://doi.org/10.1007/978-1-4842-7477-4_5

5. Error Handling

Balaji Varanasi1   and Maxim Bartkov2
(1)
Salt Lake City, UT, USA
(2)
Kharkov, Ukraine
 
In this chapter we will discuss the following:
  • Handling errors in a REST API

  • Designing meaningful error responses

  • Validating API inputs

  • Externalizing error messages

Error handling is one of the most important yet somewhat overlooked topics for programmers. Although we develop software with good intent, things do go wrong, and we must be prepared to handle and communicate those errors gracefully. The communication aspect is especially important to developers consuming a REST API. Well-designed error responses allow consuming developers to understand the issues and help them to use the API correctly. Additionally, good error handling allows API developers to log information that can aid in debugging issues on their end.

QuickPoll Error Handling

In our QuickPoll application, consider the scenario in which a user tries to retrieve a poll that doesn’t exist. Figure 5-1 shows the Postman request for a nonexistent poll with id 100.
../images/332520_2_En_5_Chapter/332520_2_En_5_Fig1_HTML.png
Figure 5-1

Requesting a nonexistent poll

On receiving the request, the PollController in our QuickPoll application uses PollRepository to retrieve the poll. Because a poll with id 100 doesn’t exist, PollRepository’s findById method returns an empty Option and the PollController sends an empty body to the client, as shown in Figure 5-2.
../images/332520_2_En_5_Chapter/332520_2_En_5_Fig2_HTML.jpg
Figure 5-2

Response to a nonexistent poll

Note

In this chapter, we will continue working on the QuickPoll application that we built in the previous chapter. The code is also available under the Chapter5starter folder of the downloaded source code. The completed solution is available under the Chapter5final folder. As we have omitted getter/setter methods and imports in some of the listings in this chapter, please refer to the code under the final folder for complete listings. The Chapter5 folder also contains an exported Postman collection containing REST API requests associated with this chapter.

This current implementation is deceptive, as the client receives a status code 200. Instead, a status code 404 should be returned, indicating that the requested resource doesn’t exist. To achieve this correct behavior, we will validate the poll id in the com.apress.controller.PollController’s getPoll method and, for nonexistent polls, throw a com.apress.exception.ResourceNotFoundException exception. Listing 5-1 shows the modified getPoll implementation .
@GetMapping("/polls/{pollId}")
public ResponseEntity<?> getPoll(@PathVariable Long pollId) {
        Optional<Poll> poll = pollRepository.findById(pollId);
        if(!poll.isPresent()) {
                throw new ResourceNotFoundException("Poll with id " + pollId + " not found");
        }
        return new ResponseEntity<>(poll.get(), HttpStatus.OK);
}
Listing 5-1

getPoll Implementation

The ResourceNotFoundException is a custom exception, and its implementation is shown in Listing 5-2. Notice that an @ResponseStatus annotation is declared at the class level. The annotation instructs Spring MVC that an HttpStatus NOT_FOUND (404 code) should be used in the response when a ResourceNotFoundException is thrown.
package com.apress.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
        private static final long serialVersionUID = 1L;
        public ResourceNotFoundException() {}
        public ResourceNotFoundException(String message) {
                super(message);
        }
        public ResourceNotFoundException(String message, Throwable cause) {
                super(message, cause);
        }
}
Listing 5-2

ResourceNotFoundException Implementation

With these modifications in place, start the QuickPoll application, and run the Postman request for poll with ID 100. The PollController returns the right status code as shown in Figure 5-3.
../images/332520_2_En_5_Chapter/332520_2_En_5_Fig3_HTML.png
Figure 5-3

New response for a nonexistent poll

In addition to GET, other HTTP methods such as PUT, DELETE, and PATCH act on existing Poll resources. Hence, we need to perform the same poll ID validation in the corresponding methods so that we return the right status code to the client. Listing 5-3 shows the poll id verification logic encapsulated into a PollController’s verifyPoll method along with the modified getPoll, updatePoll , and deletePoll methods.
protected Poll verifyPoll(Long pollId) throws ResourceNotFoundException {
        Optional<Poll> poll = pollRepository.findById(pollId);
        if(!poll.isPresent()) {
                throw new ResourceNotFoundException("Poll with id " + pollId + " not found");
        }
        return poll.get();
}
@GetMapping("/polls/{pollId}")
public ResponseEntity<?> getPoll(@PathVariable Long pollId) {
        return new ResponseEntity<>(verifyPoll(pollId), HttpStatus.OK);
}
@PutMapping("/polls/{pollId}")
public ResponseEntity<?> updatePoll(@RequestBody Poll poll, @PathVariable Long pollId) {
        verifyPoll(pollId);
        pollRepository.save(poll);
        return new ResponseEntity<>(HttpStatus.OK);
}
@DeleteMapping("/polls/{pollId}")
public ResponseEntity<?> deletePoll(@PathVariable Long pollId) {
        pollRepository.deleteById(pollId);
        pollRepository.delete(pollId);
        return new ResponseEntity<>(HttpStatus.OK);
}
Listing 5-3

Updated PollController

Error Responses

HTTP status codes play an important role in REST APIs. API developers should strive to return the right codes indicating the request status. Additionally, it is good practice to provide helpful, fine-grained details regarding the error in the response body. These details will enable API consumers to troubleshoot issues easily and help them to recover. As you can see in Figure 5-3, Spring Boot follows this practice and includes the following details in error response bodies:
  • Timestamp—The time in milliseconds when the error happened.

  • Status—HTTP status code associated with the error; this is partly redundant as it is same as the response status code.

  • Error—The description associated with the status code.

  • Exception—The fully qualified path to the exception class resulting in this error.

  • Message—The message providing more details about the error.

  • Path—The URI that resulted in the exception.

These details are generated by the Spring Boot framework. This feature is not available out of the box in non-Boot Spring MVC applications. In this section, we will implement a similar error response for a QuickPoll application using generic Spring MVC components so that it works in both Boot and non-Boot environments. Before we dive into this implementation, let’s look at the error response details of two popular applications: GitHub and Twilio. Figure 5-4 shows GitHub’s error response details for a request containing invalid inputs. The message attribute gives a plain description of the error, and the error attribute lists the fields with invalid inputs. In this example, the client’s request is missing the Issue resource’s title field.
../images/332520_2_En_5_Chapter/332520_2_En_5_Fig4_HTML.jpg
Figure 5-4

GitHub error response

Twilio provides an API that allows developers programmatically make phone calls, send texts, and receive texts. Figure 5-5 shows the error response for a POST call that is missing a “To” phone number. The status and message fields are similar to fields in Spring Boot’s response. The code field contains a numeric code that can be used to find more information about the exception. The more_info field contains the URL for error code documentation. On receiving this error, a Twilio API consumer can navigate to https://www.twilio.com/docs/errors/21201 and get more information to troubleshoot the error.
../images/332520_2_En_5_Chapter/332520_2_En_5_Fig5_HTML.jpg
Figure 5-5

Twilio error response

It is clear that there is not a standard response format for errors. It is up to the API and framework implementers to decide on the details to be sent to the client. However, attempts to standardize the response format have begun, and an IETF specification known as Problem Details for HTTP APIs (http://tools.ietf.org/html/draft-nottingham-http-problem-06) is gaining traction. Inspired by the “Problem Details for HTTP APIs” specification, Listing 5-4 shows the error response format that we will be implementing in our QuickPoll application.
{
        "title" : "",
        "status" : "",
        "detail" : ",
        "timestamp" : "",
        "developerMessage: "",
        "errors": {}
}
Listing 5-4

QuickPoll Error Response Format

Here is a brief description of the fields in the QuickPoll error response:
  • Title—The title field provides a brief title for the error condition. For example, errors resulting as a result of input validation will have the title “Validation Failure.” Similarly, an “Internal Server Error” will be used for internal server errors.

  • Status—The status field contains the HTTP status code for the current request. Even though it is redundant to include status code in the response body, it allows API clients to look for all the information that it needs to troubleshoot in one place.

  • Detail—The detail field contains a short description of the error. The information in this field is typically human readable and can be presented to an end user.

  • Timestamp—The time in milliseconds when the error occurred.

  • developerMessage—The developerMessage contains information such as exception class name or stack trace that is relevant to developers.

  • Errors—The error field is used to report field validation errors.

Now that we have our error response defined, we are ready to modify QuickPoll application. We begin by creating a Java representation of the response details, as shown in Listing 5-5. As you can see, the ErrorDetail class is missing the errors field. We will be adding that functionality in the upcoming section.
package com.apress.dto.error;
public class ErrorDetail {
        private String title;
        private int status;
        private String detail;
        private long timeStamp;
        private String developerMessage;
        // Getters and Setters omitted for brevity
}
Listing 5-5

Error Response Details Representation

Error handling is a crosscutting concern. We need an application-wide strategy that handles all of the errors in the same way and writes the associated details to the response body. As we discussed in Chapter 2, classes annotated with @ControllerAdvice can be used to implement such crosscutting concerns. Listing 5-6 shows the RestExceptionHandler class with an aptly named handleResourceNotFoundException method. Thanks to the @ExceptionHandler annotation, any time a ResourceNotFoundException is thrown by a controller, Spring MVC would invoke the RestExceptionHandler’s handleResourceNotFoundException method. Inside this method, we create an instance of ErrorDetail and populate it with error information.
package com.apress.handler;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import com.apress.dto.error.ErrorDetail;
import com.apress.exception.ResourceNotFoundException;
@ControllerAdvice
public class RestExceptionHandler {
        @ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<?> handleResourceNotFoundException(ResourceNotFoundException rnfe, HttpServletRequest request) {
                ErrorDetail errorDetail = new ErrorDetail();
                errorDetail.setTimeStamp(new Date().getTime());
                errorDetail.setStatus(HttpStatus.NOT_FOUND.value());
                errorDetail.setTitle("Resource Not Found");
                errorDetail.setDetail(rnfe.getMessage());
                errorDetail.setDeveloperMessage(rnfe.getClass().getName());
                return new ResponseEntity<>(errorDetail, null, HttpStatus.NOT_FOUND);
        }
}
Listing 5-6

RestExceptionHandler Implementation

To verify that our newly created handler works as expected, restart the QuickPoll application and submit a Postman request to a nonexistent poll with id 100. You should see an error response as shown in Figure 5-6.
../images/332520_2_En_5_Chapter/332520_2_En_5_Fig6_HTML.png
Figure 5-6

ResourceNotFoundException error response

Input Field Validation

As a famous proverb goes, “garbage in, garbage out”; input field validation should be another area of emphasis in every application. Consider the scenario in which a client requests a new poll to be created but doesn’t include the poll question in the request. Figure 5-7 shows a Postman request with a missing question and the corresponding response. Make sure that you set the Content-Type header to “application/json” before firing the Postman request. From the response, you can see that the poll still gets created. Creating a poll with a missing question can result in data inconsistencies and other bugs.
../images/332520_2_En_5_Chapter/332520_2_En_5_Fig7_HTML.png
Figure 5-7

Creating a poll with a missing question

Spring MVC provides two options for validating user input. In the first option, we create a validator that implements the org.springframework.validation.Validator interface. Then we inject this validator into a controller and invoke validator’s validate method manually to perform validation. The second option is to use the JSR 303 validation, an API intended to simplify field validation in any layer of the application. Considering the simplicity and the declarative nature of the framework, we will be using JSR 303 validation framework in this book.

You can read more about JSR 303 at https://beanvalidation.org/1.0/spec.

The JSR 303 and JSR 349 define specifications for the Bean Validation API (version 1.0 and 1.1, respectively). They provide a metadata model for JavaBean validation via a set of standardized validation constraints. Using this API, you annotate domain object properties with validation constraints such as @NotNull and @Email. Implementing frameworks enforce these constraints at runtime. In this book, we will be using Hibernate Validator, a popular JSR 303/349 implementation framework. Table 5-1 shows some of the out-of-the-box validation constraints available with Bean Validation API. Additionally, it is possible to define your own custom constraints.
Table 5-1

Bean Validation API Constraints

Constraint

Description

NotNull

Annotated field must not have null value.

Null

Annotated field must be null.

Max

Annotated field value must be an integer value lower than or equal to the number specified in the annotation.

Min

Annotated field value must be an integer value greater than or equal to the number specified in the annotation.

Past

Annotated field must be a date in the past.

Future

Annotated field must be a date in the future.

Size

Annotated field must match the min and max boundaries specified in the annotation.

For a field that is a Collection, the size of the Collection is matched against boundaries.

For a String field, the length of the string is verified against boundaries.

Pattern

Annotated field must match the regular expression specified in the annotation.

To add validation capabilities to QuickPoll, we start by annotating the Poll class as shown in Listing 5-7. Because we want to make sure that each Poll has a question, we annotated the question field with an @NotEmpty annotation. The javax.validation.constraints.NotEmpty annotation is not part of JSR 303/349 API. Instead, it is part of Hibernate Validator; it ensures that the input string is not null and its length is greater than zero. Also, to make the experience of taking a poll simpler, we will restrict each poll to contain no fewer than two and no more than six options.
@Entity
public class Poll {
        @Id
        @GeneratedValue
        @Column(name="POLL_ID")
        private Long id;
        @Column(name="QUESTION")
        @NotEmpty
        private String question;
        @OneToMany(cascade=CascadeType.ALL)
        @JoinColumn(name="POLL_ID")
        @OrderBy
        @Size(min=2, max = 6)
        private Set<Option> options;
        // Getters and Setters removed for brevity
}
Listing 5-7

Poll Class Annotated with JSR 303 Annotations

We now move our attention to the com.apress.controller.PollController and add an @Valid annotation to the createPoll method’s Poll parameter, as shown in Listing 5-8. The @Valid annotation instructs Spring to perform data validation after binding the user-submitted data. Spring delegates the actual validation to a registered Validator. With Spring Boot adding JSR 303/JSR 349 and Hibernate Validator jars to the classpath, the JSR 303/JSR 349 is enabled automatically and will be used to perform the validation.
@GetMapping(value="/polls")
public ResponseEntity<?> createPoll(@Valid @RequestBody Poll poll) {
        poll = pollRepository.save(poll);
        // Set the location header for the newly created resource
        HttpHeaders responseHeaders = new HttpHeaders();
        URI newPollUri = ServletUriComponentsBuilder
                             .fromCurrentRequest()
                             .path("/{id}")
                             .buildAndExpand(poll.getId())
                             .toUri();
        responseHeaders.setLocation(newPollUri);
        return new ResponseEntity<>(null, responseHeaders, HttpStatus.CREATED);
}
Listing 5-8

PollController Annotated with @Valid Annotations

On repeating the Postman request with a missing question as we did in Figure 5-7, you will see the operation fail with an error code 400, as shown in Figure 5-8. From the error response, notice that Spring MVC completed validating the input. On not finding the required question field, it threw a MethodArgumentNotValidException exception.
../images/332520_2_En_5_Chapter/332520_2_En_5_Fig8_HTML.png
Figure 5-8

Missing question resulting in error

Even though Spring Boot’s error message is helpful, to be consistent with our QuickPoll error response that we designed in Listing 5-4, we will modify the RestExceptionHandler so that we can intercept a MethodArgumentNotValidException exception and return an appropriate ErrorDetail instance. While we were designing the QuickPoll error response, we came up with an error field that can hold our validation errors. It is possible for a field to have one or more validation errors associated with it. For example, a missing question field in our Poll example would result in a “Field may not be null” validation error. In the same way, an empty email address could result in “Field may not be null” and “Field is not a well-formed email” validation errors. Keeping these validation constraints in mind, Listing 5-9 shows a complete error response with the validation error examples. The error object contains an unordered collection of key-value error instances. The error key represents the name of the resource feed that has validation errors. The error value is an array representing the validation error details. From Listing 5-9, we can see that field1 contains one validation error and field2 is associated with two validation errors. Each validation error itself is made up of code that represents the violated constraint and a message containing a human-readable error representation.
{
        "title" : "",
        "status" : "",
        "detail" : ",
        "timestamp" : "",
        "path" : "",
        "developerMessage: "",
        "errors": {
                "field1" : [ {
                                "code" : "NotNull",
                                message" : "Field1 may not be null"
                        } ],
                "field2" : [ {
                                "code" : "NotNull",
                                "message" : "Field2 may not be null"
                        },
                        {
                                "code" : "Email",
                                "message" : "Field2 is not a well formed email"
                        }]
                }
}
Listing 5-9

Validation Error Format

To represent the newly added validation error feature in the Java code, we created a new com.apress.dto.error.ValidationError class. Listing 5-10 shows the ValidationError class and updated ErrorDetail class . In order to generate the error response format shown in Listing 5-9, the error field in ErrorDetail class is defined as a Map that accepts String instances as keys and List of ValidationError instances as values.
package com.apress.dto.error;
public class ValidationError {
        private String code;
        private String message;
        // Getters and Setters removed for brevity
}
public class ErrorDetail {
        private String title;
        private int status;
        private String detail;
        private long timeStamp;
        private String path;
        private String developerMessage;
private Map<String, List<ValidationError>> errors = new HashMap<String, List<ValidationError>>();
        // Getters and setters removed for brevity
}
Listing 5-10

ValidationError and Updated ErrorDetail Classes

The next step is to modify the RestExceptionHandler by adding a method that intercepts and processes the MethodArgumentNotValidException exception. Listing 5-11 shows the handleValidationError method implementation in RestExceptionHandler. We begin the method implementation by creating an instance of ErrorDetail and populating it. Then we use the passed-in exception parameter to obtain all the field errors and loop through the list. We created an instance of ValidationError for each field error and populated it with code and message information.
@ControllerAdvice
public class RestExceptionHandler {
        @ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleValidationError(MethodArgumentNotValidException manve, HttpServletRequest request) {
                ErrorDetail errorDetail = new ErrorDetail();
                // Populate errorDetail instance
                errorDetail.setTimeStamp(new Date().getTime());
                errorDetail.setStatus(HttpStatus.BAD_REQUEST.value());
String requestPath = (String) request.getAttribute("javax.servlet.error.request_uri");
                if(requestPath == null) {
                        requestPath = request.getRequestURI();
                }
                errorDetail.setTitle("Validation Failed");
                errorDetail.setDetail("Input validation failed");
                errorDetail.setDeveloperMessage(manve.getClass().getName());
                // Create ValidationError instances
                List<FieldError> fieldErrors =  manve.getBindingResult().getFieldErrors();
                for(FieldError fe : fieldErrors) {
List<ValidationError> validationErrorList = errorDetail.getErrors().get(fe.getField());
                        if(validationErrorList == null) {
                                validationErrorList = new ArrayList<ValidationError>();
errorDetail.getErrors().put(fe.getField(), validationErrorList);
                        }
                        ValidationError validationError = new ValidationError();
                        validationError.setCode(fe.getCode());
                        validationError.setMessage(fe.getDefaultMessage());
                        validationErrorList.add(validationError);
                }
                return new ResponseEntity<>(errorDetail, null, HttpStatus. BAD_REQUEST);
                   }
        /** handleResourceNotFoundException method removed **/
}
Listing 5-11

handleValidationError Implementation

With this implementation in place, restart the QuickPoll application and submit a Poll with missing question. This will result in a status code of 400 with our custom error response, as shown in Figure 5-9.
../images/332520_2_En_5_Chapter/332520_2_En_5_Fig9_HTML.png
Figure 5-9

Validation error response

Externalizing Error Messages

We have made quite a bit of progress with our input validation and provided the client with descriptive error messages that can help them troubleshoot and recover from those errors. However, the actual validation error message may not be very descriptive and API developers might want to change it. It would be even better if they were able to pull this message from an external properties file. The property file approach not only simplifies Java code but also makes it easy to swap the messages without making code changes. It also sets the stage for future internationalization/localization needs. To achieve this, create a messages.properties file under the srcmain esources folder, and add the following two messages:
NotEmpty.poll.question=Question is a required field
Size.poll.options=Options must be greater than {2} and less than {1}

As you can see, we are following the convention <<Constraint_Name>>.model_name.field_Name for each key of the message. The model_name represents name of the Spring MVC’s model object to which user-submitted data is being bound. The name is typically provided using the @ModelAttribute annotation. In the scenarios in which this annotation is missing, the model name is derived using the parameter’s nonqualified class name. The PollController’s createPoll method takes a com.apress.domain.Poll instance as its model object. Hence, in this case, the model name will be derived as poll. If a controller were to take an instance of com.apress.domain.SomeObject as its parameter, the derived model name will be someObject. It is important to remember that Spring will not use the name of the method parameter as the model name.

The next step is to read the properties from the file and use them during the ValidationError instance creation. We do that by injecting an instance of MessageSource into the RestExceptionHandler class. Spring’s MessageSource provides an abstraction to easily resolve messages. Listing 5-12 shows the modified source code for handleValidationError . Notice that we are using MessageResource's getMessage method to retrieve messages.
@ControllerAdvice
public class RestExceptionHandler {
        @Inject
        private MessageSource messageSource;
        @ExceptionHandler(MethodArgumentNotValidException.class)
        @ResponseStatus(HttpStatus.BAD_REQUEST)
public @ResponseBody ErrorDetail handleValidationError(MethodArgumentNotValidException manve, HttpServletRequest request) {
                ErrorDetail errorDetail = new ErrorDetail();
                // Populate errorDetail instance
                errorDetail.setTimeStamp(new Date().getTime());
                errorDetail.setStatus(HttpStatus.BAD_REQUEST.value());
String requestPath = (String) request.getAttribute("javax.servlet.error.request_uri");
                if(requestPath == null) {
                        requestPath = request.getRequestURI();
                }
                errorDetail.setTitle("Validation Failed");
                errorDetail.setDetail("Input validation failed");
                errorDetail.setDeveloperMessage(manve.getClass().getName());
                // Create ValidationError instances
                List<FieldError> fieldErrors =  manve.getBindingResult().getFieldErrors();
                for(FieldError fe : fieldErrors) {
List<ValidationError> validationErrorList = errorDetail.getErrors().get(fe.getField());
                        if(validationErrorList == null) {
                                validationErrorList = new ArrayList<ValidationError>();
errorDetail.getErrors().put(fe.getField(), validationErrorList);
                        }
                        ValidationError validationError = new ValidationError();
                        validationError.setCode(fe.getCode());
                        validationError.setMessage(messageSource.getMessage(fe, null));
                        validationErrorList.add(validationError);
                }
                return errorDetail;
        }
}
Listing 5-12

Reading Messages from Properties File

Restarting the QuickPoll application and submitting a poll with a missing question would result in the new validation error message as shown in Figure 5-10.
../images/332520_2_En_5_Chapter/332520_2_En_5_Fig10_HTML.png
Figure 5-10

New validation error message

Improving RestExceptionHandler

By default, Spring MVC handles error scenarios such as not being able to read a malformed request or not finding a required request parameter by throwing a set of standard exceptions. However, Spring MVC doesn’t write these standard exception details to the response body. To keep things consistent for our QuickPoll clients, it is important that Spring MVC standard exceptions are also handled in the same way and that we return the same error response format. A straightforward approach is to create a handler method for each exception in our RestExceptionHandler. A simpler approach is to have RestExceptionHandler class extend Spring’s ResponseEntityExceptionHandler. The ResponseEntityExceptionHandler class contains a set of protected methods that handle standard exception and return a ResponseEntity instance containing error details.

Extending the ResponseEntityExceptionHandler class allows us to override the protected method associated with the exception and return an ErrorDetail instance. Listing 5-13 shows a modified RestExceptionHandler that overrides handleHttpMessageNotReadable method. The method implementation follows the same pattern that we used before—create and populate an instance of ErrorDetail. Because the ResponseEntityExceptionHandler already comes with a handler method for MethodArgumentNotValidException, we have moved the handleValidationError method code to an overridden handleMethodArgumentNotValid method.
@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler  {
        @Override
        protected ResponseEntity<Object> handleHttpMessageNotReadable(
                        HttpMessageNotReadableException ex, HttpHeaders headers,
                        HttpStatus status, WebRequest request) {
                ErrorDetail errorDetail = new ErrorDetail();
                errorDetail.setTimeStamp(new Date().getTime());
                errorDetail.setStatus(status.value());
                errorDetail.setTitle("Message Not Readable");
                errorDetail.setDetail(ex.getMessage());
                errorDetail.setDeveloperMessage(ex.getClass().getName());
                return handleExceptionInternal(ex, errorDetail, headers, status, request);
        }
        @Override
public ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNot
ValidException manve, HttpHeaders headers, HttpStatus status, WebRequest request) {
                     // implementation removed for brevity
              return handleExceptionInternal(manve, errorDetail, headers, status, request);
        }
}
Listing 5-13

RestExceptionHandler Handling Malformed Messages

Let’s quickly verify our implementation by submitting a nonreadable message (such as removing a “,” from the JSON request body) using Postman. You should see a response as shown in Figure 5-11.
../images/332520_2_En_5_Chapter/332520_2_En_5_Fig11_HTML.png
Figure 5-11

Not Readable message error

Summary

In this chapter, we designed and implemented an error response format for Spring MVC–based REST applications. We also looked at validating user input and returning error messages that are meaningful to API consumers. In the next chapter, we will look at strategies for documenting REST services using the Swagger framework.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.144.229