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;
}
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With