Using Jersey's #BeanParam results in a 415 error - jersey

I am trying to use Jersey's #BeanParam annotation the following way:
This is my bean:
public class BeanParamModel {
#QueryParam(value = "param1")
private String param1;
public BeanParamModel(#QueryParam("param1") String param1) {
this.param1 = param1;
}
public String getParam1() {
return param1;
}
public void setParam1(String param1) {
this.param1 = param1;
}}
And this is the resource method that needs to use it:
#Consumes("*/*")
#Path("mypath")
#GET
public Response getUpgradeStatus(#QueryParam("param1") String param1, #BeanParam BeanParamModel user) {
return Response.ok().build();
}
Now I want to test this using a unit test which sends an http request to a test server with the following url:
GET http://path_to_resource?param1=1
My problem is that results in a 415 response with Jersey printing this message:
A message body reader for Java class BeanParamModel, and Java type class BeanParamModel, and MIME media type application/octet-stream was not found.
The registered message body readers compatible with the MIME media type are:...
I've trying adding a "application/x-www-form-urlencoded" header but the message repeats for that header type as well. I also tried using an application/json header, this results in EOF expection from the jackson mapper due to end of input.
Can anyone tell me what I'm not doing correctly? from the jersey documentation of #BeanParam it seems pretty simple.

With a #GET you should not have #Consumes.

Related

What is the javax.ws.rs MediaType we need to use for JWT response

What is the MediaType we can use in #Produces annotation if we are expecting the server to return a JWT response?
for application json we can use,
#Produces({MediaType.APPLICATION_JSON})
But what if we want it to be application/jwt ?
If we use it as below server will return an error saying "No message body writer has been found for response class ...."
#Produces({"application/jwt"})
I would try something like this:
#GET
#Produces("application/jwt")
public Response authenticate(...) {
String jwt = ...;
return Response.ok(jwt).build();
}
This should work.

How to set at Spring Boot all Errors to json?

Is there at spring boot a configuration possible, which returns all errors in a json format?
For example 404, or 401. Need to replace this 404 page with just json.
Many thanks
This is since by default, springboot produces the error as html.
To get the Json output, add produces argument as follows so that the content returned will be for sure in json format.
#RequestMapping(....., produces = "application/json")
You can custom your error controller to handle it:
#RestController
public class CustomErrorController extends BasicErrorController {
private final Logger logger = LoggerFactory.getLogger(CustomErrorController.class);
public CustomErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes, new ErrorProperties());
}
// let all MediaType return json data
#RequestMapping(consumes = MediaType.ALL_VALUE)
public ResponseEntity<Map<String, Object>> allError(HttpServletRequest request, HttpServletResponse response) {
return super.error(request);
}
}
You mean using #ControllerAdvice with the content header set to application/json

getting http 415, Unsupported Media Type using text/xml

