#FormParam convert to Spring MVC - spring

I am using the latest version of Spring. I have to integrate with a third party server of company A. Now, company A has given me this code:
Path("/user")
public class CallBacks {
String hostDB="jdbc:mysql://localhost:3306/matchmove";
String username="root";
String password="password";
#POST
#Path("/add")
// #Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response receive(
#FormParam("id") String id,
#FormParam("amount") String amount,
#FormParam("asset_code") String assetCode,
#FormParam("asset_issuer") String assetIssuer,
#FormParam("memo") String memo) throws NumberFormatException, SQLException {
return Response.ok().build();
}
I want to use Spring as the rest of my project is in Spring! Can some one please advice on the following:
Which annotation can I use in place of #FormParam?
What can I use in place of Response.ok().build()?
Thank you

#FormParam -> #RequestParam
Response.ok -> ResponseEntity.ok
#PostMapping(value = "/add")
public ResponseEntity receive(#RequestParam("id") String id) {
return ResponseEntity.ok().build();
}

Related

FormUrlEncoded POST request, I need to convert snake case values into camelCase with SpringBoot and Jackson

I am integrating with a third-party's vendor API.
I have a SpringBoot and Jackson setup
They are sending me a POST request that is of type formUrlEncoded and with the params in snake_case
(over 10 params in total and no body)
e.g.
POST www.example.com?player_id=somePlayerId&product_id=someProductId&total_amount=totalAmount...
There are many out of the box helpers for JSON but I cannot find any for formUrlEncoded (I hope I am missing something obvious).
I have tried #ModelAttribute and #RequestParam but had no luck.
I am trying to avoid the #RequestParam MultiValueMap<String, String> params + custom mapper option
#RequestParam is the simplest way which allows you to define the exact name of the query parameter something like:
#PostMapping
public String foo(#RequestParam("player_id") String playerId){
}
If you want to bind all the query parameters to an object , you have to use #ModelAttribute. It is based on the DataBinder and is nothing to do with Jackson. By default it only supports binding the query parameter to an object which fields have the same name as the query parameter. So you can consider to bind the query paramater to the following object :
public class Request {
private String player_id;
private String product_id;
private Long total_amount;
}
If you really want to bind to the object that follow traditional java naming convention (i.e lower camel case) from the query parameter that has snake case values , you have to cusomtize WebDataBinder.
The idea is to override its addBindValues() and check if the query parameter name is in snake case format , convert it the lower camel case format and also add it as the bind values for the request. Something like :
public class MyServletRequestDataBinder extends ExtendedServletRequestDataBinder {
private static Converter<String, String> snakeCaseToLowerCamelConverter = CaseFormat.LOWER_UNDERSCORE
.converterTo(CaseFormat.LOWER_CAMEL);
public MyServletRequestDataBinder(Object target) {
super(target);
}
public MyServletRequestDataBinder(Object target, String objectName) {
super(target, objectName);
}
#Override
protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) {
super.addBindValues(mpvs, request);
Enumeration<String> paramNames = request.getParameterNames();
while (paramNames != null && paramNames.hasMoreElements()) {
String paramName = paramNames.nextElement();
if(paramName.contains("_")) {
String[] values = request.getParameterValues(paramName);
if (values == null || values.length == 0) {
// Do nothing, no values found at all.
} else if (values.length > 1) {
mpvs.addPropertyValue(snakeCaseToLowerCamelConverter.convert(paramName), values);
} else {
mpvs.addPropertyValue(snakeCaseToLowerCamelConverter.convert(paramName), values[0]);
}
}
}
}
}
P.S I am using Guava for helping me to convert snake case to lowerCamelCase.
But in order to use the customized WebDataBinder , you have to in turn customize WebDataBinderFactory and RequestMappingHandlerAdapter because :
customize WebDataBinderFactory in order to create the customised WebDataBinder
customize RequestMappingHandlerAdapter in order to create the WebDataBinderFactory
Something like:
public class MyServletRequestDataBinderFactory extends ServletRequestDataBinderFactory {
public MyServletRequestDataBinderFactory(List<InvocableHandlerMethod> binderMethods,
WebBindingInitializer initializer) {
super(binderMethods, initializer);
}
#Override
protected ServletRequestDataBinder createBinderInstance(Object target, String objectName,
NativeWebRequest request) throws Exception {
return new MyServletRequestDataBinder(target, objectName);
}
}
and
public class MyRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter {
#Override
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
throws Exception {
return new MyServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}
}
And finally register to use the customised RequestMappingHandlerAdapter in your configuration :
#Configuration
public class Config extends DelegatingWebMvcConfiguration {
#Override
protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
return new MyRequestMappingHandlerAdapter();
}
}
I don't think you are missing anything. Looking at the RequestParamMethodArgumentResolver#resolveName source I do no see a way to customize how a request parameter is matched. So it looks either you have to implement your own resolver or just annotate each parameter with #RequestParam and provide the name, e.g. #RequestParam("product_id") String productId
EDIT:
As for ModelAttribute, ModelAttributeMethodProcessor uses WebDataBinder. Again you can customize it with your custom DataBinder but I didn't found any that out of the box supports aliases as Jackson does.

