Alexey Zvolinskiy

About Alexey Zvolinskiy

Alexey is a test developer with solid experience in automation of web-applications using Java, TestNG and Selenium. He is so much into QA that even after work he provides training courses for junior QA engineers.

Spring REST: Exception handling vol. 3

It’s a last article from the series about Spring REST exception handling. Finally this time I will talk about handling of REST exceptions which can occur during a form processing. So in this tutorial you will see everything related to REST, forms and exception handling. What about client side? JQuery will be used to reflect all responses of the REST service.

As in the previous tutorials I will work with the Smartphone application. Now is a good time to announce the main aim of this post – a Smartphone entity need to be validated before creation and editing.

Let’s look on updated Smartphone class:

@Entity
@Table(name="smartphones")
public class Smartphone {

        @Id
        @GeneratedValue
        private Integer id;

        @Length(min=1, max=20)
        private String producer;

        @Length(min=1, max=20)
        private String model;

        @Range(min=1, max=1500)
        private double price;

        /**
         * Method updates already existed {@link Smartphone} object with values from the inputed argument.
         * @param sPhone - Object which contains new Smartphone values.
         * @return {@link Smartphone} object to which this method applied.
         */
        public Smartphone update(Smartphone sPhone) {
                this.producer = sPhone.producer;
                this.model = sPhone.model;
                this.price = sPhone.price;
                return this;
        }

        @Override
        public String toString() {
                return producer+": "+model+" with price "+price;
        }

        //getters and setters are omitted

}

Pay your attention on @Length and @Range annotations. These annotation are a standard way for a bean validation. Using of these annotations become possible after updating of already existed pom.xml file by adding to it following dependencies:

org.hibernate
                        hibernate-validator
                        5.0.1.Final

                        javax.validation
                        validation-api
                        1.1.0.Final

After this I need to update messages.properties file:

Length.smartphone.producer = Length of a Smartphone producer should be from 1 to 20 characters.
Length.smartphone.model = Length of a Smartphone model should be from 1 to 20 characters.
Range.smartphone.price = Price of a Smartphone should be from 1 to 1 500.00 $

Now let’s look on the createSmartphone method in SmartphoneController class:

...
        @RequestMapping(value="/create", method=RequestMethod.POST, 
                        produces = MediaType.APPLICATION_JSON_VALUE,
                        consumes = MediaType.APPLICATION_JSON_VALUE)
        @ResponseBody
        public Smartphone createSmartphone(@RequestBody @Valid Smartphone smartphone) {
                return smartphoneService.create(smartphone);
        }
...

The @Valid annotation applied to method argument. Pay extra attention on absence of BindingResult, it is not required for validation like in a classic Spring MVC application. If some property in the Smartphone entity will have inappropriate value the @Value annotation will throw MethodArgumentNotValidException.

Handling of exceptions during entity creation / editing requires different model for information representation. I mean ErrorInfo class from the previous article. We need to transfer additional information which will contain error field name and certain error message for that field. For this purpose here is a new class:

public class ErrorFormInfo {

        private String url;
        private String message;
        private List< FieldErrorDTO > fieldErrors = new ArrayList< FieldErrorDTO >();

        public ErrorFormInfo() {}

        public ErrorFormInfo(String url, String message) {
                this.url = url;
                this.message = message;
        }

        public ErrorFormInfo(List< FieldErrorDTO > fieldErrors, String url, String message) {
                this.fieldErrors = fieldErrors;
                this.url = url;
                this.message = message;
        }

    //getters and setters are omitted

}

And the second one class is FieldErrorDTO it was used in the code example above:

public class FieldErrorDTO {

        private String fieldName;
        private String fieldError;

        public FieldErrorDTO(String fieldName, String fieldError) {
                this.fieldName = fieldName;
                this.fieldError = fieldError;
        }

        //getters and setters are omitted

}

After error transfer objects were introduced I can continue with Error Handling in @ControllerAdvice class. Here is a code snippet of the RestExceptionProcessor class:

