Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

REST Handling a Bad Parameter

Tags:

java

rest

spring

What is the proper way to handle a bad parameter in a RESTful service? I have an endpoint right now that returns a 400 if the wrong data type is sent.

@ResponseBody
@RequestMapping(produces = "application/json", method = RequestMethod.GET, value="/v1/test")
public MyResponse getSomething(@RequestParam BigDecimal bd) {
    MyResponse r = new MyResponse();
    r.setBd(bd);
    return r;
}

It would be really nice if the end user were to pass, say, a String instead of a BigDecimal, that the response would return a json with the response code, status, and whatever else I'd like it to contain, rather than just a 400. Is there a way to do this?

Update: My initial thought was to wrap every parameter and then check to see if it's the correct type in that wrapper class. This seems a bit silly. Isn't there a validator that I could just add to the classpath that would recognize something like this?

Also, there is a way to handle this quite easily with a Bean type that I could create on my own, but what about standard types like BigDecimal?

UPDATE-2: This update addresses answer that uses @ExceptionHandler.

TestController.java

import java.math.BigDecimal;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/")
public class TestController {

    //this is where correct input from user is passed, no binding errors
    @RequestMapping(produces = "application/json", method = RequestMethod.GET, value="/v1/test")
    public
    @ResponseBody
    MyResponse getSomething(@RequestParam BigDecimal bd) {
        MyResponse r = new MyResponse();
        r.setBd(bd);
        return r;
    }

    //this will handle situation when you except number and user passess string (A123.00 for example)
    @ExceptionHandler(ServletRequestBindingException.class)
    public @ResponseBody MyErrorResponse handleMyException(Exception exception, HttpServletRequest request) {

        MyErrorResponse r = new MyErrorResponse();
        r.setEexception(exception);

        return r;
    }


}

TestUnitTest.java

public class TestUnitTest {

protected MockMvc mockMvc;

@Autowired
protected WebApplicationContext wac;

@Before
public void setup() {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}


@Test
public void test() throws Exception {
    String url =  "/v1/test?bd=a123.00";

    log.info("Testing Endpoint::: " + url);

    MvcResult result =  mockMvc.perform(get(url))
                        .andExpect(status().isOk())
                        .andReturn();

    log.info("RESPONSE::: " + result.getResponse().getContentAsString());
}

}

MyResponse.java

import java.math.BigDecimal;

public class MyResponse {

    private BigDecimal bd;

    public BigDecimal getBd() {
        return bd;
    }

    public void setBd(BigDecimal bd) {
        this.bd = bd;
    }

}

MyErrorResponse.java

public class MyErrorResponse {

    private Exception exception;

    public Exception getException() {
        return exception;
    }

    public void setEexception(Exception e) {
        this.exception = e;
    }

}
like image 899
Matt Avatar asked Oct 20 '22 01:10

Matt


1 Answers

Use Spring @ExceptionHandler along with standard @RequestMapping annotation like this:

//this is where correct input from user is passed, no binding errors
@RequestMapping(produces = "application/json", method = RequestMethod.GET, value="/v1/test")
public
@ResponseBody
MyResponse getSomething(@RequestParam BigDecimal bd) {
    MyResponse r = new MyResponse();
    r.setBd(bd);
    return r;
}

//this will handle situation when there's exception during binding, for example you except number and user passess string (A123.00 for example)
@ExceptionHandler(TypeMismatchException.class)
public 
@ResponseBody
MyErrorResponse handleMyException(Exception exception, HttpServletRequest request) {
    //....
}

TypeMismatchException is general exception thrown when trying to set a bean property. You can generalize code even more and catch every binding exception with few methods, for example:

@ExceptionHandler(TypeMismatchException.class)
public
@ResponseBody
String typeMismatchExpcetionHandler(Exception exception, HttpServletRequest request) {
    return "type mismatch";
}

@ExceptionHandler(MissingServletRequestParameterException.class)
public
@ResponseBody
String missingParameterExceptionHandler(Exception exception, HttpServletRequest request) {
    return "missing param";
}

@ExceptionHandler(Exception.class)
public
@ResponseBody
String generalExceptionHandler(Exception exception, HttpServletRequest request) {
    return "general exception";
}

It's very flexible, allowing many parameters in signature and returned objects Annotation Type ExceptionHandler

With @ResponseBody you may return any object, that can be serialized into JSON. It's only required to have jackson library in your classpath but I assume that you already know this

like image 123
kamil Avatar answered Oct 24 '22 14:10

kamil