Validations,Errors & Exceptions

  • In real world examples, it is very important to handle errors Correctly and simultaneously provide meaningful error messages to clients.
  • Exceptions may occur due to one of the following reasons
    • Users perform operations that are outside the domain of what we expect.
    • Issues with architecture i.e. server is down or database is down.
    • Our job is to present these issues to the user in a manner in which they can work around the approach.
    • Validations,Error and Exceptions are used to handle these extreme conditions.
      • Perform field validation when data binding.
      • Present validation messages.
      • Resolve exceptions securely and gracefully.
      • Handle exceptions at an application level
    • Users don't always put information in our form as we expect.
      • Sometimes we see characters in place of numbers.
      • Length of characters may be too long
      • Email format may be incorrect.
    • Spring provides validators to handle this and there is also JSR 303 which is a bean validation specification within J2EE.
    • Once we have validated the data we need to see how to display these messages back to user easily.
    • Spring builds this right into the framework.It provides a mechanism to send the error message back to view and display it.
      • Provides consistency as we don't have to reinvent the wheel to make our own approach to handle validation errors.
      • Exceptions need to be handled securely and gracefully.
        • We should not display stack-trace to an attacker which can provide them with more information about our system.
        • We should not display stack trace to user which is of no use to him.
        • When we have a exception we need to show an error message to user possibly directing them to resolve the issue.
    • Spring provides us with an exception handler annotation that can be used to indicate how exception should be handled when a controller throws an exception.
    • There is also a global exception handling approach built into Spring MVC.
      • This approach allows us to use a handler exception resolver to resolve all exceptions thrown from every controller.
      • This approach occurs at application level.
      • To accomplish this we specify a handler exception resolver and we make it a bean in the Spring IOC container.
        • When any of our controller throws an exception, the exception is passed to our handler exception resolver and we are able to resolve the exception usually by displaying a nice message to the user in view.
Validators
  • Validation is done on command objects passed into our controller methods.
  • We ensure that all the information in the command objects is valid
    • Here we will validate Project Object.
  • We use the validator interface to validate the objects.
    • Create a new class in data.validators package called as Project Validator
    • Ensure that this class implements validator interface in "org.springframework.validation" package.
    • The validation interface forces us for implementation of couple of methods present in it.
      • The "supports" method is used to verify the classes that will be validated using the validator class.
        • "supports" method gets passed in a class
        • When Spring receives an argument of a particular type in one of our controller handler methods for example "project object" in this case it will bounce against the list of validators bound to the controller and check invoke support method to check if the validator support this class.
        • Supports method is basically a check to see which class objects the validator supports.
          • We return a boolean true value to ensure the support.
      • If it does it is going to invoke the validate method.
        • The validate method does all the validation work
          • It accepts 2 arguments of type Object and Error class.
            • Object is the object to our controller.
          • Then we code for our validation.
          • To return error we use the object of Error class.
            • We are using "rejectvalue()" function of this class.
              • The first parameter is the name of the field i.e. "name".
              • The second parameter is the code which is used to find error messages within a properties file or some other resource bundle that holds error messages.
                • We will use "project.name" here.
              • The final argument is the default message to display if the error occurs.
    • To ensure that a particular controller uses that validator we add it to a method to the class which is annotated with annotation
      • "@InitBinder"
        • The "@InitBinder" annotation ensures that the method is called when Spring MVC is loading up our controller.
        • This method takes a parameter object of type WebDataBinder
          • This object is used to bind validators using following methods.
            • binder.addValidators(new ProjectValidator());
    • Once our validator is registered in the controller.We need to signal Spring MVC that it needs to invoke a validation for a particular argument.
      • We do this by annotating our method Argument within the controller handler method with the "@Valid" annotation.
        • Now this annotation is not found in Spring Libraries we need to pull the appropriate artifact for valid annotation from pom.xml
        • This is from the javax.validation group and name is validation-api.
      • This would trigger to Spring MVC that this would need to validate the argument.
      • Next we need to provide another argument which collects result of our annotation bound.
        • We can place an Errors object or BindingResult object after model attribute.
        • It should be after the model annotated with @Model attribute.
        • We are using Error Type here.
        • Next we are checking that there are not any errors in the controller.