I have a jersey endpoint(JAX-RS) that I'm trying to hit with a text/xml req. I'm getting back an http 415 and I don't understand why. Here is the info. Any ideas? Thanks.
#Path("/bid")
#Produces("text/xml;charset=ISO-8859-1")
#Consumes({"text/xml", "application/xml"})
#Resource
public class BidController {
#RolesAllowed("blah")
#POST
public Response bid(final HttpServletRequest request) {
I am hitting it via Postman(REST client) and sending {"Content-Type":"text/xml"}
My POST body is definitely well formed xml.
You are getting a 415 response because JAX-RS does not know how to convert incoming XML into a HttpServletRequest.
If you really want access to the request, then you need to annotate it with #javax.ws.rs.core.Context:
#RolesAllowed("blah")
#POST
public Response bid(#Context final HttpServletRequest request) {
However, as you say you're hitting it with text/xml, then you may actually want:
#POST
public Response bid(final MyRequest request) {
...
}
where MyRequest is declared something like:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class MyRequest {
#XmlElement
int field1;
#XmlElement
String field2;
...
}
which corresponds to XML like:
<MyRequest>
<field1>11327</field1>
<field2>some string
</MyRequest>
The JAX-RS specification requires implementations to be able to decode incoming text/xml and encode outgoing text/xml via JAXB.

415 Unsupported Media Type, when NOT sending an optional request body with POST request

I have a REST controller that defines an interface which takes an optional request body.
#RestController
#RequestMapping(ExampleRest.EXAMPLE_URI)
public class ExampleRest {
public static final String EXAMPLE_URI = "/examples";
#RequestMapping(value = "/search", method = POST)
public Page<ExampleDto> search(#RequestBody(required = false) Searchable searchable, Pageable pageable) {
return exampleService.findAll(searchable, pageable);
}
}
The Searchable object contains information to create a JPASpecification. It's pretty much a dto. I would like to make this searchable optional. I understood that #RequestBody(required = false) should do the trick.
I have the following test, where I want to test a request without any request body.
#Test
public void post_NoCriteria_Ok() {
RequestEntity requestEntity = new RequestEntity(HttpMethod.POST, URI.create(ExampleRest.EXAMPLE_URI + "/search"));
ResponseEntity <RestResponsePage<ExampleDto>> response = restTemplate.exchange(requestEntity, new ParameterizedTypeReference <RestResponsePage<ExampleDto>> () {});
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
}
If I run this test, it keeps failing with this response from the RestController:
<415 Unsupported Media Type,Page 1 of 1 containing UNKNOWN
instances,{Content-Type=[application/json;charset=UTF-8],
Transfer-Encoding=[chunked], Date=[Wed, 13 Sep 2017 10:10:22 GMT]}>
The Code execution does not even enter search method implementation inside of the RestController.
As soon I provide an empty Searchable for the test, it runs through.
Is the implementation of #RequestBody(required = false) buggy, or what am I doing wrong here?
You need to set Content-Type as "application/json" in your request while sending from #Test file.

Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported for #RequestBody MultiValueMap

Based on the answer for problem with x-www-form-urlencoded with Spring #Controller
I have written the below #Controller method
#RequestMapping(value = "/{email}/authenticate", method = RequestMethod.POST
, produces = {"application/json", "application/xml"}
, consumes = {"application/x-www-form-urlencoded"}
)
public
#ResponseBody
Representation authenticate(#PathVariable("email") String anEmailAddress,
#RequestBody MultiValueMap paramMap)
throws Exception {
if(paramMap == null || paramMap.get("password") == null) {
throw new IllegalArgumentException("Password not provided");
}
}
the request to which fails with the below error
{
"timestamp": 1447911866786,
"status": 415,
"error": "Unsupported Media Type",
"exception": "org.springframework.web.HttpMediaTypeNotSupportedException",
"message": "Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported",
"path": "/users/usermail%40gmail.com/authenticate"
}
[PS: Jersey was far more friendly, but couldn't use it now given the practical restrictions here]
The problem is that when we use application/x-www-form-urlencoded, Spring doesn't understand it as a RequestBody. So, if we want to use this
we must remove the #RequestBody annotation.
Then try the following:
#RequestMapping(
path = "/{email}/authenticate",
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = {
MediaType.APPLICATION_ATOM_XML_VALUE,
MediaType.APPLICATION_JSON_VALUE
})
public #ResponseBody Representation authenticate(
#PathVariable("email") String anEmailAddress,
MultiValueMap paramMap) throws Exception {
if (paramMap == null &&
paramMap.get("password") == null) {
throw new IllegalArgumentException("Password not provided");
}
return null;
}
Note that removed the annotation #RequestBody
answer: Http Post request with content type application/x-www-form-urlencoded not working in Spring
It seems that now you can just mark the method parameter with #RequestParam and it will do the job for you.
#PostMapping( "some/request/path" )
public void someControllerMethod( #RequestParam Map<String, String> body ) {
//work with Map
}
Add a header to your request to set content type to application/json
curl -H 'Content-Type: application/json' -s -XPOST http://your.domain.com/ -d YOUR_JSON_BODY
this way spring knows how to parse the content.
In Spring 5
#PostMapping( "some/request/path" )
public void someControllerMethod( #RequestParam MultiValueMap body ) {
// import org.springframework.util.MultiValueMap;
String datax = (String) body .getFirst("datax");
}
#RequestBody MultiValueMap paramMap
in here Remove the #RequestBody Annotaion
#RequestMapping(value = "/signin",method = RequestMethod.POST)
public String createAccount(#RequestBody LogingData user){
logingService.save(user);
return "login";
}
#RequestMapping(value = "/signin",method = RequestMethod.POST)
public String createAccount( LogingData user){
logingService.save(user);
return "login";
}
like that
Simply removing #RequestBody annotation solves the problem (tested on Spring Boot 2):
#RestController
public class MyController {
#PostMapping
public void method(#Valid RequestDto dto) {
// method body ...
}
}
I met the same problem when I want to process my simple HTML form submission (without using thymeleaf or Spring's form tag) in Spring MVC.
The answer of Douglas Ribeiro will work very well. But just in case, for anyone, like me, who really want to use "#RequestBody" in Spring MVC.
Here is the cause of the problem:
Spring need to ① recognize the "Content-Type", and ② convert the
content to the parameter type we declared in the method's signature.
The 'application/x-www-form-urlencoded' is not supported, because, by
default, the Spring cannot find a proper HttpMessageConverter to do
the converting job, which is step ②.
Solution:
We manually add a proper HttpMessageConverter into the Spring's
configuration of our application.
Steps:
Choose the HttpMessageConverter's class we want to use. For
'application/x-www-form-urlencoded', we can choose
"org.springframework.http.converter.FormHttpMessageConverter".
Add the FormHttpMessageConverter object to Spring's configuration,
by calling the "public void
configureMessageConverters(List<HttpMessageConverter<?>>
converters)" method of the "WebMvcConfigurer" implementation class
in our application. Inside the method, we can add any
HttpMessageConverter object as needed, by using "converters.add()".
By the way, the reason why we can access the value by using "#RequestParam" is:
According to Servlet Specification (Section 3.1.1):
The following are the conditions that must be met before post form
data will be populated to the parameter set: The request is an HTTP
or HTTPS request. 2. The HTTP method is POST. 3. The content type is
application/x-www-form-urlencoded. 4. The servlet has made an initial
call of any of the getParameter family of methods on the request
object.
So, the value in request body will be populated to parameters. But in Spring, you can still access RequestBody, even you can use #RequstBody and #RequestParam at the same method's signature.
Like:
#RequestMapping(method = RequestMethod.POST, consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public String processForm(#RequestParam Map<String, String> inputValue, #RequestBody MultiValueMap<String, List<String>> formInfo) {
......
......
}
The inputValue and formInfo contains the same data, excpet for the type for "#RequestParam" is Map, while for "#RequestBody" is MultiValueMap.
I wrote about an alternative in this StackOverflow answer.
There I wrote step by step, explaining with code. The short way:
First: write an object
Second: create a converter to mapping the model extending the AbstractHttpMessageConverter
Third: tell to spring use this converter implementing a WebMvcConfigurer.class overriding the configureMessageConverters method
Fourth and final: using this implementation setting in the mapping inside your controller the consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE and #RequestBody in front of your object.
I'm using spring boot 2.
#PostMapping(path = "/my/endpoint", consumes = { MediaType.APPLICATION_FORM_URLENCODED_VALUE })
public ResponseEntity<Void> handleBrowserSubmissions(MyDTO dto) throws Exception {
...
}
That way works for me
You can try to turn support on in spring's converter
#EnableWebMvc
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
// add converter suport Content-Type: 'application/x-www-form-urlencoded'
converters.stream()
.filter(AllEncompassingFormHttpMessageConverter.class::isInstance)
.map(AllEncompassingFormHttpMessageConverter.class::cast)
.findFirst()
.ifPresent(converter -> converter.addSupportedMediaTypes(MediaType.APPLICATION_FORM_URLENCODED_VALUE));
}
}
Just add an HTTP Header Manager if you are testing using JMeter :

Resources