Null check for Request body object - spring

Is it possible to do integration test for null check.
I passed null value.
HttpEntity<Employee> entity = new HttpEntity<Employee>(null, headers);
restTemplate.exchange(url, httpMethod, entity, String.class);
I got the below error.
{"timestamp":"2018-10-06T14:33:52.113+0000","status":400,"error":"Bad Request","message":"Required request body is missing:"}
#RestController
public class EmployeeController {
#PostMapping(value = "/employee/save", produces = "application/json")
public Employee save(#RequestBody Employee employee){
if(employee==null){
throw new RuntimeException("Employee is null");
}
}
}
class Employee {
}

#RequestBody(required=false) Employee employee
please try with required option in #RequestBody.
The problem here is the mapping in spring mvc.
required
Default is true, leading to an exception thrown in case there is no body content. Switch this to false if you prefer null to be passed when the body content is null.
#RequestBody Employee employee
Your method only is processed the request if employee is not null. Then it considered mapping correctly and pass request to this method and handle it. So the check null condition will be needless here.

I am not sure whether it's doable, but I will say it is a bad practice.
According to RFC 7231:
The POST method is used to request that the origin server accept the
representation enclosed in the request as data to be processed by the
target resource.
Since you have annotated your controller PostMapping, there should be request body to server. I don't see the value of writing integration test for null or empty request body.
If we look into the HTTP request structure,
POST /your_url HTTP/1.1
HOST your_host
ContentType ...
ContentLength ...
Body line 1
What's the difference between null and empty?

You can use javax validation framework to check whether #requestbody is null or not
please use below approach: It'll definitely resolve your concern.
Maven Dependency:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
#RestController
public class EmployeeController {
#PostMapping(value = "/employee/save", produces = "application/json")
public Employee save(#NotNull #RequestBody Employee employee){
}
}

You are getting this error message because #RequestBody annotation has default value and you have to send valid request to your method.
You don't need to null check inside of method for request object because #RequestBody has required field and this field's default value is true. If you check the inside of #RequestBody interface you are going to see what I am saying. Also you can see the below;
If you set #RequestBody(required = false) in method's parameter you are not going to get error message, but you need to check your request object is null or not... If you don't check-out of object is null when you use that object you will get "Null Pointer Excepiton"...
your choise...
good luck

You can use #NonNull either with the #RequestBody annotation. Or, even better if you want the variables of the Object to always be non-null after the constructor is called, you can use this annotation in the class itself when defining the variables/attributes.

Related

#PathVariable validation gives 500 instead of 400

