Spring Boot Validation Respond with JSON - java

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?

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();

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

Related

How to retrieve all user information except passwords

I implemented a basic JPA authentication following this tutorial.
I wanted to create an endpoint /mydetails to display user information (profile info).
What I've tried:
#GetMapping("/mydetails")
public Optional<User> getUser(HttpServletRequest request) {
Optional<User> foundUser = Optional.ofNullable(userRepo.getUserByUsername(request.getUserPrincipal().getName()));
return foundUser;
}
Outcome:
{
"id":1,
"username":"name.surname#companyname.com",
"password":"$2a$10$7YzUO6scaC06LV6IgOsSXetFm4/U0WM.UZykhRfQcJBzKacyZFMK",
"first_name":"John",
"last_name":"Walker",
"organization_name":"ABC",
"role":"Admin",
"credibility_rating":"100"
}
The problem is that this literally takes out all the information and I want everything except the password.
How could I stop the response from sending the password information?
I am totally new to Spring and have not used Java for many years.
Any insight would be highly appreciated.
It seems you are talking about a REST controller that returns JSON. With the default configuration, Spring Boot uses Jackson to transform objects to JSON. The most simple fix would be to tell Jackson to ignore the password field in your User class:
public class User {
...
#JsonIgnore
private String password;
...
}
See this article for more information.

What's the difference between return type of ResponseEntity<> and simple object (ex. User) in REST Spring Java?

I'm new to REST and I'm making simple REST application with users and articles. I wonder what's the difference between two samples below:
#GetMapping("/user/{id}")
public User getUserById(PathVariable("id") String id) {
.....
return userService.getUserById();
}
and
#GetMapping("/user/{id}")
public ResponseEntity<User> getUserById(PathVariable("id") String id) {
.....
return new ResponseEntity<> ....
}
Which one is better to use?
And what's the main difference between two of them?
ResponseEntity is containing the entire HTTP response that returns as a response which gives the flexibility to add headers, change status code and do similar things to the response.
Another hand sending PJO class directly like returning users in the example is somewhat similar to return ResponseEntity.ok(user) which responded to user details successfully to the user. But the ability to change headers, status codes is not available if you return PJO directly.
It is good to use ResponseEntity over PJO when there is a scenario you need to change the headers or you need to change status according to the result.
eg: show not found when there is no data you can return ResponseEntity.status(404).body(<-body->).
at least in the Response Entity, you can set the http status and you can use ResponseEntity<?> where ? is generic any object its very convenient to use

Lazy loading object causing issue during serialization. Getting 500 internal server error

I have written API's which are resulting in 500 error when hit through postman or browser. However, when I debug and see server is not throwing any error and in fact returning a proper response. Other controller I have implemented in a similar way is returning expected result. below is my controller code. Has anyone faced similar situation. Kindly help.
#CrossOrigin
#GetMapping(value="/byPatientId/{patientId}", produces = "application/json")
public List<ContactInfo> getAllContacts(#PathVariable String patientId) {
logger.info("Received request for List of ContactInfo for patientId: "+patientId);
List<ContactInfo> list =
contactInfoService.getAllContacts(patientId);
return list;
}
#CrossOrigin
#GetMapping("/byContactId/{contactId}")
public ContactInfo getContactById(#PathVariable Integer contactId) {
logger.info("Received request for ContactInfo for contactId: "+contactId);
return contactInfoService.getContactById(contactId);
}
The problem was with one of the dependent object which was having oneToMany relationship with the return type object and it was set to Lazy loading and issue was during the serialization.
Either we can change it to Eager loading or ignore the dependent object by adding #JsonIgnore on dependent object.
I handled it by adding #JsonIgnore annotation on top of the dependent object as I don't need the dependent object in this particular usecase. Issue is solved now.
How is your Controller annotated? is it with #Controller or #Rest?
#RestController = #Controller + #ResponseBody(for serializing the response and pass it into the HttpResponse.
Add the #ResponseBody in your methods on the controller or change the #Controller tag into a #RestController(take into account that #RestController is available since 4.0 Spring version).
More info:https://www.baeldung.com/spring-controller-vs-restcontroller

How to validate request parameters in Spring REST controller