Changes to Project Controller
      @RequestMapping(value="/add",method=RequestMethod.POST)  
      public String saveProject(@Valid @ModelAttribute Project project,Errors errors,HttpServletRequest request,@RequestParam("name") String name,HttpSession session,Model model)  
      {  
           List<String> errs=new LinkedList<String>();  
           if(errors.hasErrors())  
           {  
                System.out.println("The Project is not Validated");  
                errs.add("Name is too Short");  
                model.addAttribute("errorList",errs);  
                return "project_add";  
           }  
           else  
           {  
                System.out.println("The Project is Validated");  
           }  
           System.out.println(session.getAttribute("token"));  
           System.out.println(request.getParameter("name"));  
           System.out.println(name);  
           System.out.println(project);  
           model.addAttribute("project",project);  
           System.out.println("Invoking saveProject");  
           return "project_add";  
      }  
      @InitBinder  
      public void initBinder(WebDataBinder binder)  
      {  
           binder.addValidators(new ProjectValidator());  
      }  
project_add.jsp
 <%@ page language="java" contentType="text/html; charset=ISO-8859-1"  
   pageEncoding="ISO-8859-1"%>  
 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
 <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>  
 <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>  
 <!DOCTYPE html>  
 <html>  
 <head>  
 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">  
 <title>Project Manager</title>  
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">  
      <link rel="stylesheet" href="<spring:url value="/resources/css/home.css"/>" type="text/css"/>  
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>  
      <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>  
 </head>  
 <body>  
      <c:forEach items="${errorList}" var="error">  
          <c:out value="${error}"/>  
      </c:forEach>  
      <jsp:include page="../views/fragments/header.jsp"></jsp:include>  
      <div class="container">  
           <div class="row">  
                <spring:url value="/project/add" var="formUrl" htmlEscape="true"/>  
                <form:form method="POST" action="${formUrl}" modelAttribute="project">  
                     <div class="form-group">  
                          <label for="project-name">Name</label>  
                          <form:input path="name" id="project-name" cssClass="form-control"/>  
                     </div>  
                     <div class="form-group">  
                          <label for="project_type">Type</label>  
                          <form:select path="type" cssClass="selectpicker" items="${types}"></form:select>  
                     </div>  
                     <div class="form-group">  
                          <label for="sponsor-name">Sponsor Name</label>  
                          <form:input id="sponsor-name" path="sponsor.name" cssClass="form-control"/>  
                     </div>  
                     <div class="form-group">  
                          <label for="sponsor-phone">Sponsor Phone</label>  
                          <form:input id="sponsor-phone" path="sponsor.phone" cssClass="form-control"/>  
                     </div>  
                     <div class="form-group">  
                          <label for="sponsor-email">Sponsor Email</label>  
                          <form:input id="sponsor-email" path="sponsor.email" cssClass="form-control"/>  
                     </div>  
                     <div class="form-group">  
                          <label for="funds">Authorized Funds</label>  
                          <form:input path="authorizedFunds" id="funds" cssClass="form-control"/>  
                     </div>  
                     <div class="form-group">  
                          <label for="hours">Authorized Hours</label>  
                          <form:input path="authorizedHours" id="hours" cssClass="form-control"/>  
                     </div>  
                     <div class="form-group">  
                          <label for="project-name">Description</label>  
                          <form:textarea path="description" id="description" cssClass="form-control" rows="3"/>  
                     </div>  
                     <div class="form-group">  
                          <label for="poc">POC</label>  
                          <form:input id="poc" path="pointsOfContact[0]" cssClass="form-control"/>  
                     </div>  
                     <div class="form-group">  
                          <label for="poc2">POC 2</label>  
                          <form:input id="poc2" path="pointsOfContact[1]" cssClass="form-control"/>  
                     </div>  
                     <div class="form-group">  
                          <label for="poc3">POC 3</label>  
                          <form:input id="poc3" path="pointsOfContact[2]" cssClass="form-control"/>  
                     </div>  
                     <div class="form-group">  
                          <label for="special">Special</label>  
                          <form:checkbox path="special" id="special" />  
                     </div>  
                     <button type="submit" class="btn btn-default">Submit</button>  
                </form:form>  
           </div>  
      </div>  
 </body>  
 </html>  
