I have a spring endpoint that looks like this:
#PostMapping(path = "/test",
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE})
#ResponseStatus(HttpStatus.OK)
public TestDTO test(TestDTO testDTO){
return testDTO;
}
with the DTO looking like this:
public class TestDTO {
#ApiModelProperty(
name = "sample",
value = "sample",
dataType = "String",
example = "shhhhhh"
)
private String sample;
}
so obviously, a curl request like this:
curl --location --request POST 'localhost:8081/test' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'sample=testReturnData'
returns
{
"sample": "testReturnData"
}
But, if I add a parameter like this
curl --location --request POST 'localhost:8081/test?sample=doNotIncludeThis' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'sample=testReturnData'
I get a response back like this:
{
"sample": "doNotIncludeThis,testReturnData"
}
I only want spring to grab data from the body, not the param. I want the response to look like
{
"sample": "testReturnData"
}
The #RequestBody annotation does not work with x-www-form-url-encoded format (which is the format I need), because it will return a 415.
I think you are looking for the wrong solution. Why do you get a 415 when you use #RequestBody? This is not standard behavior.
You see, Spring doesn't recognize the body of a request when application/x-www-form-urlencoded is used. The Spring documentation states:
The spring-web module provides FormContentFilter to intercept HTTP PUT, PATCH, and DELETE requests with a content type of application/x-www-form-urlencoded, read the form data from the body of the request, and wrap the ServletRequest to make the form data available through the ServletRequest.getParameter*() family of methods.
In order to configure Spring properly, you need to configure Spring's FormContentFilter. This should be enabled by default, but you can make sure it is by setting it in the application.properties.
spring.mvc.formcontent.putfilter.enabled=true
Related
I have an API that only accepts requests with application/x-www-form-urlencoded content type.
Request Curl:
curl --location --request POST 'http://localhost:8181/inquiry' \
--header 'deviceid: 6' \
--header 'accept: application/json, text/plain, */*' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'token=03AGdBq253vhAAyXa00DAUcAPbZHD8oAW9FTeYS3r3mKQtxHCpTHv-BxaVcX3AVNcsG41KeyjdDXiJfOxtwCHMVif4yohWJ' \
--data-urlencode 'plate=ABCDEFG' \
--data-urlencode 'type=1' \
--data-urlencode 'reason_id=1'
Controller:
#PostMapping(path = "/inquiry")
public InsuranceInformationDto inquiryByPlate(
#ModelAttribute InquiryRequestDto inquiryRequestDTO,
#RequestHeader(value = "deviceid") Integer deviceId,
) { ... }
And the Dto:
#Data
public class PlateInquiryRequestDto {
private String plate;
#JsonProperty("type")
private Boolean hasNaj;
#JsonProperty("reason_id")
private Integer reasonId;
private String userToken;
private Integer deviceId;
private String token;
}
after running that code, plate and token fill in the dto, but hasNaj and reasonId are null. there is no issue if the request parameter name matches the dto property name. but #JsonProperty does not work for mapping parameters with different names.
I checked some other scenarios. when I change the request content type to json and use RequestBody this will work properly but I have to use form-urlencoded.
Also, the result is the same with and without ModelAttribute. I used #SerializedName instead of #JsonProperty but it didn't work.
I found a similar question in spring-mvc, and an answer said there isn't any annotation-based approach to solve this problem but I think spring boot maybe has a solution for me.
I need to receive a csv file on the request and process it.
#PostMapping(value = "/csv", consumes = "text/csv")
public ViewObject postCsv(#RequestBody InputStream request){
// do stuff
}
But when I execute:
curl -X POST -H 'Content-Type: text/csv' -d #input.csv http://localhost:8080/csv
This is what shows up on my console:
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/csv;charset=UTF-8' not supported]
Spring is saying my request is invalid before anything else.
So, the question is: How to properly receive this csv?
As someone mentioned in comments, one approach is to implement your own HttpMessageConverter - there are examples of varying quality available on the web.
Another option if you already have some code for parsing CSVs using Spring's org.springframework.core.io.Resource classes is to change your method signature as follows to use Spring's built in ResourceHttpMessageConverter:
#PostMapping(path = "/csv", consumes = "application/octet-stream")
public ViewObject postCsv(#RequestBody Resource resource){
// do stuff
}
I have JAX-RS 2.8.9 with Spring 4.3.4 app. I perform a very simple POST request to the following server code
#POST
#Consumes({MediaType.APPLICATION_FORM_URLENCODED})
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response test(MultivaluedMap<String, String> work) {
return Response.ok(work.keySet().size()).build();
}
I test with curl:
curl -i -X POST 'http://localhost:XXX/some/test' -d "param=value¶m2=value2" -H "Content-Type: application/x-www-form-urlencoded"
I get the following warning
A servlet request to the URI http://localhost:XXX/some/test contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using #FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.
About which I found only cause that involve connection issues, apparently I don't have.
According to documentation this is the way to handle a case when we have a variable number of FormParams passed.
This works, though.
#POST
#Consumes({MediaType.APPLICATION_FORM_URLENCODED})
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response test(#FormParam("param") String param) {
return Response.ok(param).build();
}
What can be the reason the multivalued map doesn't? Could it be some filtering? What is an alternative for unknown number of parameters?
UPDATE
Is is due to a particularity of Jersey + Spring.
A solution can be found in this answer.
What can be the reason the multivalued map doesn't? Could it be some filtering? What is an alternative for unknown number of parameters?
By default, it seems your JAX-RS implementation is detecting the form-input and reading/processing the body before it gets to your method. Have you tried:
#POST
#Consumes({MediaType.APPLICATION_FORM_URLENCODED})
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response test(Form workForm) {
MultivaluedMap<String,String> work = workForm.asMap();
return Response.ok(work.keySet().size()).build();
}
?
Form is a special JAX-RS class that encapsulates all the form parameters and should be usable as an input parameter to your method.
In my Spring Boot 2 REST with HATEOAS application, my /users endpoint produces application/hal+json and my /users2 endpoint produces application/json. I'd prefer to have a single endpoint /users which generates two different representations based on the client-defined Accept header:
# Produce JSON+HAL
curl http://localhost:8080/api/users
# Produce JSON+HAL
curl -H "Accept: application/hal+json" http://localhost:8080/api/users
# Produce JSON
curl -H "Accept: application/json" http://localhost:8080/api/users
Is there an elegant way with Spring REST?
The two endpoints are current defined as:
#GetMapping("/users")
public #ResponseBody PagedResources<PersistentEntityResource> pagedUsers(Pageable pageable, PersistentEntityResourceAssembler assembler) {
return pagedAssembler.toResource(personHandler.users(pageable), assembler);
}
#GetMapping("/users2")
public List<Person> allUsers() {
return personHandler.allUsers();
}
I need the plain JSON (i.e., non-HALed) API because of the (current) lack of reactive support in Spring HATEOAS.
From NetBeans, I created a new REST webservice (using jersey), using the built-in wizards. in the container resource class, it created a stub,
#POST
#Consumes("application/json")
#Produces("application/json")
public Response postJson(Identity identity) {
identities.addIdentity(identity);
return Response.status(Status.OK).entity(identity).build();
}
how to i POST to this? my understanding is that in need to post name=val pairs. what's jersey expecting here? how would i post json to this using say curl? here's what i tried,
#!/bin/bash
DATA="{ \"id\": \"$1\", \"vcard\": \"$2\", \"location\": { \"latitude\": \"$3\", \"longitude\": \"$4\" } }"
echo "posting: $DATA"
HEADER='Content-Type:application/json'
URL='http://localhost:8080/contacthi-proximity-service/resources/is'
curl --data-binary "${DATA}" -H "${HEADER}" "${URL}"
when I post this, and look at the identity object coming in, all fields are null? I suspect my json is incorrect. when i manually add an object to my container, then form a get, I see this result,
{"identities":{"id":"Foo Bar","vcard":"VCARD123","location":{"latitude":"-1.0","longitude":"-1.0"}}}
when I try to post the same thing, all fields all null. I also tried,
{"id":"Foo Bar","vcard":"VCARD123","location":{"latitude":"-1.0","longitude":"-1.0"}}
same result.
To send requests to this method using curl, you would have to use something like:
HEADER='--header Content-Type:application/json'
URL='http://localhost:<port>/methodName'
curl --data-binary request.json ${HEADER} ${URL} -D response.txt
You can pass a string to the method. Above code will pick json string from the file mentioned. Sample json could be:
{"userName":"test","timestamp":"2010-08-05T11:35:32.982-0800","userId":"0982"}
For creating response you can use something like:
return Response.status(Status.OK).entity(responseString).build();
Classes used are:
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;