PUT: PathVariable and RequestParam don't work together - java

I'm using Spring boot 2.1.3-RELEASE. In my RestController I'm trying to set up a PUT method with one PathVariable and a RequestParam (application/x-www-form-urlencoded).
However when I call it the response is a bad request because the required RequestParam is not present.
I tried changing PutMapping to RequestMapping, swapping parameters position and using the syntax #RequestParam(value="param2", required=false) but nothing changes.
Curiously using PostMapping works. Also removing PathVariable works.
Here is the RestController code:
#PutMapping(value="/myurl/{param1}", consumes=MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String myMethod( #PathVariable("param1") Integer param1, #RequestParam("param2") String param2);
I call the method in this way:
curl -X PUT \
http://localhost:8080/myurl/42 \
-H 'Accept: application/json' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'param2=myparam2value'
The response is:
{
"timestamp": 1553613278534,
"status": 400,
"error": "Bad Request",
"message": "Required String parameter 'param2' is not present",
"path": "/myurl/42"
}
I expect that PUT works just like POST, but it seems not to.
Unfortunately I cannot send parameters as QueryParam, so I should maintain the same request call because I am refactoring an existing endpoint that works exactly this way.
Thanks
EDIT
I found this is caused by using an HandlerInterceptorAdapter (via WebMvcConfigurer).
For some reason, around
org.springframework.web.util.ContentCachingRequestWrapper.getParameterValues
org.apache.coyote.Request.parameters has no content and an exception is thrown, so it works only for POST and not for PUT (GET are handled differently).
I appreciate if someone can suggest if this can be reported as a bug considering that removing the interceptor made it work.
Regards

Use -G along with --data-urlencode:
curl -G -X PUT \
http://localhost:8080/myurl/42 \
-H 'Accept: application/json' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'param2=myparam2value'
From the documentation:
-G, --get
When used, this option will make all data specified with -d, --data, --data-binary or --data-urlencode to be used in an HTTP GET request instead of the POST request that otherwise would be used. The data will be appended to the URL with a ? separator. [...]
--data-urlencode <data>
(HTTP) This posts data, similar to the other -d, --data options with the exception that this performs URL-encoding. [...]

Related

Missing request header 'client_id' for method parameter of type String

While doing a post request to the Spring Boot Rest API it is throwing Servlet Request Binding Exception with error message "Missing request header 'client_id' for method parameter of type String". Even after passing the client_id field in header.
It is happening in only one of our testing env. where request header is containing "_" underscore in its name. Same code is working in local. What could be the issue here. Is it something related to deployment file or code related issue.
curl --location --request POST 'http://localhost:7087/test' \
--header 'accept: application/json' \
--header 'client_id: test' \
--header 'Content-Type: application/json' \
--data-raw '{
"data": "test"
}'
#PostMapping(path = "/test", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public Object pushMsg(#Valid #RequestBody String data,
#RequestHeader("client_id") String clientId) {
return null;
}

How to annotate a Java List form-urlencoded parameter for Swagger?

I have this API endpoint which expects one form-urlencoded array parameter. This is the relevant Java snippet:
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.APPLICATION_JSON)
public Response addItems(#Parameter(description = "Items to add") #FormParam("items") List<Long> items) {
return service.addItems(items);
}
I can't reach the endpoint from the generated Swagger UI because of the following error:
RESTEASY003870: Unable to extract parameter from http request: javax.ws.rs.FormParam("items") value is '1%2C2%2C3'
From what I read, Swagger is making this request:
curl -X 'POST' \
'http://localhost:8080/items' \
-H 'accept: */*' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'items=1,2,3'
It looks to me that the culprit is how Swagger serializes the array: Swagger sends this items=1,2,3 while RESTEasy expect this items=1&items=2&items=3.
I've already read the relevant Swagger documentation and tried every style/explode combination, including the ones that looks to have most sense for me (style = ParameterStyle.SIMPLE, explode = TRUE which by the way should be default behavior) with no luck.
So, how should I annotate this endpoint in order to Swagger to be able to invoke it?
I used https://github.com/quarkusio/quarkus-quickstarts/tree/main/openapi-swaggerui-quickstart and added your method to FruitResource
According to swagger documentation if you want to use form urlencoded
application/x-www-form-urlencoded is used to send simple ASCII text data as key=value pairs. The payload format is similar to query parameters.
at https://swagger.io/docs/specification/describing-request-body/, so it says use query params and I changed your function to
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.APPLICATION_JSON)
public Response addItems(
#Parameter(description = "Items to add") #QueryParam(
"items"
) List<Long> items
) {
return service.addItems(items);
}
and open the dev-ui
As you can see UI is preventing to add wrong parameters, correct ui below
and created curl command is working as expected.
curl -X 'POST' \
'http://localhost:8080/fruits?items=1&items=2' \
-H 'accept: */*' \
-d ''
in case you want to test the code it is here.

Unable to have a method with application/json or multipart/form-data in SPRING java