ProjectValidator.java
 package com.springimplant.mvc.data.validators;  
 import org.springframework.validation.Errors;  
 import org.springframework.validation.Validator;  
 import com.springimplant.mvc.data.entities.Project;  
 public class ProjectValidator implements Validator {  
      @Override  
      public boolean supports(Class<?> clazz) {  
           return Project.class.equals(clazz);  
      }  
      @Override  
      public void validate(Object obj, Errors errors) {  
           Project project=(Project) obj;  
           if(project.getName().length()<5)  
           {  
                errors.rejectValue("name","project.name","The Name is too short");  
           }  
      }  
 }  
Bean Validation
  • Also called as jsr 303 validation
    • It is the bean validation within the java api
    • Spring mvc allows us to integrate jsr 303 into framework
  • We need to add an implementation of the bean validation api.
    • Hibernate provides an implementation of this validation
  • Add "org.hibernate" groups "hibernate-validator" artifact in pom.xml.
  • Now in our entity class we can add validation annotations above our class attributes.
    • For example here we add @NotBlank over description in Project Entity.
      • We can also add our custom message as shown in Snippet below
    • We can check min/max of a number.
    • Check the length of String Field.
    • Pattern validation provides us that value in textbox which matches the regular expression.
  • @valid annotation is also from jsr 303 which indicates Spring that we need to validate our entity.
Changes to Project Entity
      @NotBlank(message="Desription Can't be Blank")  
      private String description;   
Form Errors
  • The errors tag within the form tag helps us display messages to the end user when a validation issue occurs.
  • Whenever there are errors in a page they are automatically bound to a view using the form's error tag present in the form tag library.
  • As we see in the below snippet we have added 
    • <form:errors path="description"/>
    • <form:errors path="name"/>
           <div class="form-group">  
                          <label for="project-name">Name</label>  
                          <form:input path="name" id="project-name" cssClass="form-control"/>  
                          <form:errors path="name"/>  
                     </div>  
                     <div class="form-group">  
                          <label for="project-name">Description</label>  
                          <form:textarea path="description" id="description" cssClass="form-control" rows="3"/>  
                          <form:errors path="description"/>  
                     </div>  

Exception Handler
  • Some mechanism to handle exceptions thrown by application.
  • Showing a stack trace through view may lead to security implications and is also not good for user.
  • Uses @ExceptionHandler annotation to handle any exceptions thrown by Controller Methods.
  • In order to demonstrate we will throw an exception
    • Let us manually throw an exception from Handler Method "/add" in Resource Controller.
    • If we now go to the page we will get an exception with stack trace on page.
    • Let us create a new method in the same controller which will return a String.
      • Annotate this method with a exception handler annotation.
      • In this handler annotation we will provide an array of exceptions or Instance of Exception Class as parameter.
        • This is to judge which type of Exception this method will handle.
      • That's all just place in HttpServletRequest and HttpServletResponse as arguments in method if needed.
    • This approach only applies to the RequestMapping annotated controller methods.
    • We cannot have project controller throw an exception and let Resource Controller handle it.
    • The scope of this controller is limited to the controller in which the method is placed in.
Changes to Resource Controller Class
           @RequestMapping("/add")  
      public String add(Model model)  
      {  
           System.out.println("Invoking add() method");  
           if(1==1)  
           {  
                throw new RuntimeException("There was an error");  
           }  
           return "resource_add";  
      }  
      @ExceptionHandler(Exception.class)  
      public String handleError(HttpServletRequest request)  
      {  
           return "controller_error";  
      }  

