Using both #RequestBody and #RequestParam together in spring mvc3 - spring

I am using spring-mvc 3.1.0.RELEASE and for some reason, mapping POST with query params and request body does not work.
Here is how my controller method looks:
#RequestMapping(method = POST, value = "/post-to-me/")
public void handlePost(
#RequestBody Content content,
#RequestParam("param1") String param1,
#RequestParam("param2") String param2
){
//do stuff
}
However, if I convert all the request params to path params, mapping works. Has anyone run into something similar?
Thanks!
EDIT:
"does not work" == 404 when I try doing, POST /post-to-me?param1=x&param2=y

First, your POST url doen't match the controller method url, your POST url must be "/post-to-me/?param1=x&param2=y" not "/post-to-me?param1=x&param2=y"
Second, where did Content class come from?? I used a String and works fine for me
#RequestMapping(method = RequestMethod.POST, value = "/post-to-me/")
public void handlePost(#RequestBody String content,
#RequestParam("param1") String param1,
#RequestParam("param2") String param2, HttpServletResponse response) {
System.out.println(content);
System.out.println(param1);
System.out.println(param2);
response.setStatus(HttpServletResponse.SC_OK);
}
Note that I used HttpServletResponse to return a HTTP 200 code, but I think there is a better solution for return Http codes, check this: Multiple response http status in Spring MVC

Trailing slash at the end of your request mapping value might be the problem.
Try:
#RequestMapping(method = RequestMethod.POST, value = "/post-to-me")
or send your POST request to POST /post-to-me/?param1=x&param2=y

Related

In Spring Boot - Can you make request mapping that receives the whole message body without any parsing/intervention?