I'm trying to create a method that either receives a JSON or a .csv file in order to populate a database. It goes like:
#RequestMapping(method = RequestMethod.POST,
consumes = {"multipart/form-data","application/json"})
public ResponseEntity<PeopleResponse> create(
#RequestBody People PeopleDTO,
#RequestParam(name = "csvFile", required = false) MultipartFile csvFile,
HttpServletRequest request)
When I try it with Postman with a json sending:
curl --location --request POST 'localhost:9001/people' \
--header 'Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJtcmVnYWxhZG8iLCJhdXRob3JpdGllcyI6eyJsb2dpbiI6IjEiLCJnb2RNb2RlIjoiMSIsInJlZ2lzdGVyIjoiMSJ9LCJzYWx0IjoxNTg5NTg2ODgwMzI1LCJzb3VyY2UiOiJsb2NhbGhvc3QifQ.DrdUC7JN-Db0-_6qNGn9i5n3YzVopGNisecLy08SvMCudmveZnwATRwMftf2VKBZqhUKdQIrJGsNXudU1uwTgA' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Homer",
"age": 25
}'
It works, but when I try it sending a csv file with Postman, it fails:
curl --location --request POST 'localhost:9001/people' \
--header 'Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJtcmVnYWxhZG8iLCJhdXRob3JpdGllcyI6eyJsb2dpbiI6IjEiLCJnb2RNb2RlIjoiMSIsInJlZ2lzdGVyIjoiMSJ9LCJzYWx0IjoxNTg5NTg2ODgwMzI1LCJzb3VyY2UiOiJsb2NhbGhvc3QifQ.DrdUC7JN-Db0-_6qNGn9i5n3YzVopGNisecLy08SvMCudmveZnwATRwMftf2VKBZqhUKdQIrJGsNXudU1uwTgA' \
--form 'csvFile=#/home/myself/Downloads/people.csv' \
It gives me the following error:
2020-10-05 15:09:20.602 WARN 13086 - [nio-9001-exec-3] s.w.s.h.AbstractHandlerExceptionResolver | Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;boundary=--------------------------276557520366487467828532;charset=UTF-8' not supported]
I'd like to know if it's possible to have either application/json or multipart/form-data in one method or if the only way is to make two separates methods (Though it ruins a little the naming, because second method would be like "POST people/by-file"

how can I consume an endpoint annotated with #Suspended using postman or curl?

I am testing some code that has an annotation I haven't used before and reading the documentation seems like I should not even send any parameter to call it, but I might.
The endpoint looks like this:
#POST
#Path("/hello")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public void postHello(
#Suspended final AsyncResponse asyncResponse,#QueryParam("dtd") String name, String obj) {
...
}
So by now I am using Postman or cURL and sending this:
curl -X POST \
'http://localhost:9000/hello?name=John' \
-H 'Accept: */*' \
-H 'Content-Type: application/json' \
-H 'Host: localhost:8080' \
-d '{}'
Unfortunately, I get an error 400 Bad Request
Any idea what header I am missing? or might be #Produces/#Consumes?

How can i get params in spring from curl -X request?

I have a problem with processing params from curl request to my spring-boot app.
My controller post method:
#RequestMapping(value = "/cat", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity addCat(#Valid #ModelAttribute NewCatRequest newCatRequest, BindingResult bindingResult) {
System.out.println( newCatRequest);
if (bindingResult.hasErrors()) {
return ResponseEntity.badRequest().body(bindingResult.getFieldError().getDefaultMessage());
}
int rowsAffected = catsRepository.saveCat(newCatRequest.getName(), newCatRequest.getColor(), newCatRequest.getTail_length(), newCatRequest.getWhiskers_length());
if (rowsAffected == 1) {
return ResponseEntity.ok().body(newCatRequest);
} else {
return ResponseEntity.badRequest().body("There was an unexpected error while trying to create cat for you :(");
}
}
And the problem is: when i'm trying to send this with curl:
curl -X POST http://localhost:8080/cat \
-d "{\"name\": \"Tihon\", \"color\": \"red & white\", \"tail_length\": 15, \"whiskers_length\": 12}"
I have all null params in 'newCatRequest':
NewCatRequest{name='null', color='null', tail_length=0, whiskers_length=0}
BUT when i'm trying to do the same with Postman (POST method, x-www-form-urlencoded in body with my params) i have a valid result:
Result from Postman
Help me pls to understand what's the problem.
curl -X POST http://localhost:8080/cat \
-d "{\"name\": \"Tihon\", \"color\": \"red & white\", \"tail_length\": 15, \"whiskers_length\": 12}"
The above curl request has a JSON body, whereas your request processing method
#RequestMapping(value = "/cat", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
consumes/accepts: application/x-www-form-urlencoded. Therefore, you should either convert your method to consume/accept application/json or change your curl request to:
curl -X POST http://localhost:8080/cat \
-d 'name=Tihon&color=red%20%26%20white&tail_length=15&whiskers_length= 12' \
-H 'Content-Type: application/x-www-form-urlencoded'
EDIT 1
Please note that the default Content-Type for curl is application/x-www-form-urlencoded. To use JSON, change your request to:
curl -X POST http://localhost:8080/cat \
-d "{\"name\": \"Tihon\", \"color\": \"red & white\", \"tail_length\": 15, \"whiskers_length\": 12}" \
-H 'Content-Type: application/json'
Try this:
curl -X POST \
http://localhost:8080/cat \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'name=Thidon&color=red%20%26%20white&tail_length=15&whiskers_length=12'
You forgot the header application/x-www-form-urlencodedand the body should not be in json format.
you can use the code option below send button in postman to generate the exact curl request which will work for you https://i.stack.imgur.com/hbk8M.png
in code drop down menu search for curl and it will generate a neat and clean curl request for you .
Hope this helps

Categories