Handler Exception Resolver
  1. Spring MVC provides with an handler exception resolver interface.
    1. Using this interface we can handle exceptions from our application at a global level.
  2. We create a separate class called as GlobalExceptionHandlerResolver which implements this interface.
    1. This implements method resolveException
      1. It has 4 parameters
        1. HttpServeletRequest
        2. HttpServeletResponse
        3. objectHandler
        4. Exception
    2. We return a ModelAndView object from this method
    3. This class needs to be annotated with @Component annotation which makes this class into a bean.
  3. Next we need to add the package of this class to be component Scan in Dispatcher Servelet if not already done.
  4. When Spring IOC container starts up it is going to scan this package.
    1. It is going to find our class annotated with Component annotation and it is going to implement it within the IOC container as a handler exception resolver able to resolve any exception thrown by our views.
  5. If there is already a method in class with annotation @ExceptionHandler i.e. exception is handled at more granular level then our global level exception resolver will be overridden.
GlobalHandlerExceptionResolver.java
 package com.springimplant.mvc.resolvers;  
 import javax.servlet.http.HttpServletRequest;  
 import javax.servlet.http.HttpServletResponse;  
 import org.springframework.stereotype.Component;  
 import org.springframework.web.servlet.HandlerExceptionResolver;  
 import org.springframework.web.servlet.ModelAndView;  
 @Component  
 public class GlobalHandlerExceptionResolver implements HandlerExceptionResolver {  
      @Override  
      public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,  
                Exception ex) {  
           ModelAndView mav=new ModelAndView();  
           mav.setViewName("global_error");  
           return mav;  
      }  
 }  
ProjectController.java(updated)
      @RequestMapping(value="/exception")  
      public String exception()  
      {  
           if(1==1)  
           {  
                throw new RuntimeException("There was an Error");  
           }  
           return "project_add";  
      }  
global_error.jsp
 <%@ page language="java" contentType="text/html; charset=ISO-8859-1"  
   pageEncoding="ISO-8859-1"%>  
 <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>  
 <!DOCTYPE html>  
 <html>  
 <head>  
 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">  
 <title>Project Manager</title>  
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">  
      <link rel="stylesheet" href="<spring:url value="/resources/css/home.css"/>" type="text/css"/>  
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>  
      <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>  
 </head>  
 <body>  
      <jsp:include page="../views/fragments/header.jsp"></jsp:include>                 
      <div class="container">  
           <h2>An error was encountered and handled by a global ExceptionHandlerResovler.</h2>  
      </div>  
 </body>  
 </html>  
DispatcherServlet.xml(updated)
      <context:component-scan base-package="com.springimplant.mvc.resolvers"/> 
Controller Advice
  • @RestController
    • Is the base annotation for classes that handle the rest operations.
  • @ControllerAdvice 
    • is used to handle exception globally, it allows you to use the same exception handler for multiple controllers.
    • This way we can define how to treat an exception in just one place because this handle will be called when the exception is thrown from classes that are covered by controller advice.
    • As the name suggest it is an “Advice” for Multiple controllers.
    • It allows our class to be a global interceptor of exceptions, thrown by methods, annotated by @RequestMapping
  • @ExceptionHandler
    • Spring annotation that provides a mechanism to treat exceptions that are thrown during execution of handlers(Controller operations).
    • This annotation if used on methods of controller classes Will serve as an entry entry point for handling exceptions, thrown within the controller only.
    • If used on methods of @ControllerAdvice advice Classes will be applied globally to a subset of controllers.
  • @ExceptionHandler and @ControllerAdvice areused to define central point for treating exception and wrapping them up in a class
  • @ResponseStatus handles our error responses which are always giving us http status 500 instead of a more descriptive status code.
    • To address this, we annotate our exception with @ResponseStatus and pass in desired HttpResponse status.
  • We can also override the existing exception handler with spring boot’s Built in exception, class ResponseEntityExceptionHandler.
HandlerExceptionResolver
  • Response Status Exception Resolver
    • @ResponseStatus
      • Used in restful methods.
      • Used for giving a response status when a certain exception is thrown
    • Error Handling
      • @Exception Handler 
      • @Controller Advice
      • Annotate on customised exception class
  • Exception handler
    • Exception resolver
      • @ExceptionHandler

No comments:

Post a Comment

Spring Boot

What is circular/cyclic dependency in spring boot? When two services are interdependent on each other, that is to start one service, we requ...