I need to have a method that does not regard/parse the content of request message, just ... pass it along as input parameter to the #PostMapping method.
Is it possible? Because defining parameters like:
#RequestBody byte[] data
or
#RequestBody String text
tell the framework that it suppose to get some xml/json. and I want it to receive plain text + utf-8 encoding.
Some code to clarify:
#RestController
#RequestMapping(path="/abc", method = RequestMethod.POST)
public class NlpController {
#PostMapping(path="/def", consumes="text/plain; charset: UTF-8", produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> processText(#RequestBody String text)
{
...
return ResponseEntity.ok().body(object);
}
}
Trying also:
#RestController
#RequestMapping(path="/abc", method = RequestMethod.POST)
public class NlpController {
#PostMapping(path="/nlp", consumes=MediaType.APPLICATION_JSON_UTF8_VALUE, produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> process(HttpServletRequest request)
{
....
return ResponseEntity.ok().body(article);
}
}
But I get 406 response...
using curl:
curl -v -s -X POST -H "Content-Type:" -H "Content-Type: application/json; charset: utf-8" --data-binary #article.txt localhost:8080/abc/def/
I think you should inject HttpServletRequest as controller method attribute, then you will have acces to request payload.
#PostMapping(path="/something")
public ResponseEntity<Object> processText(HttpServletRequest request) {
// do something with request
}
More info.
406 Not Acceptable
The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/406
If i understand your question correctly you need to load text file directly as input param in spring boot rest call.
You need to modify your code and curl request , please use fllowing code as referance .
#RequestMapping(value = "/abc", method = RequestMethod.POST)
public String ResponseEntity<Object> processText(#RequestParam("file")
MultipartFile file) {
System.out.println("---------loading file----------");
/// Calculation and your logic
return ResponseEntity.ok().body(article);
}
Curl request :
curl -X POST localhost:8080/abc -F "file=#article.txt"
One more issue i can see in your curl request your mapping is abc and you are calling
localhost:8080/abc/def/
Using #RequestParam for multipartfile is a right way?
If using data in memory following code will work for you
#PostMapping(value = "/abc", consumes = "application/json", produces =
"application/json")
ResponseEntity<Object> processText( #RequestBody String input)
throws JSONException {
//
return ResponseEntity.ok().body(article);
}
Short answer: This is not a job for a full blown framework like spring boot. Better use something like spark that can do this with one liner and without any configurations. At least this is the best answer for my humble causes.
Long answer: I could not make spring boot to receive clean body text from a client, not even after many (failed) attempts to tweak the headers / media / consume flag / ... Guess this just (might) not be possible.

Debugging AJAX to Spring Controller (count of variables and spelling)

I am spending a lot of effort debugging ajax calls. The common issues are
number of parameters dont match from the ajax to the controller
spelling of the #RequestMapping parameter does not match
If the type does not match the call happens and it can be debugged. But the bigger issue is the above two. I have 88 parameters that I am passing and have a hell of a time trying to figure out what is missing or spelt incorrectly.
example
#RequestMapping("/saveClient")
public #ResponseBody String saveClientAJAXMethodView(#RequestParam(value = "clientName") String clientName,
.... 88 parameters more
$
.ajax({
type : "Post",
url : "saveClient",
data : {
clientName : clientName,
... 88 parameters more
I got this error
The request sent by the client was syntactically incorrect.
So I changed the signature of my controller to add , method = RequestMethod.POST).
Now I am getting
message Request method 'GET' not supported
description The specified HTTP method is not allowed for the requested resource.
Its clearly a "POST" and still it get a request method GET not supported.
The question is NOT how to solve this problem. The question is how to debug such issues easily. What errors map to what issues, how to debug the 88 parameter spellings and count ? There must be a easier way to do this debugging.
I use the following
Debugging Mode of the controller
Inspect on Chrome.
Since you are sending huge amount of parameter in URL for POST request. I would suggest you to send your data in body.
For example if you are sending parameters like clientId, clientName, clientEmail etc.. you have used #RequestParam annotation to get individual parameter data in your controller:
String saveClientAJAXMethodView( #RequestParam String clientId,
#RequestParam String clientName,
#RequestParam String clientEmail
.... more parameters)
Instead of using #RequestParam I would suggest you to use #RequestBody, For this you need to create a Data transfer object (DTO) like this:
class ClientInfo{
String clientId,
String clientName,
String clientEmail,
....
.... other variables
.... getters and setters of variables
}
And then use this DTO in your controller method like this:
String saveClientAJAXMethodView(#RequestBody ClientInfo clientInfo){
}
Using this approach you will not get any exception regarding spelling mistake or parameter missing .The value will be assigned to a DTO variable if you are sending value with right key as specified in DTO.
To count variables in ClientInfo object you will need to cast ClientInfo to JSONObject and use its size() method to get count of variables
String saveClientAJAXMethodView(#RequestBody ClientInfo clientInfo){
JSONObject json = new JSONObject(clientInfo);
System.out.println(json.keySet().size());
}
Your ajax call will look like this:
var clientInfo = {
'clientName': 'tom',
'clientId': '23AZ1',
'clientEmail': 'xyz#gmail.com',
...
};
$.ajax({
url: url,
type: "POST",
data: JSON.stringify(clientInfo),
contentType: "application/json",
complete: callback
});
I hope following steps would help you debug:
1- Use a filter to intercept request.
2- Create a custom annotation which would indicate that you want to debug this method.
3- Use the method defined in this post Can I get all of requestMapping URL with GET method in the Spring? and your custom annotation to store list of all methods which you want to debug in a singleton bean.
4- Now write some logic in filter which would print mismatch between the method parameters and request parameters.
CustomFilter:
public class CustomFilter extends GenericFilterBean {
#Autowired
#Qualifier("printMismatchMethods")
HashMap<String,Method> methodsToCheck;
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
HttpServletRequest r = (HttpServletRequest) request;
String url = r.getRequestURI().substring(r.getContextPath().length());
//Remove extensions if present any
int index = url.indexOf('.');
if(index > 0)
url = url.substring(0,url.indexOf('.'));
/*Matching string this should be replaced by url pattern matching of spring.*/
if (methodsToCheck.containsKey(url)){
Method method = methodsToCheck.get(url);
Map<String, String[]> requestParameterMap = r.getParameterMap();
Map<String,Boolean> isParamPresent = new HashMap<String,Boolean>();
for (Parameter parameter : method.getParameters()){
RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
if (requestParam != null && requestParam.required()){
if (!requestParam.name().isEmpty())
isParamPresent.put(requestParam.name(), false);
else
isParamPresent.put(requestParam.value(), false);
}
}
for (Parameter parameter : method.getParameters()){
RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
if (requestParam != null && requestParam.required()){
String name = null;
if (!requestParam.name().isEmpty())
name=requestParam.name();
else
name=requestParam.value();
if (requestParameterMap.containsKey(name)){
isParamPresent.put(name, true);
}
}
}
for (Map.Entry<String, Boolean> entry : isParamPresent.entrySet()){
if (!entry.getValue()){
System.out.println(entry.getKey() + " is either missing or mis-spelled");
}
}
}
chain.doFilter(request, response);
}
}
configured as follows:
http.addFilterAfter(
new CustomFilter(), BasicAuthenticationFilter.class);
Declare following custom annotation.
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface PrintParamMismatch {
}
Define following bean which would populate on startup
#Bean(name="printMismatchMethods")
#Autowired
public HashMap<String,Method> printParamMismatchMethods(BeanFactory beanFactory){
HashMap<String,Method> methods = new HashMap<String,Method>();
Map<String, RequestMappingHandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(
(ListableBeanFactory)beanFactory,
RequestMappingHandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
ArrayList<HandlerMapping> handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(handlerMappings);
RequestMappingHandlerMapping mappings = matchingBeans.get("requestMappingHandlerMapping");
Map<RequestMappingInfo, HandlerMethod> handlerMethods = mappings.getHandlerMethods();
for (Map.Entry<RequestMappingInfo, HandlerMethod> handlerMethod : handlerMethods.entrySet()){
RequestMappingInfo info = handlerMethod.getKey();
HandlerMethod hMethod = handlerMethod.getValue();
Method method = hMethod.getMethod();
if (method.getAnnotation(PrintParamMismatch.class) != null){
String path = info.getPatternsCondition().toString();
path = path.substring(1,path.length());
path = path.substring(0,path.length()-1);
methods.put(path, method);
}
}
}
return methods;
}
This, I think is generic enough to show debug information for now. However we need to store and match Patterns instead of url string.
So I used divide and rule and solved my issue. I commented top half of the parameters and ran, to check if my controller gets called. It did. then I added 1/4th, then 1/8th and found that I missed a parameter.
That along with #ArsianAnjum's answer is good for debugging. #Aji's answer is the long term solution. I should be using that.

Jersey get credentials from url

I have an url like this:
http://log:pass#localhost:8080/myendpoint
And Jersey endpoint:
#GET #Produces(MediaType.APPLICATION_JSON) #Path("/login")
#Consumes(MediaType.APPLICATION_JSON) public Response login(
#Context HttpHeaders headers, #QueryParam("callback") String callback)
{
}
And ideally I want to get 'log' and 'pass' in my endpoint method. How to do that in Jersey? I tried many endpint method signatures, filters, etc but it shows me
http://localhost instead of http://log:pass#localhost everywhere
How to achieve this?
Assuming that in your front end, you are sending your parameters as a json object using JSON.stringify() then back in your endpoint method. Add this as a second argument to that method signature JsonObject payload. Then you can access your query parameters within that method as follows
String log = payload.getString("log");
String pass = payload.getString("pass");
Revised Version
#Path("/login")
#GET
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response login(#Context UriInfo uriInfo, #Context HttpHeaders headers, #QueryParam("callback") String callback) {
URI requestUri = uriInfo.getRequestUri();
String authority = requestUri.getAuthority(); // authority contains what you need;
}

Bind body and uri http parameters into Spring controller method parameters

Hy,
I have the next controller method in Spring:
#RequestMapping(value="indicadores/{entUsuario}/{codUsuario}/{idCliente}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public IndicadoresBean getIndicadoresPlanBasicoBean(
#RequestBody InvestmentProposalGroupingResponse investmentProposalGroupingBean,
IndicadoresPlanBasicoRequest request,
BindingResult validationResult,
HttpServletResponse response) {......... }
But when I send a request I get :
"Required request body content is missing: org.springframework.web.method.HandlerMethod$HandlerMethodParameter".
If I change the verb to POST it works but I need it works with GET
I need to fill in the request body json in the parameter investmentProposalGroupingBean and uri parameters in parameter request. Is possible?
Thanks

How to pass json parameters with jax-rs?

if I want to post json parameter to a url with jax-rs, how to receive the parameters in the back end and convert it into an integer array?
parameters:{"sbfIdList":[14]}
backend service(in java): I tried below , but feel no luck, I just got an empty array.
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Path("/delete")
public String deleteSbc(#QueryParam("sbfIdList") List<Long> sbfIds,#Context HttpServletRequest request, #Context HttpServletResponse response) {
return "";
}
I request the backend service using POST method.

Resources