I am trying to perform an ajax request using the jsonp data type due to cross domain issues in a clustered environment.
I can make jsonp requests to methods mapped with no #RequestBody parameters, but when I do try to implement a RequestMapping with a #RequestBody parameter I get a 415 Unsupported Media Type error.
Usually when I get this problem it is due to some property not mapping correctly between the json object posted and the Java object it is mapped to in Spring. But the only discrepency I can find is that using jsonp it adds a parm named callback and one with the name of an underscore "_"
So I added the tag #JsonIgnoreProperties(ignoreUnknown = true) to my Java object and figured that should solve that, however it is still throwing this error.
Is there anything else I need to do?
EDIT: I am now seeing this stack trace in the debug log output from Spring:
org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported
$.ajax({
url : 'http://blah/blah.html',
data : { abc : '123' }, (I also tried to JSON.stringify the object but no difference)
dataType : 'jsonp',
success : function(response) {
alert('ok '+JSON.stringify(response));
},
fail : function(response) {
alert('error'+JSON.stringify(response));
}
});
The Spring controller is:
#RequestMapping({ "blah/blah" })
#ResponseBody
public ReturnObject getBlahBlah (#RequestBody MyObject obj) throws Exception {
}
The parameter object is:
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyObject {
private String abc;
// getter and setter for abc auto generated by MyEclipse
}
I have a breakpoint on the Controller method which is never hit.
JSONP means that jQuery will create a <script> element with src pointing to your controller URL.
As you can see, this approach doesn't allow you to pass any data in request body, all data should be passed in URL as query parameters. data : { abc : '123' } means that abc=123 is added to the URL.
At controller side you need to use either #RequestParam (to bind inidividual parameters) or #ModelAttribute (to bind multiple parameters into an object):
public ReturnObject getBlahBlah (#RequestParam("abc") int abc) throws Exception { ... }
Related
I am working on spring boot with graphql implementation. And, I am mapping my pojo request parameters to an internal project with help of mapstruct. I have written my mapper class where I have mapped my request parameters [source] to the target parameters. I have two different request payload in place and parameters will be mapped based on the request body.
Whatever parameters are given in the request, only those parameters should get mapped and should return the mapped request body. But in my case, When I don't send few parameters in the request payload, after mapping parameters those are not sent in the request also gets added in the conversion.
Sample Request1:
{
GetOrder( input : {
orderId: xxxxxx,
orderType: Online
})
}
Sample Request2:
{
GetOrder( input : {
orderId: xxxxxx,
orderType: Online,
user :{
userName: "ABC"
})
}
In this sample, "User" object is required in the request2, where in request1, I am not sending the user object. But when I send request1, this "User" object is also getting added in the request payload as an empty object after the mapping using mapstruct.
Could you someone please guide me on this.? Thanks in advance!
I have a spring boot controller endpoint as follows.
#PutMapping("/manage/{id}")
public ResponseEntity<Boolean> manage(#PathVariable Long id, #RequestBody Type type) {
...
}
Where Type is an Enum as follows.
public enum Type {
ONE,
TWO
}
ISSUE 1: When I test this controller, I have to send the content as "ONE" instead of ONE for a successful invocation. i.e. it works with the following code.
mvc.perform(put("/api/manage/1")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content("\"" + Type.ONE + '\"'))
.andExpect(status().isOk());
It does not work with
mvc.perform(put("/api/manage/1")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(Type.ONE.name()))
.andExpect(status().isOk());
ISSUE 2: I am not able to invoke this method from the Angular service.
this.http.put<string>('/api/manage/' + id, type)
gives me
org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain;charset=UTF-8' not supported
Everything works when I add the Enum to a Dto and send an object from the client. But due to some business requirements, I want to use the current structure itself. i.e the Enum as a RequestBody.
UPDATE
I also tried to change the controller method structure to
#PutMapping(value = "/manage/{id}", consumes = MediaType.TEXT_PLAIN_VALUE)
I get the following error.
Content type 'text/plain' not supported
Both issues stem from trying to use a JSON endpoint as a plain text endpoint.
Ad 1, ONE is invalid JSON ("ONE" is valid)
Ad 2, when you just post a string, it is sent as text/plain and the endpoint complains.
Probably adding consumes="text/plain" to your #PutMapping will solve the problem, but frankly - I am not sure if string/enum mappings work out-of-the-box in the hodge-podge that is spring boot.
I'm going to use a very basic hello world endpoint as an example
#RequestMapping("/hello")
public String hello(#RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
model.addAttribute("name", name);
return "helloworld";
}
If I have this endpoint and I want to be able to go to /hello and retrieve the helloworld view.
Is it possible for me to use the SAME /hello path to retrieve model as json if I pass in a specific request param like content-type?
You could try passing in a parameter using the RequestMapping params option. This does require modifying the URL, but the mapping is still the same and a mapped method without a params tag could be added as a default.
#RequestMapping(value="/hello" params= param1)
public returnType method(#RequestParam("param1") p) { ... }
#RequestMapping(value="/hello" params= param2)
public differentreturnType method2(#RequestParam("param2") p) { ... }
So to handle the first, request URL : http://etc.com/hello?param1=x and the second http://etc.com/hello?param2=y.
Params section of #RequestMapping docs: http://docs.spring.io/spring/docs/4.0.5.RELEASE/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#params--
I'm not sure I understand what you mean.
If you mean that you want to be able to send a request to /hello and get two different responses, with different content types, yes, you can do that.
#RequestMapping identifies a method as being a request handler, but it also provides options for restricting when the handler should be used.
In this case, you should use the Accept header in your HTTP request and set it to application/json for a response containing JSON and text/html for a response containing HTML.
You can then have two #RequestMapping methods like
#RequestMapping(value = "/hello", produces = "application/json")
public SomeType handleJson() {...}
#RequestMapping(value = "/hello", produces = "text/html")
public String handleHtml() {...}
Spring will determine which method to use based on the request's Accept header and the method's produces value.
Controller signature (I have tried as requestbody as well) :
#RequestMapping(value = "/Lame", method = RequestMethod.POST)
public
#ResponseBody
boolean getLame(#RequestParam String strToMatchA, #RequestParam String strToMatchB) {}
And this as my json :
{
"strToMatchA": "EN",
"strToMatchB": "lon"
}
Not working, I receive the error :
org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'strToMatchA' is not present
Removing this first parameter from method signature then makes it work (the method gets called correctly), what should I be doing ?
When I change method parameters to be annotated with #RequestBody I get the following error :
java.io.IOException: Stream closed
Your json is fine but not the controller signature.
Create a class with setters matching the json.
Use it as argument instead of your strings.
Annotate it with requestbody. It should work.
I'm familiar with how to return json from my #Controller methods using the #ResponseBody annotation.
Now I'm trying to read some json arguments into my controller, but haven't had luck so far.
Here's my controller's signature:
#RequestMapping(value = "/ajax/search/sync")
public ModelAndView sync(#RequestParam("json") #RequestBody SearchRequest json) {
But when I try to invoke this method, spring complains that:
Failed to convert value of type 'java.lang.String' to required type 'com.foo.SearchRequest'
Removing the #RequestBody annotation doesn't seem to make a difference.
Manually parsing the json works, so Jackson must be in the classpath:
// This works
#RequestMapping(value = "/ajax/search/sync")
public ModelAndView sync(#RequestParam("json") String json) {
SearchRequest request;
try {
request = objectMapper.readValue(json, SearchRequest.class);
} catch (IOException e) {
throw new IllegalArgumentException("Couldn't parse json into a search request", e);
}
Any ideas? Am I trying to do something that's not supported?
Your parameter should either be a #RequestParam, or a #RequestBody, not both.
#RequestBody is for use with POST and PUT requests, where the body of the request is what you want to parse. #RequestParam is for named parameters, either on the URL or as a multipart form submission.
So you need to decide which one you need. Do you really want to have your JSON as a request parameter? This isn't normally how AJAX works, it's normally sent as the request body.
Try removing the #RequestParam and see if that works. If not, and you really are posting the JSON as a request parameter, then Spring won't help you process that without additional plumbing (see Customizing WebDataBinder initialization).
if you are using jquery on the client side, this worked for me:
Java:
#RequestMapping(value = "/ajax/search/sync")
public ModelAndView sync(#RequestBody SearchRequest json) {
Jquery (you need to include Douglas Crockford's json2.js to have the JSON.stringify function):
$.ajax({
type: "post",
url: "sync", //your valid url
contentType: "application/json", //this is required for spring 3 - ajax to work (at least for me)
data: JSON.stringify(jsonobject), //json object or array of json objects
success: function(result) {
//do nothing
},
error: function(){
alert('failure');
}
});