title: Handling InvalidPropertyException with @ModelAttribute
body: <p>We are trying to resolve issues related to a security scan.  It is considered a vulnerability to expose any information about underlying classes.  The scanner is sending invalid data to this endpoint:</p>
  
  <pre><code>@PostMapping(value = "/accountKey", params = "update")
  public String accountKeyUpdate(@Valid @ModelAttribute("accountKeyForm") AccountKeyForm key, BindingResult bindingResult, Authentication authentication)
  </code></pre>
  
  <p>The invalid input looks like this, where "description" is a valid key in the entity, but adding "[]" to the end of the property name in the POST data is causing the parsing error:</p>
  
  <pre><code>description[]:
  </code></pre>
  
  <p>The server returns the following:</p>
  
  <pre><code>{
      "timestamp": "2018-04-20T14:28:36.653Z",
      "status": 500,
      "error": "Internal Server Error",
      "message": "Invalid property 'description[]' of bean class 
  [com.imsweb.seerapi.account.AccountKeyForm]: Property referenced in indexed property path 'description[]' is neither an array nor a List nor a Map; returned value was []",
  "path": "/accountKey/"
  }
  </code></pre>
  
  <p>This is what appears in the log:</p>
  
  <pre><code>org.springframework.beans.InvalidPropertyException: Invalid property 'description[]' of bean class [com.imsweb.seerapi.account.AccountKeyForm]: Property referenced in indexed property path 'description[]' is neither an array nor a List nor a Map; returned value was []
      at org.springframework.beans.AbstractNestablePropertyAccessor.processKeyedProperty(AbstractNestablePropertyAccessor.java:375) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:275) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:266) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:97) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:839) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.validation.DataBinder.doBind(DataBinder.java:735) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.bind.WebDataBinder.doBind(WebDataBinder.java:197) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.bind.ServletRequestDataBinder.bind(ServletRequestDataBinder.java:107) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.bindRequestParameters(ServletModelAttributeMethodProcessor.java:157) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:153) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:124) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:131) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
      at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877) ~[spring-webmvc-5.0.5.RELEASE.jar:5.0.5.RELEASE]
  </code></pre>
  
  <p>The issue is that I cannot find a way to gracefully handle the invalid input.  It looks like it happens when the @ModelAttribute is converting the POST body into an AccountKeyForm.  That is before it gets inside the controller method.  I would prefer to handle the error and just forward them to another page.  Alternatively if the message said</p>
  
  <pre><code>"message": "Invalid property 'description[]'"
  </code></pre>
  
  <p>That would be fine as well.</p>
  
  <p>UPDATE:</p>
  
  <p>I can trap that specific exception using an @ExceptionHandler:</p>
  
  <pre><code>@ControllerAdvice
  public class WebControllerAdvice {
  
      @ExceptionHandler(InvalidPropertyException.class)
      public String handleBadPropertyException() {
          return "error";
      }
  }
  </code></pre>
  
  <p>That means I will just get a generic message.  That will not pick up other types of exceptions that may fall through the cracks.  Is there a better way?</p>
  
  <p>UPDATE:</p>
  
  <p>Here is the entity class.  It is a simple bean with two properties.</p>
  
  <pre><code>public class AccountKeyForm {
  
      private String _apiKey;
      private String _description;
  
      public AccountKeyForm() {
      }
  
      public AccountKeyForm(String apiKey) {
          _apiKey = apiKey;
      }
  
      public AccountKeyForm(String apiKey, String description) {
          _apiKey = apiKey;
          _description = description;
      }
  
      public String getApiKey() {
          return _apiKey;
      }
  
      public void setApiKey(String apiKey) {
          _apiKey = apiKey;
      }
  
      @Size(max = 256)
      public String getDescription() {
          return _description;
      }
  
      public void setDescription(String description) {
          _description = description;
      }
  }
  </code></pre>