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

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.

Related

Remove null attributes from request body Spring Boot

I'm new to spring boot and was referring to this article. It asks to add #JsonInclude(Include.NON_NULL) annotation to remove null attributes from response body, however I'm interested in removing null attributes from request body.
Please suggest how can I achieve that.
The request body is:
{
"userUuid": "2u9k2ld8f-ghj47dhj",
"suggestion": null
}
and the request DTO class is:
#Data
#NoArgsConstructor
public class UserRequestDTO implements Serializable {
private String userUuid;
#JsonInclude(JsonInclude.Include.NON_NULL)
private String suggestion;
}
Clearly it is not working for me. When I stringify and print the request body, it includes suggestion attr which is null.
Thank you for any help.
Well... Do not use toString() use Jackson ObjectMapper. So like:
var objectMapper = new ObjectMapper();
var dto = objectMapper.readValue(JSON, UserRequestDTO.class);
var strNoNullFields = objectMapper.writeValueAsString(dto);
That way the annotation is working.
Anyway this is not something very optimal. You might want to re-think your design how to calculate checksum (your question smells a bit like XY-problem).
I need to generate the checksum of the payload excluding the null attrs. I do not want to replace null attr rather remove it. To generate the signature I will to doing .toString() on the request body
I think you are looking for a request has dynamic attributes.
Try use a HashMap<String,Object> on parameters.
Other simple way is overriding toString() from DTO and ignore null attrs.
Suggestions: Besides using this only for logging I think both is bad practice could be better create a method generateSignature() handles all rules of this.

resilience4j circuit breaker change fallback method return type than actual called method return type

I am trying to learn Spring Boot microservices. Now I am trying to implement circuit breaker with resilience4j if any of my called service is off.
If I set the fallback method return type as like the actual method return type than it works fine but I can't show the information that my service is off. Because it then send the response null in object's fields. But if I change the return type to String on actual method and also in fallback then I will not be able to get the object value as JSON.
Is it possible to return as string something like Branch service is down!.. with my fallback method and if OK then get the object value as JSON from actual called method? My attempts are below:
My controller method:
#GetMapping("/getById/{id}")
#CircuitBreaker(name = "default", fallbackMethod = "employeeFallback")
public ResponseModelEmployee getEmployee(#PathVariable("id") Long id) {
return employeeService.findByEmployeeId(id);
}
My fallback method in controller:
public ResponseModelEmployee employeeFallback(Long id, Exception ex) {
return new ResponseModelEmployee();
}
My service method called from controller:
public ResponseModelEmployee findByEmployeeId(Long id) {
ResponseModelEmployee empDetails = new ResponseModelEmployee();
...
Branch branch = restTemplate.getForObject("http://BRANCH-SERVICE/branch/getById/" +
employee.get().getBranchId(),
Branch.class);
...
return empDetails;
}
My desire method as fallback:
public String employeeFallback(Long id, Exception ex) {
return "Branch Service is down";
}
If I set my desire method for fallback then it gives the following error:
java.lang.NoSuchMethodException: class com.example.employee.VO.ResponseModelEmployee class com.example.employee.controller.EmployeeController.employeeFallback(class java.lang.Long,class java.lang.Throwable) at io.github.resilience4j.fallback.FallbackMethod.create(FallbackMethod.java:92) ~[resilience4j-spring-1.7.0.jar:1.7.0] ....
Resilince4j expects the fallback method to have the same return type as of the actual method.
Documentation says:
It's important to remember that a fallback method should be placed in
the same class and must have the same method signature with just ONE
extra target exception parameter).
If there are multiple fallbackMethod methods, the method that has the
most closest match will be invoked, for example:
If you try to recover from NumberFormatException, the method with
signature String fallback(String parameter, IllegalArgumentException
exception)} will be invoked.
You can define one global fallback method with an exception parameter
only if multiple methods has the same return type and you want to
define the same fallback method for them once and for all.
So, you cannot directly change the return type to a different one.
You can try few suggestions:
Add #CircuitBreaker and fallback to the service method.
Change return type of service method and fallback method to Object.
One more way could be , you can keep the return type as it is but add a String type message object to response model ResponseModelEmployee. Then set message string to it during fallback.
Another solution could be to return ResponseEntity from the from the method where rest call is made and in the fallback method use ResponseEntity<?> as response object.
you can use Object as a return type
in my case for example:
#GetMapping("/getUser/{id}")
#CircuitBreaker(name= something , fallbackMethod = "ContactsServiceDown")
public ResponseEntity<User> getDetailsById(#PathVariable(id)){
//some logic
return new ResponseEntity<User>(user , HttpStatus.OK);
}
public ResponseEntity<Object> ContactsServiceDown(int id , Exception e){
//some logic
return new ResponseEntity<Object>("ContactsServersDown", HttpStatus.Forbidden)
}
or in returnType ResponseEntity<> leave the type Field empty, hopefully it may work!

