Can spring map POST parameters by a way other than #RequestBody - spring

I am using #RestControllers with an application where all requests are POST requests ... As I learned from this post , you can't map individual post parameters to individual method arguments, rather you need to wrap all the parameters in an object and then use this object as a method parameter annotated with #RequestBody thus
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestParam(value="idNumber") String idNumber , #RequestParam(value="applicationId") String applicationId) {
return customerService.requestOTP(idNumber, applicationId);
will not work with a POST request of body {"idNumber":"345","applicationId":"64536"}
MY issue is that I have A LOT of POST requests , each with only one or two parameters, It will be tedious to create all these objects just to receive the requests inside ... so is there any other way similar to the way where get request parameters (URL parameters) are handled ?

Yes there are two ways -
first - the way you are doing just you need to do is append these parameter with url, no need to give them in body.
url will be like - baseurl+/requestotp?idNumber=123&applicationId=123
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestParam(value="idNumber") String idNumber , #RequestParam(value="applicationId") String applicationId) {
return customerService.requestOTP(idNumber, applicationId);
second- you can use map as follows
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestBody Map<String,Object> body) {
return customerService.requestOTP(body.get("idNumber").toString(), body.get("applicationId").toString());

I have change your code please check it
DTO Class
public class DTO1 {
private String idNumber;
private String applicationId;
public String getIdNumber() {
return idNumber;
}
public void setIdNumber(String idNumber) {
this.idNumber = idNumber;
}
public String getApplicationId() {
return applicationId;
}
public void setApplicationId(String applicationId) {
this.applicationId = applicationId;
}
}
Rest Controller Method
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestBody DTO1 dto){
System.out.println(dto.getApplicationId()+" (------) "+dto.getIdNumber());
return "";
}
Request Type -- application/json
{"idNumber":"345","applicationId":"64536"}
OR
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestBody String dto){
System.out.println(dto);
return "";
}

Related

MockMVC #PathVariable not work when using String

Mockmvc test not work when Pathvariable is a String but work on int.
Here is my code:
#Controller
#RequestMapping("/etablissement/{typeEtablissement}")
public class EtablissementController{
#RequestMapping(method = RequestMethod.GET)
public String accueil(#PathVariable("typeEtablissement") String typeEtablissement) {
return "/test";
}
}
// somewhere in my test
mockMvc.perform(get("/etablissement/{typeEtablissement}", "test")).andExpect(status().isOk()); // Error 400
But, if I use int instead of String it works
#RequestMapping(method = RequestMethod.GET)
public String accueil(#PathVariable("typeEtablissement") int typeEtablissement) {
return "/test";
}
// somewhere in my test
mockMvc.perform(get("/etablissement/{typeEtablissement}", 123)).andExpect(status().isOk()); // Works
Using Object also work
public String accueil(#PathVariable("typeEtablissement") Object typeEtablissement) {}
Thanks for your help !
Here is an example of a GetMapping who should help you :
#GetMapping("/{typeEtablissement}/{something2}/{something3}")
public List<Object> extract(
#PathVariable String typeEtablissement,
#PathVariable String something2,
#PathVariable String something3) {
List<Object> object = objectService.extractDatas(typeEtablissement, something2, something3);
return object;
}
Strange cause it means typeEtablissement is an int. You can also create a new variable after with valueOf:
#GetMapping("/{typeEtablissement}/{something2}/{something3}")
public List<Object> extract(
#PathVariable String typeEtablissement,
#PathVariable String something2,
#PathVariable String something3) {
String typeEtablissementConverted = String.valueOf(typeEtablissement );
List<Object> object = objectService.extractDatas(typeEtablissementConverted, something2, something3);
return object;
}

Spring Boot - How can I pass custom values in HTTP Post api?

I'm new with Spring Boot and I have difficult to understand how can I pass data. For example:
I want pass those data to my server:
{
"code", 1,
"name": "C01"
}
So I have create always a custom Object with code and name as attributes to have this http post api?
#RequestMapping(value = "/new/", method = RequestMethod.POST)
public ResponseEntity<?> createOrder(#RequestBody CustomObject customObject){
...
}
Another solution I see that can be this but I can't pass numbers (int code), right?
#RequestMapping(value = "/new/{code}/{name}", method = RequestMethod.POST)
public ResponseEntity<?> createOrder(#PathVariable("code") int code, #PathVariable("name") String name) {
...
}
Kind regards :)
You can pass code and name as PathVariables just like in your example:
#RequestMapping(value = "/new/{code}/{name}")
public ResponseEntity<?> createOrder(#PathVariable("code") int code, #PathVariable("name") String name) {
...
}
A PathVariable can be an int or a String or a long or a Date, according to the docs:
A #PathVariable argument can be of any simple type such as int, long, Date, etc. Spring automatically converts to the appropriate type or throws a TypeMismatchException if it fails to do so.
You could also define a PathVariable of type Map<String, Object> like this:
#RequestMapping(value = "/new/{code}/{name}")
public ResponseEntity<?> createOrder(#PathVariable("map") Map<String, Object> map) {
Integer code = (Integer) map.get("code");
String name = (String) map.get("name");
...
}
You could even use #RequestParam and supply the data in the form of URL query parameters.
So, there are numerous ways in which data can be passed to a Spring MVC controller (more details in the docs) but I think the convention for posting complex data (by "complex" I mean more than a single piece of state) is to define a request body which contains a serialised form of that complex state i.e. what you showed in the first example in your queston:
#RequestMapping(value = "/new/", method = RequestMethod.POST)
public ResponseEntity<?> createOrder(#RequestBody CustomObject customObject){
...
}
If this question is about RESTful best practice, since you are developing webservice for creating an Order object, this is how I would design it
Order.java
public class Order {
private Integer code;
private String name;
public Integer getCode() {
return code;
}
public void setCode(final Integer code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
}
Controller
#RequestMapping(value = "/orders", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<Order> createOrder(#Valid #RequestBody Order order){
...
}
Technically, you can do many things to achieve the same thing, but that will not be a RESTful service, it will be an RPC at best.