I am working on a Spring REST application.
This application has only REST controllers, no view part.
I want to know how can I validate a #RequestParam
For example
#RequestMapping(value = "", params = "from", method = RequestMethod.GET)
public List<MealReadingDTO> getAllMealReadingsAfter(#RequestParam(name = "from", required = true) Date fromDate) {
......
......
}
In the above example, my goal is to validate the Date. Suppose someone pass an invalid value, then I should be able to handle that situation.
Now it is giving and exception with 500 status.
PS
My question is not just about Date validation.
Suppose, there is a boolean parameter and someone passes tru instead of true by mistake, I should be able to handle this situation as well.
Thanks in advance :)
Spring will fail with an 500 status code, because it cannot parse the value.
The stages of request handling are:
receive request
identify endpoint
parse request params / body values and bind them to the detected objects
validate values if #Validated is used
enter method call with proper parameters
In your case the flow fails at the parse (3) phase.
Most probably you receive a BindException.
You may handle these cases by providing an exception handler for your controller.
#ControllerAdvice
public class ControllerExceptionHandler {
#ExceptionHandler(BindException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public YourErrorObject handleBindException(BindException e) {
// the details which field binding went wrong are in the
// exception object.
return yourCustomErrorData;
}
}
Otherwise when parsing is not functioning as expected (especially a hussle with Dates), you may want to add your custom mappers / serializers.
Most probably you have to configure Jackson, as that package is responsible for serializing / deserializing values.

java webservice response object vs Produces

Im trying to java webservices and trying to follow couple of tutorial examples.
In one of the example, I see #Produces annotation being used to specify the type of response that is being returned.
Example:
#GET
#Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello() {
return "Hello Jersey";
}
but in another case, I see the Response object is being used as a Response...
Example:
#GET
#Path("/{param}")
public Response getMsg(#PathParam("param") String msg) {
String output = "Jersey say : " + msg;
return Response.status(200).entity(output).build();
}
Question:
Which is the correct way to send a response back - Response object or #Produces annotation?
When both scenarios can be used?
The best way is to use the combination of both, all the times. Here's why
#Produces basically defines the CONTENT-TYPE (MIME-TYPE) of the Response. But that is pretty much all. It does not define the HTTP Status codes (on error/success/server error etc). #Produces annotation just makes your life easier by not explicitly specifying WHAT the content-type would be in the Response.
Now why to use Response instead of "String" as the return type? Here's an example
Let's take the following code into consideration:
#GET
#Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello() {
try
{
return someRemoteServerApi.getHelloString();
}
catch(exception ex)
{
return getErrorMessageString();
}
}
Now lets say that the remote server api failed to return back, which cased some kind of an error. NOW, you want to return an error back to the client ALONG with an error code (because frankly speaking, clients will only care for the error message string when developing. Once the client is developed, they will solely base their apis on the HTTP return status code).
So, in the above example, lets say you return an error json String such as (the getErrorMessageString()) :
{
"error":500
"message": "something went wrong"
}
BUT, your actual HTTP status code will still be "200 OK", because the Response is still a String and the response went through just fine. How would a client know if something went wrong in the apis? He will have to parse the json string (on error) and determine what went wrong.
Basically, in the above case, your success and failure BOTH will have the same HTTP status return code, which is not very helpful to the clients. Instead, the above code should change something like:
#GET
#Produces(MediaType.TEXT_PLAIN)
public Response sayPlainTextHello() {
try
{
return Response.status(200).entity(someRemoteServerApi.getHelloString()).build();
}
catch(exception ex)
{
return Response.status(500).entity(getErrorMessageString()).build();
}
}
Finally,
In regards to answering both your questions:
1) #Produces has nothing to do with WHAT kind of Response will be sent. It just sets the content-type on whatever Response object you will be sending. JAX-RS, by default, will put 200 OK response, unless ofcourse, an exception occurs that is uncaught. Then it will probably return 500. Basically, you will be relying on JAX-RS to return your error codes for you, which is not very good. You, as an implementer, should determine what kind of error codes and error messages should be sent to the client that are MOST meaningful
2) I will ALWAYS use Response as the return type of the method, along with #Produces and #Consumes annotations for each method. If you believe that your full resource (all methods in java resource class) is using the same #Produces and #Consumes mime-type (in most cases it's application/json) then you can define this at the class level itself, something along the lines of:
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#Path("/rest/someResource")
public class MyResource()
{
#Path("/{resourceId")
#GET
public Response getResource(#PathParam("resourceId") String id)
{
doStuffAndReturnResponse();
}
}
This will, by default, apply the #produces and #consumes annotation to ALL the resource methods and if you want something specific on some specific resource method, you can override it by just providing the annotation for that specific method.
I hope I have explained it good enough! Happy coding!

Categories

Resources