Different class types as RequestBody depending on RequestParam provided in a Spring Boot Controller?

So, I have a controller which takes in a request parameter and a body. The request body can be of various class types depending on the type of parameter. Currently I am using JsonNode for the body which works fine. Looks like this :
#PostMapping() public ResponseEntity<Response> save(#RequestParam("request type") RequestProcess process, #Valid #RequestBody JsonNode requestJson) {
I want to know whether it's possible to provide the body with the class type depending on the param provided. If yes how do I do it?
If this is not possible in REST, is there a chance I might be able to do this using GraphQl. I don't know much about GraphQL still researching.
TIA
The nearer you can reach is by using generics
class Controller < T > {
#PostMapping("/save")
ResponseEntity < Response > save(#RequestBody T requestJson) {}
}

Null check for Request body object

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.

spring security : Why can't we access Hibernate entitiy parameters in #PreAuthorize?

I have the following interface method on which I am applying #PreAuthorize :
#PreAuthorize("doSomething(#user.id)")
void something(User user, List<User> accessList);
where User is a Hibernate entity object. It gives me an error :
org.springframework.expression.spel.SpelEvaluationException:
EL1007E:(pos 13): Field or property 'id' cannot be found on null at
org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:204)
There is no way that the user parameter is null, as if I remove the annotation, and inspect the value of user in the method that implements this interface method, there is a valid User object present there. Additionally, just before calling this method, I have made sure that the user object is correctly constructed.
I really can't figure out why would the user field be considered null by the SPEL parser
You can check with the debugger what's going on in MethodSecurityEvaluationContext, inside Object lookupVariable(String name) method:
#Override
public Object lookupVariable(String name) {
Object variable = super.lookupVariable(name);
if (variable != null) {
return variable;
}
if (!argumentsAdded) {
addArgumentsAsVariables();
argumentsAdded = true;
}
and so you can see what's really going on in the addArgumentsAsVariables() method as the convertion of method arguments to SPEL variables is implemented very clearly in Spring.
Spring Security has a better answer for this problem now:
http://docs.spring.io/spring-security/site/docs/3.2.5.RELEASE/reference/htmlsingle/#access-control-using-preauthorize-and-postauthorize
Basically, you can use the #P annotation or #Param annotation if you are using < JDK 8.
You can check LazyParamAwareEvaluationContext,inside loadArgsAsVariables() method, version 3.1.0.
The same key for different Entity, because of implementing interface.
I need to add something to this as the title indicates that we cannot access hibernate properties.
There are two editions of hasPermission, the loaded object and the serialized object. Here is some code from a test case:
#PreAuthorize("isAuthenticated() and hasPermission(#organization, 'edit')")
public long protectedMethod(Organization organization)
{
return organization.getId();
}
And for the latter here we see that we can infact access the id proprty of the organization (which is a hibernate entity):
#PreAuthorize("isAuthenticated() and hasPermission(#organization.getId(), 'organization', 'edit')")
public long protectedMethodSerializableEdtion(Organization organization)
{
return organization.getId();
}

Resources