exception message for missing @RequestBody method parameter

6.6k views Asked by At

Firstly,if you have any ideas or solution, thank you to present here. I use @RequestBody on one of my controllers for a required parameter, I need some useful way of saying which parameter was missing if it's not there. When there are some parameters missing, it will throw the NullPointerException, SO I create a new exception to instance of this null exception (please see the httpemun and the httphandler) These are the primary codes which referring to this question.

my controller:

public ResponseEntity<?> createOrder(@RequestBody Cart cart) throws Exception {
  // ......
}

my entity cart:

public class Cart{
      private String channel
      private String cartId;
      private String status;
      private String currency;

      getters...
      setters...

    }

my Http emun class:

public enum HttpStatusEnum {
    CRE_CART_INCOMPLETE_BODY(400,"E001","Incomplete request body","");
    private HttpStatusEnum(int statusCode, String code,
                           String message, String detail) {
        this.statusCode = statusCode;
        this.code = code;
        this.message = message;
        this.detail = detail;
    }

    private final int statusCode;
    private final String code;
    private final String message;
    private String detail;

    public int getStatusCode() {
        return statusCode;
    }

    public String getCode() {
        return code;
    }
    public String getMessage() {
        return message;
    }
    public void setDetail(String detail) {      
        this.detail = detail;
    }

    public String getDetail() {     
        if(detail.isEmpty()) {
            return message;
        }else {
            return detail;
        }
    }
}

I also have one exception handle

@ControllerAdvice
public class GlobalExceptionHandler {
    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseEntity<Object> defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
  if(e instanceof NullPointerException)
    {if(req.getRequestURL().toString().contains(HttpSourceEnum.CART.toString()) && req.getMethod().equals(RequestMethod.POST.toString())){   
            errorBodyVo.setMessage(HttpStatusEnum.CRE_CART_INCOMPLETE_BODY.getMessage());
            errorBodyVo.setDetails(HttpStatusEnum.CRE_CART_INCOMPLETE_BODY.getDetail());  
            errorBodyVo.setCode(HttpStatusEnum.CRE_CART_INCOMPLETE_BODY.getCode());         
        }else {        
            errorBodyVo.setMessage(HttpStatusEnum.COMMON_MISSING_FIELD.getMessage());
            errorBodyVo.setDetails(HttpStatusEnum.COMMON_MISSING_FIELD.getDetail());  
            errorBodyVo.setCode(HttpStatusEnum.COMMON_MISSING_FIELD.getCode());
        }    
        httpStatus = HttpStatus.BAD_REQUEST;
    }
}

right now, my API request is:

{
  "channel": "XX",
  "cartId": "109",
  "status": "1",
}

I receive the API response just like below:

{
    "error": {
        "code": "E001",
        "message": "Incomplete request body",
        "details": ""
    }
}

but it doesn't match my expect. if the channel is missing in my request like below:

{
  "cartId": "109",
  "status": "1",
}

I expect to show "Required request body content is missing: Channel" in the details:

{
    "error": {
        "code": "E001",
        "message": "Incomplete request body",
        "details": "Required request body content is missing: Channel"
    }
}

How could I do that? Thanks guys!

1

There are 1 answers

0
DaveyDaveDave On BEST ANSWER

A better approach, if you can do it, would probably be to use JSR 303 Validation, which is probably included with your existing Spring dependencies, assuming you're using recent versions.

There's a good, if quite simple, tutorial here: https://www.mkyong.com/spring-mvc/spring-rest-api-validation/ and many more online with more details.

The official Spring docs on the subject are here: https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#validation-beanvalidation

You should only need to make changes to a few classes, and can potentially remove a lot of your exception handling, depending on how much control you want, or how much you're willing to go with Spring's defaults. The basic changes you'll need to make though are:

To tell Spring what validation is required, you add one or more annotations to the Cart class, for example, if you want to ensure that the channel is specified, you could do something like:

public class Cart{
  @NotNull
  private String channel
  private String cartId;
  private String status;
  private String currency;

Then, to instruct Spring to validate the Cart object before passing it to your controller, you need to add an @Valid annotation to your controller method signature:

public ResponseEntity<?> createOrder(@Valid @RequestBody Cart cart) throws Exception {

Lastly, modify the createOrder signature again to accept an Errors object:

public ResponseEntity<?> createOrder(@Valid @RequestBody Cart cart, Errors errors) throws Exception {

In the createOrder method you can then query the Errors object and respond accordingly. This could mean sending a specific response from the controller or throwing an exception to be handled by your existing exception handling mechanism.