...
        @ExceptionHandler(MethodArgumentNotValidException.class)
        @ResponseStatus(value=HttpStatus.BAD_REQUEST)
        @ResponseBody
        public ErrorFormInfo handleMethodArgumentNotValid(HttpServletRequest req, MethodArgumentNotValidException ex) {

                String errorMessage = localizeErrorMessage("error.bad.arguments");
                String errorURL = req.getRequestURL().toString();

                ErrorFormInfo errorInfo = new ErrorFormInfo(errorURL, errorMessage);

                BindingResult result = ex.getBindingResult();                
                List< FieldError > fieldErrors = result.getFieldErrors();

                errorInfo.getFieldErrors().addAll(populateFieldErrors(fieldErrors));

                return errorInfo;
        }

        /**
         * Method populates {@link List} of {@link FieldErrorDTO} objects. Each list item contains
         * localized error message and name of a form field which caused the exception.
         * Use the {@link #localizeErrorMessage(String) localizeErrorMessage} method.
         * 
         * @param fieldErrorList - {@link List} of {@link FieldError} items
         * @return {@link List} of {@link FieldErrorDTO} items
         */
        public List< FieldErrorDTO > populateFieldErrors(List< FieldError > fieldErrorList) {
                List< FieldErrorDTO > fieldErrors = new ArrayList< FieldErrorDTO >();
                StringBuilder errorMessage = new StringBuilder("");

                for (FieldError fieldError : fieldErrorList) {

                        errorMessage.append(fieldError.getCode()).append(".");
                        errorMessage.append(fieldError.getObjectName()).append(".");
                        errorMessage.append(fieldError.getField());

                        String localizedErrorMsg = localizeErrorMessage(errorMessage.toString());

                        fieldErrors.add(new FieldErrorDTO(fieldError.getField(), localizedErrorMsg));
                        errorMessage.delete(0, errorMessage.capacity());
                }
                return fieldErrors;
        }

        /**
         * Method retrieves appropriate localized error message from the {@link MessageSource}.
         * 
         * @param errorCode - key of the error message
         * @return {@link String} localized error message 
         */
        public String localizeErrorMessage(String errorCode) {
                Locale locale = LocaleContextHolder.getLocale();
                String errorMessage = messageSource.getMessage(errorCode, null, locale);
                return errorMessage;
        }
...

The full version of RestExceptionProcessor class can be found by link in the start of the article. I hope that the code snippet above is self explained. If no, feel free to ask questions in comments.

The last thing which I need to do is to develop client code side:

$(document).ready(function() {

      $('#newSmartphoneForm').submit(function(event) {

              var producer = $('#producer').val();
              var model = $('#model').val();
              var price = $('#price').val();
              var json = { "producer" : producer, "model" : model, "price": price};

        $.ajax({
                url: $("#newSmartphoneForm").attr( "action"),
                data: JSON.stringify(json),
                type: "POST",

                beforeSend: function(xhr) {
                        xhr.setRequestHeader("Accept", "application/json");
                        xhr.setRequestHeader("Content-Type", "application/json");
                        $(".error").remove();
                },
                success: function(smartphone) {
                        var respContent = "";

                                  respContent += "Smartphone was created: [";
                                  respContent += smartphone.producer + " : ";
                                  respContent += smartphone.model + " : " ;
                                  respContent += smartphone.price + "]";

                        $("#sPhoneFromResponse").html(respContent);                   
                },
                error: function(jqXHR, textStatus, errorThrown) {
                        var respBody = $.parseJSON(jqXHR.responseText);
                        var respContent = "";

                        respContent += "";
                        respContent += respBody.message;
                        respContent += "";

                        $("#sPhoneFromResponse").html(respContent);

                        $.each(respBody.fieldErrors, function(index, errEntity) {
                                var tdEl = $("."+errEntity.fieldName+"-info");
                                tdEl.html(""+errEntity.fieldError+"");
                        });
                }
        });

        event.preventDefault();
      });

    });

The full version of client new-phone.jsp file can be found by link in the start of the article.

This is the end, I have to make a demonstration of all stuff which we developed in the article above. So scenario is simple, I’m going to open New Smartphone page and submit a form with invalid data.

sr1

Summary

I hope that my three articles about exception handling in a Spring REST application were useful for you and you have learned something new. These articles highlight just a basic flow of exception handling, all other things you can get only throughout practice on real projects. Thanks for reading of my blog.
 

Reference: Spring REST: Exception handling vol. 3 from our JCG partner Alexey Zvolinskiy at the Fruzenshtein’s notes blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

One Response to "Spring REST: Exception handling vol. 3"

  1. varun says:

    Excellent reference point for young REST developers like me !Thank you

Leave a Reply


+ 3 = eight



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close