spring boot : RequestMapping

I have following three REST API methods :
#RequestMapping(value = "/{name1}", method = RequestMethod.GET)
public Object retrieve(#PathVariable String name1) throws UnsupportedEncodingException {
return configService.getConfig("frontend", name1);
}
#RequestMapping(value = "/{name1}/{name2}", method = RequestMethod.GET)
public Object retrieve(#PathVariable String name1, #PathVariable String name2) throws UnsupportedEncodingException {
return configService.getConfig("frontend", name1, name2);
}
#RequestMapping(value = "/{name1}/{name2}/{name3}", method = RequestMethod.GET)
public Object retrieve(#PathVariable String name1, #PathVariable String name2, #PathVariable String name3) {
return configService.getConfig("frontend", name1, name2,name3);
}
getConfig method is configured to accept multiple parameters like:
public Object getConfig(String... names) {
My Question is : is it possible to achieve the above RequestMapping using only one method/RequestMapping ?
Thanks.
Simple approach
You can use /** in your mapping to grab any URL and then extract all parameters from the mapping path. Spring has a constant which allows you to fetch the path from the HTTP request. You just have to remove the unnecessary part of the mapping and split the rest to get the list of parameters.
import org.springframework.web.servlet.HandlerMapping;
#RestController
#RequestMapping("/somePath")
public class SomeController {
#RequestMapping(value = "/**", method = RequestMethod.GET)
public Object retrieve(HttpServletRequest request) {
String path = request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).toString();
String[] names = path.substring("/somePath/".length()).split("/");
return configService.getConfig("frontend", names);
}
}
Better approach
However, path variables should be rather used for identifying resources in your application and not as a parameters to a given resource. In that case, it is advised to stick with simple request parameters.
http://yourapp.com/somePath?name=value1&name=value2
You mapping handler would look much more simple:
#RequestMapping(method = RequestMethod.GET)
public Object retrieve(#RequestParam("name") String[] names) {
return configService.getConfig("frontend", names);
}
You should probably use #RequestParam instead and method POST in order to achieve what you want.
#RequestMapping(name = "/hi", method = RequestMethod.POST)
#ResponseBody
public String test(#RequestParam("test") String[] test){
return "result";
}
And then you post like that:
So your array of Strings will contain both values
Also in REST a path corresponds to a resource, so you should ask yourself "what is the resource i am exposing ?". It would probably be something like /config/frontend and then you specify your options through request params and/or HTTP verbs
You can retrieve the complete path with request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE) and then parse it to get all the values.
This should work:
#SpringBootApplication
#Controller
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#RequestMapping(value ={"/{name1}","/{name1}/{name2}","/{name1}/{name2}/{name3}"})
public #ResponseBody String testMethod(
#PathVariable Map<String,String> pathvariables)
{
return test(pathvariables.values().toArray(new String[0]));
}
private String test (String... args) {
return Arrays.toString(args);
}
}

Influence on performance of using 'private static final' strings in Spring 3 REST controller

I'm working on REST API based on Spring 3 MVC. In each call I'm adding to JSON response two variables: 'description' and 'result'.
For example:
#RequestMapping(value = "entity.htm", method = RequestMethod.GET)
public ModelAndView get() {
ModelAndView mav = new ModelAndView(JSON_VIEW);
mav.addObject("description", "entity list");
mav.addObject("result", someService.getAll());
return mav;
}
Does it make sense for performance of the app to create a pool of private static final strings and use them every time I need?
I mean like this:
#Controller
public class MyController {
private static final String JSON_VIEW = "jsonView";
private static final String VAR_DESCRIPTION = "description";
private static final String VAR_RESULT = "result";
private static final String DESC_CREATED = "entity created";
private static final String DESC_ENTITY_LIST = "entity list";
private static final String DESC_ACCESS_DENIED = "forbidden";
#RequestMapping(value = "entity.htm", method = RequestMethod.PUT)
public ModelAndView put(HttpServletResponse response) {
ModelAndView mav = new ModelAndView(JSON_VIEW);
if (!entityService.someChecking()) {
mav.addObject(VAR_DESCRIPTION, DESC_ACCESS_DENIED);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
} else {
mav.addObject(VAR_DESCRIPTION, DESC_CREATED);
mav.addObject(VAR_RESULT, entityService.save(new Entity()));
response.setStatus(HttpServletResponse.SC_CREATED);
}
return mav;
}
#RequestMapping(value = "entity.htm", method = RequestMethod.GET)
public ModelAndView get(HttpServletResponse response) {
ModelAndView mav = new ModelAndView(JSON_VIEW);
if (!entityService.someChecking()) {
mav.addObject(VAR_DESCRIPTION, DESC_ACCESS_DENIED);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
} else {
mav.addObject(VAR_DESCRIPTION, DESC_ENTITY_LIST);
mav.addObject(VAR_RESULT, entityService.getAll());
}
return mav;
}
// and so on
}
Someone of these statuses I use only once, but DESC_ACCESS_DENIED I use up to 10 times in one REST controller.
Your get is not returning json, it returns a view.
I prefer using an enum instead of static final ints - easier to add functionality later.
Yes, it does make sense. It's a good pratice. It save's you time and effort if you ever need to change this values. It's quite insignificant in terms of memory use or process time, but it's better.
If you intend to use those strings more than once, then it is a good pratice to turn then into static final. But notice your methods aren't returning JSON responses. A JSON response is something like that:
#RequestMapping(value = "/porUF", method = RequestMethod.GET)
public #ResponseBody List<Municipio> municipios(
#RequestParam(value = "uf", required = true) String uf) {
if ( uf.length() != 2) {
return null;
}
return municipioBO.findByUf(uf);
}
The #ResponseBody annotation will transform the List into a JSON object, and the response of a HTTP GET for that is something like that:
[{"codigo":9701,"uf":{"uf":"DF","nome":"DISTRITO FEDERAL"},"nome":"BRASILIA "}]
This is a JSON response.

create two method for same url pattern with different arguments

I have scenario where one url "serachUser" may come with two different value (request parameter) userId or UserName.
so for this I have created two methods
public String searchUserById(#RequestParam long userID, Model model)
public ModelAndView searchUserByName(#RequestParam String userName)
But i am getting Ambiguous mapping found exception. Can Spring handle this situation?
You can use the params parameter to filter by HTTP parameters. In your case it would be something like:
#RequestMapping(value = "/searchUser", params = "userID")
public String searchUserById(#RequestParam long userID, Model model) {
// ...
}
#RequestMapping(value = "/searchUser", params = "userName")
public ModelAndView searchUserByName(#RequestParam String userName) {
// ...
}
Any way incase of request param null is allowed if you don't pass any value it will be null then you can write your coad like:
#RequestMapping(value = "/searchUser", params = {"userID","userName"})
public String searchUserById(#RequestParam long userID,#RequestParam String
userName,
Model model) {
if(userID != null){
//..
}else{
// ...
}

Resources