Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java/Spring > Handle Bad Request response for controller method with @RequestBody when no body is sent in request

long story short: I'm creating API that is supposed to be 100% REST. I'm trying to overwrite default response for the following case: I've got a method in my @RestController that has @RequestBody as an attribute

@RequestMapping(value = {"register"}, method = RequestMethod.POST, produces = "application/hal+json")
public Resource<User> registerClient(@RequestBody User user, HttpServletRequest request)

and the method is working just fine if I send a proper request. But there is a problem when I don't. When a request has empty body, I get a generic Tomcat error page for status 400 and I need it to send just a string or a JSON object instead.

So far I tried to add Exception Handlers in my RestControllerAdvice for all Spring exceptions from package org.springframework.web.binding, but it didn't work either.

I'm already aware that for some security-related errors one have to create handlers in configuration, but I don't know if this is the case.

Did anyone face similar issues? Is there something I'm missing?

like image 224
mr_m1m3 Avatar asked Sep 01 '17 10:09

mr_m1m3


People also ask

What does @RequestBody do in Spring?

Simply put, the @RequestBody annotation maps the HttpRequest body to a transfer or domain object, enabling automatic deserialization of the inbound HttpRequest body onto a Java object. Spring automatically deserializes the JSON into a Java type, assuming an appropriate one is specified.

Does Spring @RequestBody support the GET method?

Thanks. spring v5. 3.6 supports request body with GET.

Can RequestBody be optional?

requestBody consists of the content object, an optional Markdown-formatted description , and an optional required flag ( false by default). content lists the media types consumed by the operation (such as application/json ) and specifies the schema for each media type. Request bodies are optional by default.

How do you throw an exception in a response entity?

You have to provide implementation to use your error handler, map the response to response entity and throw the exception. Create new error exception class with ResponseEntity field. Custom error handler which maps the error response back to ResponseEntity.


2 Answers

The solution was to simply put required = false in RequestBody annotation. After that, I could easily add some logic to throw custom exception and handle it in ControllerAdvice.

@RequestMapping(value = {"register"}, method = RequestMethod.POST, produces = "application/hal+json")
public Resource<User> registerClient(@RequestBody(required = false) User user, HttpServletRequest request){
    logger.debug("addClient() requested from {}; registration of user ({})", getClientIp(request), user);
    if(user == null){
        throw new BadRequestException()
                .setErrorCode(ErrorCode.USER_IS_NULL.toString())
                .setErrorMessage("Wrong body or no body in reqest");
    } (...)
like image 53
mr_m1m3 Avatar answered Oct 22 '22 09:10

mr_m1m3


Firstly I suggest you to use BindingResult as a parameter of the POST call and check if it returns an error or not.

@RequestMapping(value = {"register"}, method = RequestMethod.POST, produces = "application/hal+json")
public ResponseEntity<?> registerClient(@RequestBody User user, HttpServletRequest request, BindingResult brs)
    if (!brs.hasErrors()) {
        // add the new one
        return new ResponseEntity<User>(user, HttpStatus.CREATED);
    }
    return new ResponseEntity<String>(brs.toString(), HttpStatus.BAD_REQUEST);
}

Secondly, the call can throw some of errors, a good practice is to carch them and return them itself or transform them to your own exception object. The advantage is it secures a call of all the update/modify methods (POST, PUT, PATCH)

@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
    return new ResponseEntity<List<MethodArgumentNotValidException>>(e, HttpStatus.BAD_REQUEST);
}

@ExceptionHandler({HttpMessageNotReadableException.class})
@ResponseBody
public ResponseEntity<?> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
    return new ResponseEntity<List<HttpMessageNotReadableException>>(e, HttpStatus.BAD_REQUEST);
}
like image 3
Nikolas Charalambidis Avatar answered Oct 22 '22 09:10

Nikolas Charalambidis