Search code examples
javaspringspring-mvcspring-bootspring-annotations

how to return a custom spring error response with custom parameter annotations?


I'm new to spring, so please excuse my ignorance. I'm trying to "return" a custom response when a given method parameter, which is "required", is null. the current response, from spring, is:

{
  "timestamp": 1477060294961,
  "status": 400,
  "error": "Bad Request",
  "exception": "org.springframework.web.bind.MissingServletRequestParameterException",
  "message": "Required String parameter 'bookname' is not present",
  "path": "/getbook"
}

I am trying to get to a point where it "returns":

{
  "status": 400,
  "error": {
    // custom error body
  }
}

I thought a nice way of doing this would be to have a custom "parameter annotation". This would also make the code much more readable and store useful information about this endpoint parameter.

I followed the example given here, but I'm not sure where or how to return the custom response?

So far I have the annotation:

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface customParameter {

String value() default "";
boolean required() default false;
String defaultValue() default ValueConstants.DEFAULT_NONE;
String customInfo() default  "blar blar";
}

the "endpoint" :

  @RequestMapping(value = "/getbook", method = {RequestMethod.POST})
  public ResponseEntity<BookResponse> getBookInfo(
      @customParameter(value = "bookname", required = true, customInfo = "extremely valuable book")
      final String bookname
   ) {
    return new bookRequest(bookname).getResponse;
}

and have an custom resolver :

public class CustomAnnotationResolver implements HandlerMethodArgumentResolver {


  @Override
  public boolean supportsParameter(MethodParameter parameter) {
    return parameter.getParameterAnnotation(customParameter.class) != null;
  }


  @Override
  public Object resolveArgument(MethodParameter methodparameter, ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

    CustomParameter customParameter = methodparameter.getParameterAnnotation(CustomParameter.class);

    String parameter = webRequest.getParameter(CustomParameter.value());

    // todo: do Validation here
    if (customParameter == null) {
      if (Parameter.required()) {
        String customInfo = customParameter.customInfo();
        String body getBody(customInfo);
        new ResponseEntity(body, 400); // so the problem is here!! how do I return this response??  
      }
    }

    return webRequest.getParameter(customParameter.value());

  }
}

I have also "registered" this resolver with the webConfig :

@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

  @Override
  public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    argumentResolvers.add(new CustomAnnotationResolver());
  }
}

any help with this implementation or any other suggestions of how to do this would be fantastic. Thanks all :)


Solution

  • Thank you @growlingchaos, brilliant, this is the solution.

    @ControllerAdvice
    @RestController
    public class customExceptionAdvice {
    
      @ExceptionHandler(Exception.class)
      @ResponseStatus(HttpStatus.BAD_REQUEST)
      public ResponseEntity handleConflict(BadRequestException e, HttpServletResponse response)
          throws IOException {
    
        return new ResponseEntity(e.getErrorBody(), HttpStatus.BAD_REQUEST);
      }