Does Springboot #RequestParam support List<Object> params in get request

Springboot #RequestParam annotation can pass basic list parameters, just like:
#GetMapping("param")
public String requestParamDemo(#RequestParam("list")List<Long> list) {
System.out.println(list.toString());
return list.toString();
}
and in postman, GET request localhost:8998/param?list=1,3,100 is works, "1,3,100" can be converted to List, but how or if Springboot #RequestParam support custom Generics such as below:
#GetMapping("objlist")
public String paramWithObjList(#RequestParam("objList")List<AaParam> objList) {
System.out.println("objList = " + objList);
return objList.toString();
}
import lombok.Data;
#Data
public class AaParam {
private int id;
private String name;
}
postman request: GET url: localhost:8998/objlist?objlist=[{id: 1, name: "aa"},{id: 2, name: "bb"}]
I tested in local and it didn't work.
Want to know if #RequestParam can do that or any alternative way to implement it.
Thanks!
Hope this suggestion holds good for your requirement.
I would suggest going with #RequestBody code will look like this
#PostMapping(path = "/objlist", consumes = "application/json", produces = "application/json")
public String paramWithObjList(#RequestBody List<AaParam> objList) {
System.out.println("objList = " + objList);
return objList.toString();
}
Note: please add some ObjectPaser in your actual logic (for example Jackson )
Postman request would be like this

discussion about #RequestParam and #RequestBody

I have a class:
public class user{
private String id;
private MultiPartFile file;
**Getters And Setters**
}
And in the Controller:
#PostMapping(value="/upload)
public void upload(User user){
}
In the front end I post data with form-data.I can get the user object.
But when I add #RequestBody and #RequestParam,it can't works.
in my opinion,#RequestParam is used to binding parameter to simple class . when I use #RequestBody ,spring will find HttpMessageConverter to convert http request body to class.But I'm not sure about that.Does anyone can explain to me?
So, I believe we are talking about org.springframework.web.multipart.MultipartFile, which is to be used together with #RequestParam variable. The mechanism is somewhat special in this case.
I had a similar problem, and what I ended up using was org.springframework.web.multipart.commons.CommonsMultipartResolver. From frontend I've constructed multipart request with two parts, in your scenario it could be user (containing just JSON data) and file (containing the file itself), e.g.:
#PostMapping(value="/upload")
public void upload(#RequestParam("user") User user, #RequestParam("file") MultipartFile file){
...
}
But then, you need to configure custom serialization of the User part, which can be done using org.springframework.web.multipart.commons.CommonsMultipartResolver. You can configure it using bean config like this:
#Configuration
public class MappingConfig {
#Order(Integer.MIN_VALUE)
#Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}
#Bean
public Converter<String, User> stringToUser() {
return new Converter<String, User>() {
#Override
public User convert(String jsonString) {
return new Gson().fromJson(jsonString, User.class);
}
};
}
...
}
Also, as you can see I am using Gson manually, I couldn't find a better way how to do it. Also, it doesn't play with Java 8 lambdas, so it cannot be shortened (because of explicit types are needed for it to work).
I hope that this will at least points you to a right path.

How to specify multiple parameters in POST method

