MultipartFile Wrapper Object woks as #ModelAttribute but not as #RequestParam - spring-boot

I created a spring RestController to process fileUpload from a react js UI.
To be able to use custom validations by #Validated annotation, had to wrap the MultipartFile into a class, I named it as UploadedFile. Created my Post Handler method with argument as #ModelAttribute.
Everything works fine.
validate(target, error) method in my custom validator is called and inside POST handler method, UploadedFile object has the multipart file containing the uploaded document..
Here is a perfectly working code
#PostMapping("/file")
public ResponseEntity<?> receiveFile(#Validated #ModelAttribute UploadedFile file) {
}
#Getter
#Setter
public class UploadedFile {
MultipartFile file;
}
// one CustomValidator class and webDataBinder.addValidators(customValidator) in controller
multipart.enabled=true //in application.properties
So far everything works as expected, Problem arise when
Someone asked me that, #ModelAttribute is a spring MVC construct, as this is a microservice, which in future, apart from my React UI, will cater to other api requests too, so I should use #RequestParam instead of #ModelAttribute.
So I changed #ModelAttribute to #RequestParam as follows
#PostMapping("/file")
public ResponseEntity<?> receiveFile(#Validated #RequestParam(name = "file") UploadedFile file)
But now I get below exception
Failed to convert value of type
'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile'
to required type 'com.x.x.x.UploadedFile';
nested exception is java.lang.IllegalStateException:
Cannot convert value of type
'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile'
to required type 'com.x.x.x.UploadedFile':
no matching editors or conversion strategy found
If, for #RequestParam, I change type as MultipartFile instead of UploadedFile then my posthandler gets called but the custom Validator doesn't gets called by spring before handler call,
I tried to use instanceOf MultiPartFile in validator supports method by still no avail.
#PostMapping(value="/file" )
public ResponseEntity<?> receiveFile(#Validated #RequestParam(name = "file") MultipartFile file)
I've the work around to explicitly call the custom validator method by code as first line in POST handler, so I dont need a solution MY QUESTION IS
How come, without adding any custom #Bean or any extra external dependencies everything works fine with #ModelAttribute, but merely changing the annotation #RequestParam doesn't work.

Related

How the request body of form is passed to spring mvc controller and how to map to pojo class instance?

How the request body of form is passed to the spring MVC controller and how to map to the POJO class instance?
I presume, you are building end point using POST. If yes, you can annotate method parameter with #RequestBody to a capture request body.
Basically, #RequestBody is used to bind the HTTP request body with a domain object in method parameter. Behind the scenes, these annotation uses HTTP Message converters to convert the body of HTTP request to domain objects.
#RequestMapping(value="/user/create", method=RequestMethod.POST)
public void createUser(#RequestBody User user){
// your logic goes here..
// Make sure that parameters in User POJO matches with HTTP Request Parameters.
}
I assume you are using POST API request for your use case. In the spring mvc controller class, we can make use of #RequestBody annotation followed by Pojo class.
Example:
#PostMapping
public ResponseEntity<ResponseDto> saveData(#RequestBody Data data) {
// Access to service layer
}
Let's say you're doing a POST request.
#PostMapping
public void saveSomeData(#RequestBody PojoClass pojoClass){
// whatever you wanna do
}
The #RequestBody annotation extracts the data your client application is sending on the body of the request. Also, you'd want to name the member variables on your pojo class similar to how you named it in on your request body.
Your POJO class can be something like:
public class PojoClass{
private String name;
private int age;
}
Now you can create getters and setters or use the Lombok annotation #Data on the class level to auto-generate the getters and setters.
Hope this was helpful:)

Can't construct instance of MultipartFile

I get the following error when trying to save a vehicle entity attached with an image:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of org.springframework.web.multipart.MultipartFile (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
Here is my controller code and the request body class I made:
#PostMapping("/save")
public ResponseEntity<Vehiculo> postVehiculo(#RequestBody VehiculoSaveBody json) throws IOException{
Vehiculo vehiculo = json.getVehiculo();
Version_Vehiculo version = json.getVersion();
MultipartFile file = json.getFile();
System.out.printf(file.getResource().getFilename());
return vehiculoService.postVehiculo(vehiculo, version, file);
}
#Data
public class VehiculoSaveBody{
Vehiculo vehiculo;
Version_Vehiculo version;
MultipartFile file;
}
I've seen in every tutorial that everyone receives de file as a RequestParam and specifies the name, something like #RequestParam("file") MultipartFile file.
Is the error caused by the way I receive the Json as a RequestBody?
try #MultipartForm of org.jboss.resteasy.annotations.providers.multipart.MultipartForm instead of #RequestBody

Get raw json string in Spring MVC Rest

Im have #RestController and this method. how can I get any json and then select the handler depending on the method and pass it there for processing
PS. I use GSON instead of JACKSON
You can use #RequestBody in your method and take a String parameter:
public AbstractJsonResponse(#PaqthVariable String method, #RequestBody String json) {
...
}
See here: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html
Mainly the part that says:
Annotation indicating a method parameter should be bound to the body of the web request

Spring #InitBinder on #RequestBody

I'm trying to use the #InitBind annotation to map only certain fields on the object in the request body.
I have a spring controller defined in this way:
#RequestMapping(value = "addAddress", method = RequestMethod.POST)
public Object addAddressToPerson(
HttpServletRequest request,
HttpServletResponse res,
#RequestParam(value = "name", required = false) String name,
#RequestParam(value = "surname", required = false) String surname,
#RequestBody personDTO personJson,BindingResult result) {
The client request will be a a json representing a personDTO, but I don't want that field except the address to be mapped in the object for security reasons.
The input will be something like:
{ "address":"123 Street","........}
The personDTO contains many fields, and since spring map all of them directly in a DTO, that can be a problem.
I've seen that a solution is to use a Binder to declase the allowed or Disallowed field, but if I check the personDTO inside the controller, other fields are populate (for example if pass "id":"1234").
Any Hints?
The binder code is the following:
#InitBinder("orderJson")
protected void orderJsonBinder(WebDataBinder binder){
binder.setAllowedFields(new String[]{"address"});
}
Am I missing something?
Best Regards,
Luca.
But you are not binding request parameters to a model attribute bean, you are just asking spring to use an appropriate MessageConverter to convert the request body. As you say it is Json, you will use a MappingJackson2HttpMessageConverter (or MappingJacksonHttpMessageConverter with Jackson 1.x). The Spring Reference Manual says for this converter :
[This is an] HttpMessageConverter implementation that can read and write JSON using Jackson's ObjectMapper. JSON mapping can be customized as needed through the use of Jackson's provided annotations. When further control is needed, a custom ObjectMapper can be injected through the ObjectMapper property for cases where custom JSON serializers/deserializers need to be provided for specific types. By default this converter supports (application/json).
#InitBinder can only configure binding of #ModelAttribute annotated parameters. It is useless here. If Jackson annotations are not enough, you will have to use a custom object mapper.
And I am surprised that you can use a BindingResult after a #RequestBody parameter, because the documentation says that it should follow a #ModelAttribute one.

Spring Controller fetch query parameter for a wrapper request class

I am trying to build RESTful web service by using spring 4.0
I have a controller:
#Controller
#RequestMapping("test")
public class Controller{
#RequestMapping("fetch",method=RequestMethod.GET)
#ResponseStatus(value=HttpStatus.OK)
#ResponseBody
public ResultResponse fetchController(ResultRequest req){
if((req.getName).equals("John"))
return new ResultResponse(100);
else
return new ResultResponse(0);
}
}
and my ResultRequest.class
public class ResultRequest{
private String name;
//getter,setter
}
If I hit the url to //host//contextPath/test/fetch?name=John
the controller will return the object ResultResponse(100)
my question is, there no #RequestParam or other annotation in the request parameter,
how does the spring controller know to set the query parameter "name" as the property of wrapper class
ResultRequest ?
Thanks
Spring uses implementations of an interface called HandlerMethodArgumentResolver for resolving arguments to pass to handler methods, ie. methods annotated with #RequestMapping.
One of these is a ModelAttributeMethodProcessor. Its javadoc states
Resolves method arguments annotated with #ModelAttribute and handles
return values from methods annotated with #ModelAttribute.
Model attributes are obtained from the model or if not found possibly
created with a default constructor if it is available. Once created,
the attributed is populated with request data via data binding and
also validation may be applied if the argument is annotated with
#javax.validation.Valid.
When this handler is created with annotationNotRequired=true, any
non-simple type argument and return value is regarded as a model
attribute with or without the presence of an #ModelAttribute.
Spring registers two objects of this type. One to handle parameters annotated with #ModelAttribute and one to handle ones without.
Further reading:
Form submit in Spring MVC 3 - explanation
An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the #RequestBody or the #RequestPart arguments

Resources