2

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 :)

3
  • 2
    if you want to Handle de response easily you can use a trasnlation controller using @ControllerAdvice; this aproach catchs the rumtime exceptions what you want handle and create a custom error response like you want, based in a errorDTO objet with the attributes that you need to show. Commented Nov 17, 2016 at 12:19
  • so should I throw an error in resolveArgument(){} and catch it how you suggested.? Could you provide example please, I'm a bit lost, as is clearly apparent. it looks like spring raps whatever I throw in a 500 error. Commented Nov 17, 2016 at 12:37
  • 1
    To customize the body for an error status you can return a custom java bean in the @ExceptionHandler of the @ControllerAdvice. You can look at this little example I wrote. Commented Nov 17, 2016 at 13:46

3 Answers 3

3

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);
  }
Sign up to request clarification or add additional context in comments.

Comments

2

I be late, This is an simple example of how to catch rumtime error and translate the response in simple way.

TranslationController:

@ControllerAdvice
public class ExceptionTranslator {

private final Logger log = LoggerFactory.getLogger(ExceptionTranslator .class);

@ExceptionHandler(RuntimeException.class)
public ResponseEntity<ErrorDTO> processRuntimeException(HttpServletRequest req, RuntimeException ex) throws Exception {
    ErrorDTO errorDTO;
    ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
    if (responseStatus != null) {
        errorDTO = new ErrorDTO(req.getRequestURI(), responseStatus.value().toString(), ex.getMessage());
    } else {
        errorDTO = new ErrorDTO(req.getRequestURI(), HttpStatus.INTERNAL_SERVER_ERROR.toString(), ex.getMessage());
    }
    log.debug(errorDTO.toString());
    return new ResponseEntity(errorDTO, HttpStatus.valueOf(Integer.valueOf(errorDTO.getCode())));
}
}

DTO class to define attributes to show

public class ErrorDTO implements Serializable {

    private static final long serialVersionUID = 1L;

    private final String uri;
    private final String code;
    private final String description;

    public ErrorDTO(String message) {
        this(null, message, null);
    }

    public ErrorDTO(String uri, String code, String description) {
        this.uri = uri;
        this.code = code;
        this.description = description;
    }

    public String getUri() {
       return uri;
    }

    public String getCode() {
       return code;
    }

    public String getDescription() {
        return description;
    }

    @Override
    public String toString() {
        return "ErrorDTO{" + "uri=" + uri + ", code=" + code + ", description=" + description + '}';
    }
 }

Comments

1

If you need custom MissingServletRequestParameterException handling I would add this to the controller or controller advice:

@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, Object> handleMissingParam(MissingServletRequestParameterException e) {


    Map<String, Object> result = new HashMap();

    result.put("status", Integer.toString(400));

    Map<String, String> error = new HashMap();
    error.put("param", e.getParameterName());

    result.put("error", error);
    return result;
}

You can of course replace usage of Map by java objects. Personally I would advice you to use the original error handling unless you have a real reason for that.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.