I have Model called Loan:
public class Loan {
private int loan_id;
private String clientName;
private String clientSurname;
private Double amount;
private int days;
//getters and setters
}
And Controller
#RestController
public class MyController {
#Autowired
MyService myService;
#RequestMapping(value = "/makeAction",method = RequestMethod.POST)
public String makeLoan(){
return myService.makeAction(...);
}
}
The question is: how to bypass multiple variables via adressbar like:
localhost:8080/makeAction?loanId=1#clientName=Stive#clientSurname=Wassabi
and so on.
UPD: Another attempt failed:
#RequestMapping(value="/makeLoan",method = RequestMethod.GET)
public String makeLoan(#PathVariable("loan_id")int loan_id,
#PathVariable("name") String clientName,
#PathVariable("surname") String clientSurname,
#PathVariable("amount") double amount,
#PathVariable("days") int days ) throws Exception {
return myService.makeLoan(loan_id,clientName,clientSurname,amount,days);
P.S tried #PathVariables - failed to use
Thanks you all for helping me with this
The final code looks like that:
#RequestMapping(value = "/makeAction")
public String makeLoan(#RequestParam("loan_id")int loan_id,
#RequestParam("clientName")String clientName,
#RequestParam("clientSurname")String clientSurname,
#RequestParam("amount")double amount,
#RequestParam("days")int days ) throws Exception {
return loanService.makeAction(loan_id,clientName,clientSurname,amount,days);
}
I had to remove GET/POST method and switch #PathVariable to #RequestParam
Well, first of all, you shouldn't put parameters for POST in the URL.
URL parameters are used for GET, and they are separated with & so in your case:
localhost:8080/makeAction?loanId=1&clientName=Stive&clientSurname=Wassabi
For POST you should submit parameters as request body parameters. Parameters are bound with #RequestParam annotation like #SMA suggested.
In your method define them with RequestParam annotation like:
public String makeLoan(#RequestParam(value="clientName", required=false) String clientName) {//and others, and hope you meant & to seperate request parameters.
}
Well, assuming you're using spring MVC, this could be helpful:
How to explictely obtain post data in Spring MVC?
Be aware that if you're using a POST method, your parameters should be read in the request body...

Spring MVC #RequestParam a list of objects

I want to create a page where a person sees a list of users and there are check boxes next to each of them that the person can click to have them deleted.
In my MVC that consumes a REST API, I want to send a List of User objects to the REST API.
Can the #RequestParam annotation support that?
For example:
#RequestMapping(method = RequestMethod.DELETE, value = "/delete")
public #ResponseBody Integer delete(
#RequestParam("users") List<Users> list) {
Integer deleteCount = 0;
for (User u : list) {
if (u != null) {
repo.delete(u);
++deleteCount;
}
}
return deleteCount;
}
In the MVC client, the url would be:
List list = new ArrayList<User>();
....
String url = "http://restapi/delete?users=" + list;
Request parameters are a Multimap of String to String. You cannot pass a complex object as request param.
But if you just pass the username that should work - see how to capture multiple parameters using #RequestParam using spring mvc?
#RequestParam("users") List<String> list
But I think it would be better to just use the request body to pass information.
Spring mvc can support List<Object>, Set<Object> and Map<Object> param, but without #RequestParam.
Take List<Object> as example, if your object is User.java, and it like this:
public class User {
private String name;
private int age;
// getter and setter
}
And you want pass a param of List<User>, you can use url like this
http://127.0.0.1:8080/list?users[0].name=Alice&users[0].age=26&users[1].name=Bob&users[1].age=16
Remember to encode the url, the url after encoded is like this:
http://127.0.0.1:8080/list?users%5B0%5D.name=Alice&users%5B0%5D.age=26&users%5B1%5D.name=Bob&users%5B1%5D.age=16
Example of List<Object>, Set<Object> and Map<Object> is displayed in my github.
Just a reminder, any List of custom objects might require custom converters to be registered, like:
#Bean
public Converter<String, CustomObject> stringToCustomObjectConverter() {
return new Converter<>() {
#Override
public CustomObject convert(String str) {
return new ObjectMapper().readValue(str, CustomObject.class);
}
};
}
#Bean
public Converter<String, List<CustomObject>> stringToListCustomObjectConverter() {
return new Converter<>() {
#Override
public List<CustomObject> convert(String str) {
return new ObjectMapper().readValue(str, new TypeReference<>() {
});
}
};
}
So you can cover custom cases like:
/api/some-api?custom={"name":"Bla 1","age":20}
/api/some-api?custom={"name":"Bla 1","age":20}&custom={"name":"Bla 2","age":30}
/api/some-api?custom=[{"name":"Bla 1","age":20},{"name":"Bla 2","age":30}]
where: #RequestParam("custom") List customObjects

Resources