In the Spring Framework validating the request respond with error 400 (Bad Request), specially when validating the request body and the request fields decorated with the
javax.validation.constraints.* annotations (which specified in JSR 303).
For make it more clear lets go through the example:
I have decorated the fields of the class ContactDetails with #Email and #NotEmpty constraints
class ContactDetails {
#Email
String email;
#NotEmpty
String message;
}
In the controller I used #Valid annotation to make Spring Validator validate the http request body.
#RestController
class NotificationController {
#PostMapping("/sendNotification")
public String sendNotification(#Valid #RequestBody ContactDetails contactDetails) {
...
}
}
If the validation fails, it will trigger a MethodArgumentNotValidException. By default, Spring will translate this exception to a HTTP status 400 (Bad Request).
But for validating the request params or path variables based on Spring documentations I will decorate the controller class with #Validated and just using javax.validation.constraints.* annotations on the parameters and I expect the same result same as validating the request body.
#Validated
#RestController
class NotificationController {
#GetMapping("/getContactDetailsByEmail/{email}")
public ContactDetails findContactDetails(#Email String email) {
...
}
}
In contrast to request body validation a failed validation will trigger a ConstraintViolationException instead of a MethodArgumentNotValidException. Spring does not register a default exception handler for this exception, so it will by default cause a response with HTTP status 500 (Internal Server Error).
I expected to get error 400 for this scenario and I do not know if I missed any thing in my code?
That would be great if any body can help me with this scenario why Spring has different approaches for validating the parameters.
You can create the answer you want by using the fields in the ConstraintViolationException with the following method;
#ExceptionHandler(ConstraintViolationException.class)
protected ResponseEntity<Object> handlePathVariableError(final ConstraintViolationException exception) {
log.error(exception.getMessage(), exception);
final List<SisSubError> subErrors = new ArrayList<>();
exception.getConstraintViolations().forEach(constraintViolation -> subErrors.add(generateSubError(constraintViolation)));
final SisError error = generateErrorWithSubErrors(VALIDATION_ERROR, HttpStatus.BAD_REQUEST, subErrors);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
In the first case - with argument annotated with #RequestBody - Spring uses RequestResponseBodyMethodProcessor to validate it and throws MethodArgumentNotValidException if validation fails, which is handled later by ResponseEntityExceptionHandler or DefaultHandlerExceptionResolver by translating it into 400 BAD REQUEST http response code.
In the second case Spring is using AOP for method validation with MethodValidationInterceptor class, which throws ConstraintViolationException if validation fails.
But, unlike the first case, Spring doesn't provide a default exception handler for ConstraintViolationException, so it's translated into 500 http response code.
That's why developers should consider creating their own exception handlers for this kind of method-level validation.

How to write appropriate endpoint in Spring Boot for GET request?

I have an assignment to write simple GET request.
The format that is going to be typed in URL is like this:
http://localhost:8080/api/tasks/20-08-2020
Server should return TODOs for that date. I did managed to write a finder method. But not sure how to write an endpoint. This is what I have so far:
#GetMapping(value = "/{date}", consumes="application/json")
public ResponseEntity<List<Task>> getTasksByDateUsingURL(#PathVariable("date") #DateTimeFormat(pattern="dd-MM-yyyy") #Valid LocalDate dueDate){
List<Task> tasks = taskService.getAllTasksByDate(dueDate);
return new ResponseEntity<List<Task>>(tasks,HttpStatus.OK);
}
This is inside RestController class:
#RestController
#RequestMapping(value="/api/tasks")
public class TaskController {...}
I cannot hit this GET endpoint...
Workaround for your problem is to get the string as parameter and parse it manually
#GetMapping(value = "/{date}", consumes="application/json")
public ResponseEntity<List<Task>> getTasksByDateUsingURL(
#PathVariable("date")
String date
){
LocalDate dueDate = parse(date);
List<Task> tasks = taskService.getAllTasksByDate(dueDate);
return new ResponseEntity<List<Task>>(tasks,HttpStatus.OK);
}
private LocalDate parse(String stringDate) {
// TODO
}
As author said in comments:
When try to call the endpoint from browser, the mapping is not executed.
Seems like that the browser is sending request with wrong Content-Type header. Your mapping is explicitly requires only application/json value.
When try to call the endpoint from Postman, the application returns 400 status.
I could not see the body of response, but I guess the problem is #Valid annotation on the parameter. How should Spring validate the LocalDate?
So the solution is to remove consumes="application/json" from mapping or send corresponding Content-Type value
and remove #Valid annotation from parameter.

Create a new Object with #RequestBody(required=false)

I have a Rest Controller with an optional #RequestBody parameter, that I want to initialize if a user does not specify it. Now I'm doing this:
#PostMapping
public ResponseEntity<Page<?>> findAll(
#RequestBody(required=false) MyRequest request,
Pageable pageable
) {
if (request == null) {
request = new MyRequest();
}
...
}
Is there any automatic way to tell Spring MVC that in case the parameter is null it must create a new object?
I don't full control Spring but I think there is not an automatic way to initialize an object like this. If the body is empty, the message converter initializes an object with null value. That's why you have a NPE.
You can use the Java 8 Optional<Myrequest> even if I've never use it in this case and I'll be curious to know how the message converter treats this case. If you test it, please let me know.
Remember #RequestBody calls the empty constructor of MyRequest. Therefore request will always reference to an instance of that class.
Conclusion: There is no way request == null or request.equals(null) evaluates to true.

#RequestBody not restricting to POJO type and BindingResult hasErrors always false

I was not experiencing this problem early in development but just noticed that this was happening when debugging another problem. This happens on all REST endpoints, but below is an example:
#RestController
#RequestMapping("/editlisting")
public class EditParkingSpaceListingController {
#Autowired
ParkingSpaceRepository parkingSpaceRepository;
#Autowired
ParkingSpaceListingRepository parkingSpaceListingRepository;
#RequestMapping(method = RequestMethod.PUT)
public ResponseEntity<String> editParking(#RequestBody ParkingSpaceListingClient pslc, BindingResult result) {
if (result.hasErrors()) {
return new ResponseEntity<String>("", HttpStatus.BAD_REQUEST);
}
// Code to save pslc data to database.
Now, if I send an HTTP request with the body as
{ }
I get a 200 response and when I check MongoDB, there is a new empty document in the collection. If I send an empty body with no brackets, as expected it will return 400. If I send a body with random garbage data that does not exist in the POJO, BindingResult does not seem to pick up the error and a new blank document is still created.
You need to follow the below steps for the input document validations:
(1) Add the javax.validation package constraints (like #NotNull, #Size, etc..) to your ParkingSpaceListingClient bean class.
(2) Add #Validated annotation to your controller method, to capture the validation errors into BindingResult object.
You can look here for more details on Input Validations.

getting null parameter values when I send body from rest client

Iam getting null parameter values when i send values from rest client. But when I send Values from form(html view page) it is working fine.
Below one is my bean class.
public class Home {
private String id;
And I am sending values from rest client as post method.
{
"id":"10",
"load":"true"
}
Content-Type: application/json
Request is coming to the controller class. but it will return all values as null .But when I am sending values from html page it is working fine. Any one can help how to get values.
When I am using #RequestBody in controller calss, I am getting Caused by: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json' not supported exception
The annotation #RequestBody should be added on Home homeRequest.
This allows the controller to analyze the request's body as the object's instance itself. This is the case of a JSON request.
Otherwise it is possible to use #RestController instead of #Controller as an annotation of the controller which does the same as #Controller, but it adds implicitly to all the request mapping methods the annotation #RequestBody to the inputs and #ResponseBody to all the response objects.

Resources