Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

I had used Springframework MVC with JSR-303/349 validations with Spring MVC forms. Those follow the normal pattern that we are all used to. This was my first time writing a RESTful service with Spring MVC that needed to return validation messages. There were a couple of posts on the internet that I used, but none was quite right. This is what I came up with that works.

I have a class that holds a validation message. The ones that I saw on the internet also had a field property. This mimics the way the Spring MVC forms expect the data. It expects a field value and the message value. Since we are writing a service, the field does not provide any connection with the client of the service. This forced us to use custom messages for all of the validations that have the full message that the client would need. Instead of returning “is null”, we returned messages like “Password cannot be null”.

package com.cdi.igs.common.spring.web;

/**
 * Holds a field error.
 * @author norris.shelton
 */
public class FieldError {

    /** The error message for the field. */
    private String message;

    /**
     * Constructor is required to set the object properties.
     * @param message the error message for the field
     */
    public FieldError(String message) {
        this.message = message;
    }

    /** Gets the error message for the field. */
    public String getMessage() {
        return message;
    }
}

The FieldError class was used by the ValidationsMessages class. This was used to create the correct json that we were expecting.

package com.cdi.igs.common.spring.web;

import java.util.ArrayList;
import java.util.List;

/**
 * Contains the controller field validation messages.
 * @author norrisshelton 
 */
public class ValidationMessages {

    /** List of field validation errors. */
    private List<FieldError> fieldErrors = new ArrayList<>();

    /** Gets the list of field validation errors. */
    @SuppressWarnings("UnusedDeclaration")
    public List<FieldError> getFieldErrors() {
        return fieldErrors;
    }

    /** Adds a field validation error. */
    public void addFieldError(String message) {
        FieldError fieldError = new FieldError(message);
        fieldErrors.add(fieldError);
    }
}

The real work is in the exception handler.

  • @ControllerAdvice – this is a new annotation in Spring 3.2. This annotation indicates that this class assists a controller.
  • @ExceptionHandler – indicates that the method handles exceptions of the specified type. MethodArgumentNotValidException exceptions are thrown by validated controller methods that do not handle their validation errors.
  • @ResponseStatus – sets the HTTP status code to be returned for this method once the exception has been handled. In this case, we are returning an HTTP 400 Bad Request.
  • @ResponseBody – indicates that the return object should be bound to the response. In this case, the object will be converted to JSON and returned to the client.
  • package com.cdi.igs.common.spring.web;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.MessageSource;
    import org.springframework.context.i18n.LocaleContextHolder;
    import org.springframework.http.HttpStatus;
    import org.springframework.validation.BindingResult;
    import org.springframework.validation.FieldError;
    import org.springframework.validation.ObjectError;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.ResponseStatus;
    
    import java.util.Locale;
    
    
    /**
     * Global Spring MVC controller exception handler.
     * @author norris.shelton
     */
    @ControllerAdvice
    public class GlobalControllerExceptionHandler {
    
        private MessageSource messageSource;
    
        @Autowired
        public GlobalControllerExceptionHandler(MessageSource messageSource) {
            this.messageSource = messageSource;
        }
    
        /**
         * Handles validation messages for controller methods that don't handle them directly.
         * @param exception the exception that is thrown when validations errors are not handled by the controller method
         * @return an object containing the validation messages
         */
        @ExceptionHandler(MethodArgumentNotValidException.class)
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        @ResponseBody
        public ValidationMessages handleMethodArgumentNotValidException(MethodArgumentNotValidException exception) {
            ValidationMessages validationMessages = new ValidationMessages();
            BindingResult result = exception.getBindingResult();
    
            // process the field validations
            for (FieldError fieldError : result.getFieldErrors()) {
                validationMessages.addFieldError(messageSource.getMessage(fieldError, LocaleContextHolder.getLocale()));
            }
    
            // process the global validations
            for (ObjectError globalError : result.getGlobalErrors()) {
                validationMessages.addFieldError(globalError.getDefaultMessage());
            }
    
            return validationMessages;
        }
    
        /**
         * Resolves localized messages.  Not currently used, but here if we switch to message resource files.
         * @param fieldError the field error object
         * @return the localized error message
         */
        private String resolveLocalizedErrorMessage(FieldError fieldError) {
            Locale currentLocale = LocaleContextHolder.getLocale();
            String localizedErrorMessage = messageSource.getMessage(fieldError, currentLocale);
    
            //If the message was not found, return the most accurate field error code instead.
            //You can remove this check if you prefer to get the default error message.
            if (localizedErrorMessage.equals(fieldError.getDefaultMessage())) {
                String[] fieldErrorCodes = fieldError.getCodes();
                localizedErrorMessage = fieldErrorCodes[0];
            }
    
            return localizedErrorMessage;
        }
    }
    

    December 30th, 2013

    Posted In: Java, json, Spring

    2 Comments

I couldn’t find the hosts file on my mac. I was used to finding the hosts file on a Linux system at /etc/hosts. They are in another location on a Mac 10.9.

/private/etc/hosts

December 20th, 2013

Posted In: Mac, OS/X

Tags: , , , , ,

2 Comments

LinkedIn Auto Publish Powered By : XYZScripts.com