Read query string parameters in Spring REST API (POST) - spring

My Spring REST API is decorated as follows:
In below, I am confused weather, parameters such as list, operation need to be part of Url as query string or do they need to be part of Request Body as form data (Url encoded).
There are situations where I am sending these parameters in query string and it works fine. But couple of my api's are not working properly on production and they work only if I send the data in request body as Url encoded. Can anyone help me explain this behaviour ?
#RequestMapping(value = "/bulkupdate/{companyId}", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<String> bulkupdateArticle(#RequestParam("list") String documentIdList,
#PathVariable("companyId") String companyId, #RequestParam("operation") String operation){
try{

Looking at the resource I find that it could be better designed in a more REST-ful fashion. I don't like to see POSTed data in the reside in the url.
Next to becoming more Rest-ful it would also make live for you much easier.
I would create a Data Transfer Object and pass it as the body of the POST request to your resource/spring controller.
Going from your data:
public class ArticleToUpdate {
private String list; // list of what ? Maybe design it like List<String> somethingMoreMeaningFull
private String operation;
// .. getters
}
public ResponseEntity<String> bulkupdateArticle(#RequestBody ArticleToUpdate articleToUpdate) {
// .. do whatever you need with the posted data
Now you can post a JSON or XML document in the body which will probably life much easier.
Additionally you could also add validation on the posted data through #Valid support now.

Related

How to display x-www-form-urlencoded format request with springdoc-openapi-ui

My environment
springboot:2.6.4
springdoc-openapi-ui:1.6.6
Probrem
I want to create an API definition to receive requests in x-www-form-urlencoded format.
Using #RequestBody will create a swagger-ui display with no Parameter notation, just the body. However, when receiving a request in x-www-form-urlencoded format, it is necessary to receive it with #RequestParam, and if this is done, the swagger-ui display is created as a query parameter.
#PostMapping(value = "/v1/hoge")
public ResponseEntity<SampleResponse> postProc(
#RequestParam("param1") int param1,
#RequestParam("param2") String param2,
#RequestParam("param3") String param3,
HttpServletRequest request) {
  ・・・
}
swagger-image
However, since there is no actual query parameter, I do not want to display the Parameter in the same way as when receiving a request with #RequestBody.
The display without parameters is correct, as in POST /user in the following link.
http://158.101.191.70:8081/swagger-ui/4.5.0/index.html#/user/createUser
Is there a solution to this problem?
I might have a solution for you:
#PostMapping(value = "/v1/hoge", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity<SampleResponse> postProc(
#RequestPart("param1") int param1,
#RequestPart("param2") String param2,
#RequestPart("param3") String param3,
HttpServletRequest request) {
  ・・・
}
In a perfect world, you could stick to the #RequestParam but I believe there is currently a bug with springdoc that make it impossible.

Is there a way to map a Retrofit #QueryMap to some object for a Spring Rest Service?

The client interface looks like this
#GET("v3/users/posts")
Call<User> loadPosts(#QueryMap Map<String,String> data);
The RestController should then process the map of query data returning the user's post. There are multiple parameters that can be put in the map as shown in the UserService.findUserPosts(). Is it possible to use a map to pass data to the Spring Rest controller? The restriction I have is this is inherited from code using #Query parameters but it has now grown to quite a number and a query map would limit the changes on the client. I would be really grateful for some feedback. Many thanks
#RestController
public class UsersController{
#RequestMapping(value = "/user/posts", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> getUserPosts(.......What here) {
List<Posts> posts = userService.findPostsBy(id,postKey,offset,when);
}
Solved it. For anyone in the future who wants to do what I am doing , simply provide Request parameters for every key pair in the map
i.e #RequestParam("id") String id,
#RequestParam("postKey") Long, #RequestParam("offset") etc.

how to apply post method to all request mappings in spring

How i can access to request POST data from different url-s to one controller method, for example I have /countries & /countries/{id} URL, It works very good with first one, because my code is
#RequestMapping(value = {"/{id}", "/{id}/"}, method = RequestMethod.GET)
public String getCountry(#PathVariable(value = "id", required = true) int id,ModelMap model) {
}
#RequestMapping(method = RequestMethod.POST)
public String deleteCountry(ModelMap model,HttpServletRequest request) {
}
And when I try to request POST data from second url I'm getting
HTTP Status 405 - Request method 'POST' not supported
Which is expectable because I haven't POST method with this mapping, but if I will be made one method for one mapping my code will be too ugly ant terrible, what solution I can use?
Hum why not add the "array" of value to your second method as well ?
#RequestMapping(value = {"", "/{id}"},method = RequestMethod.POST)
public String deleteCountry(ModelMap model,
HttpServletRequest request) {
Btw using POST verb to call an action that looks like it will delete the resource seems wrong, it should be a DELETE verb used
EDIT
But in reality, you should be creating 2 methods, as both those method are not supposed to do the same thing.
POST /countries should be creating country resources
POST /countries/{id} should be doing something else.
For an update prefer PUT /countries/{id}
And for a delete, prefer DELETE /countries/{id}
There is one way to have separate handler interceptors for different controllers.
Refer this link for details.
bind Spring HandlerInterceptor only to one controller
But I feel, it may be good you can create a common method to share business logic for this.
As Interceptor comes with proxy class for your controller which can be avoided unless you have complex logic.

Spring MVC form parameters and query parameters

In Spring 4 MVC, I'm trying to figure out how to accept both form parameters and query parameters posted to my rest endpoint and also trigger validation, but am struggling to figure out how to get this to work.
Ideally, my controller would look something like this:
#RequestMapping(value="/someurl", method=RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public someMethod(#Valid #ModelAttribute Order order, #RequestParam String parm1, #RequestParam String parm2) {
}
I need to post the form parameters to the controller URL along with some query parameters and a Content-Type = application/x-www-form-urlencoded
Any ideas on how to get this to work?
Suppose the Order class has the order_id and order_x attributes, with the following endpoint:
#RequestMapping(value="/someurl", method=RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public someMethod(#Valid #ModelAttribute Order order, BindingResult validation, #RequestParam String parm1, #RequestParam String parm2) {
}
If a client sends a POST request with application/x-www-form-urlencoded content type with following request body:
order_id=1&order_x=something&parm1=foo&parm2=bar
then an Order instance with {order_id=1, order_x=something} will be instantiated. In addition, parm1 and parm2 will contain foo and bar, respectively. BindingResult contains validation results for a preceding command or form object (the immediately preceding method argument).
See this discussion about Query Parameters in POST requests. Also Forms Spec is a good reference.

Global param on multiple webresources

I am building a webservices application using jersey,jax-rs
I have single jax-rs resource file at path "/authenticate"
I have multiple methods with individual paths like "/user" "/test"
#Path ("/authenticate")
public class Authenticate{
private static final Log log = LogFactory.getLog(Authenticate.class);
#QueryParam("entityId")
String entity;
#GET
#Path ("/{param}")
public Response getMsg(#PathParam ("param") String msg) {
String o = "Hello Welcome Back:"+msg;
return Response.status(200).entity(o).build();
}
#GET
#Path ("/user")
#Produces({"application/json"})
public UserDTO getUser (#Context HttpServletRequest request,
#QueryParam("userId") int userId) {
System.out.println("In Get User, User:"+userId);
System.out.println("In Get User, Entity:"+entity);
}
#GET
#Path ("/test")
#Produces({"application/json"})
public TestPOJO getTestPOJO () {
System.out.println("In Get TestPOJO");
System.out.println("In Get Test, Entity:"+entity);
return new TestPOJO();
}
}
As suggested for jersey client, I am using a single webresource from client and build subsequent webresources from the same webresource by using .path("/xxx").
Here is how I create the initial web resource
WebResource webResource = client.resource("http://localhost:8080/Service/jaxrs/authenticate");
webResource.queryParam("entityId", securityHelper.getEntityId().toString());
Here is how I use the webresource subsequently
MultivaluedMap<String, String> params = new MultivaluedMapImpl();
ClientResponse userRes = webResource.path("/user").queryParams(params).accept("application/json").get(ClientResponse.class);
I want to assign a queryparam to the initial webresource, and I want that to be retained by all subsequent webresources created using the .path(). But that is not happening right now. For example in the above code "entityId" is not available when the call with path("/user") is made.
My idea is to assign common parameters once and all subsequent users of the webResource need not add those again and again. Is there a way to do it? Will this approach work?
The line below creates a new WebResource and not changing the state of the webResource object:
webResource.queryParam("entityId", securityHelper.getEntityId().toString())
Eventually you could change your code like this to create the "base" resource:
WebResource webResource = client.resource("http://localhost:8080/Service/jaxrs/authenticate").queryParam("entityId", securityHelper.getEntityId().toString());
And then use this resource to create another resources as you like. WebResource.queryParam and WebResource.queryParams always create a new WebResource object.
I'm may not be the best person to answer this since I have entered the "world" of Jersey and RESTful servers not too long ago but since i saw this unanswered for 2 days ill try to help out as best as I can.
If i understood correctly you are trying to, by using a query, save the user information on entityId String so it will be available when you make a subsequent call.
Ok let's start with what you have. With your code (entityId as a global parameter), what you are specifying , is that when you are calling a resource from the Authenticate class, any call can be made with a query of the type '?entityId="something" and ANY method in this class can use the information sent in the query.
The thing is, for what I've learned by messing about with Jersey, whenever you make a call, the resource class (in your case Authenticate) is instantiated again. Therefor you can't just keep information in a global parameter since subsequent calls will have the String entityId as null.
This means that if you want to save the information you'll have to do it in a external resource (ex: db, file, etc). What method you choose depends on what you want to do and what are you looking for in your application.
I hope I was at least able to shed a sliver of light on your problem.

Resources