Reputation: 1062
I'm still new to Spring in general and I'm trying to use Spring boot. I have a (hopefully) quick question. I'm trying to build a ReSTful service which will return JSON. I have followed the Building a RESTful Web Service Guide and can successfully return JSON. I've integrated JPA into my web service so that my data is backed by a database.
Now, I need to make a route in which, users can create an object and I would like the object to be validated. I've followed the Validation Form Input Guide but I'm not really trying to create a service that serves up web content. What I want is, whenever a validation error occurs, to return my own custom JSON. Thus far, I haven't been able to find any resources for making this happen though I've tried following Petri's Sweet Rest API Guide which I've found helpful on multiple occasions but doesn't seem to quite work in this scenario. I'm using hibernate-validator:5.0.1.Final
and hibernate for the following.
@Entity
@Table(name = "PEOPLE")
public class Person{
@Id
@Column(unique = true)
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Min(18)
private long age;
private String name;
//Necessary for JPA
protected Person() {}
public Person(long age, String name) {
this.age = age;
this.name = name;
}
// Getters Omitted
}
Then my PersonController:
@Controller
public class PersonController {
@RequestMapping(value="person/", method=RequestMethod.POST)
@ResponseBody
public ResponseEntity<Person> create(@Valid @RequestBody Person person) {
// Create in DB and return
}
}
This works in the most strict way, in that, if you send garbage JSON to this route, it will return a 400 which is pretty nice. But the body of the response is an HTML page which is not as nice. So, my question is, is there some way to catch a validation error? I've tried adding the following to my Controller but with no success:
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity handleError(MethodArgumentNotValidException ex) {
//generate my own error message
return new ResponseEntity(customErrorClass, HttpStatus.BAD_GATEWAY);
}
I'm aware Bad Gateway is not a valid return code, but I used it just to prove that the Exception handler is never called. When I POST to my rest service, I still see 400 Bad Request + HTML. I would assume that there is some sensible default that I can override but I can't seem to figure out where it is. I've tried googling and searching stackoverflow to no luck.
Thanks in advance.
UPDATE
If I modify the Controller to include a BindingResult
in the method signature:
@Controller
public class PersonController {
@RequestMapping(value="person/", method=RequestMethod.POST)
@ResponseBody
public ResponseEntity<Person> create(@Valid @RequestBody Person person, BindingResult bindingResult) {
if(bindingResult.hasErrors()){
//handle errors and return
} else {
// Create in DB and return
}
}
}
I can get it to work. (Note I also had to add the jasper-el jar to my dependencies) I had tried this before and didn't get it to work but the reason is not intuitive. I was posting with the following JSON: { "id" : "foo", "age": 22, "name" : "James Bond" }
According to the JSON Spec, this is valid JSON. Obviously, my Person Model cannot cast the String "foo" to a long. I won't go into whether or not the error should be a 400 or not, my new question is this: How can I catch this 400?
Upvotes: 5
Views: 14198
Reputation: 4532
The problem is that you set the field id in your model as a long, with the string foo. This is the reason for the 400 http error. Let's say then that this kind of exception is managed correctly yet by the expressive power of the http status. The key point is that you can think to manage the 400 error and the solution of zeroflagL works fine but it was a good solution if you use the @ExceptionHandler
like below:
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity handleBadInput(HttpMessageNotReadableException ex) {
// manage the exceptio for instance log it
.....
return ResponseEntity.badRequest().body(/** body may be optional if you want send a description of the error*/);
}
It is very important that your exception handler manage the exception in sense that log it as an error for a your analysis but then you should return a http response with 400 http status.
It is important because the http respon is correct, you had post a json that for your service didn't make sense and preserve this information may be vital for discovery problem in your client app for instance, the key point hear is that this http status speaking the your entity didn't make sense for your endpoint.
I hope that it can help you
Upvotes: 1
Reputation: 26828
To handle malformed or non-convertible JSON you can catch the HttpMessageNotReadableException
class
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity handleBadInput(HttpMessageNotReadableException ex) {
Throwable cause = ex.getCause();
Upvotes: 4