ModelAttribute not mapping application/x-www-form-urlencoded - java

I have a somewhat simplistic controller configured as thus:
#RequestMapping(value = "user/savearticle", method = RequestMethod.POST)
public #ResponseBody
Object saveArticle(#ModelAttribute("article")RawArticle rawArticle);
Using snippets of code taken from here, I made a test case for the controller that looks like this:
MvcResult resultActions =
mockMvc.perform(MockMvcRequestBuilders.post("/user/savearticle")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.content(convertObjectToForumUrlEncodedBytes(rawArticle)))
.andReturn();
and I simply print out the result. In any case, the ModelAttribute "rawArticle" keeps ending up as null when it enters the controller's implementation, however when I use this:
MvcResult resultActions = mockMvc.perform(
MockMvcRequestBuilders.post("/user/savearticle")
.param("title", rawArticle.getTitle())
.param("tags", rawArticle.getTags())
.param("body", rawArticle.getBody())
.param("author", rawArticle.getAuthor())).andReturn();
the mapping actually works like a charm. What I want though is that the first test be processed correctly as it seems so wrong that it's not being mapped as I thought it should be, similarly the controller is primarily being used by another program over the network using apache http (which somehow automatically passes a urlencoded form).
Do you guys have any idea where I could've made an error? I wouldn't mind posting snippets of my context configuration if you think you need it to evaluate the problem (or my pom for that matter, but just telling me what libraries I may have missed should be enough)
Update:
I made a mistake of inserting the POJO into a session in test number 1, I simply removed it here. The question stands the same.

The content is the content body of the request, you are using a content type which expects everything encoded in the URL not in the content body. Spring does data binding based on the request parameters if you want to use the content body you have to use #RequestBody instead of #ModelAttribute.

Related

ignore csrf parameter in spring mvc

I use org.springframework.security.web.csrf.CookieCsrfTokenRepository to secure my spring based web application from CSRF attacks. This enables all the Controller methods to be called only by passing a X-XSRF-TOKEN header or a _csrf request parameter.
So in order for me get the response of a GET URL in browser, I will have to write something like the below in browser address bar.
http://localhost:8080/api/someresource?_csrf=99e3b824-d0c9-409d-91ee-c7ccbdce313f&filter1=value1&filter2=value2&so=on
However, Some of these urls have filter mechanism based on the request parameters and unfortunately this extra _csrf parameter breaks that logic.
As I see it, this request parameter should be needed if the request had passed the csrf validation. But I couldn't do anything in the documentation to remove the _csrf request parameter on the application level.
At the moment, I do something like below.
#ResponseStatus(OK)
#RequestMapping(method = RequestMethod.GET, value = "/search/advanced")
#ResponseBody
public ResponseVO advancedSearch( #RequestParam MultiValueMap<String, String> queryParameters, Pageable pageable) {
queryParameters.remove(MyApplicatonConstants.CSRF_PARAM_NAME); //this line is the hack that I wrote
return doStuffAndGetFilteredData(queryParameters);
}
This implementation has its drawbacks.
I will have to change all 143 controller methods to have this one line on the top.
I have to remember to add this for new methods in future.
it's a cheap hack and there should be some better and cleaner way of doing it.
Is there a way I can acheive this without rewriting that one line again and again?
Note:
I fully understand that I can use CURL or Postman so I can pass X-XSRF-TOKEN in header. But it's not as quick as opening the URL in a new tab.

Accessing Parameters mapped by #RequestMapping for Tracing

I am currently working on a monitoring application using Spring Cloud Sleuth. Currently I try to collect as much information about my requests as possible.
To keep the approach as scalable as possible I use GenericFilterBeans and HandlerInterceptorAdapter to access information from the requests sent to the REST-API.
I am struggling with getting parameters of a REST-call where the parameters are mapped from the URL like in the following code snippet:
#RequestMapping(
value = {"/{service}/{route_id}/book", "/accounting-core-service/{service}/{route_id}/book"},
method = RequestMethod.GET)
#ResponseBody
public ModelAndView book(#PathVariable(value="service") String serviceName,
#PathVariable(value = "route_id") int routeId,
HttpServletResponse response) {
/*Do something*/
}
The question is not, whether it is good practice or not to write it like so. The question is whether there is an approach similar to Filter or Interceptor (or the proper use of them) to access those parameters.
A requirement is, that it can be applied easily to an application by adding very few lines of code. Annotating every Method call manually or manually inserting the code to write the parameters into the trace from within the method is not feasible for me.
If you need more information feel free to ask. I will provide you with all information you need to help me with my problem.
Although not officially supported (as it's not written in the reference documentation), Spring MVC holds that information as request attributes.
You could create your own HandlerInterceptor, ordered right after the Sleuth one, and get that information from the request like this:
// "/{service}/{route_id}/book"
String matchingPattern = (String) request
.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
// "service" => "fooService", "route_id" => "42"
Map<String, String> templateVariables = (Map<String, String>) request
.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
Note, the template variables are already decoded at that point, which is probably what you want anyway.
You can create a Filter that intercept all the requests.
For each request you can retrieve also this informations:
query parameters
body of request
url
header parameters
You can save all this data as you need.
This is the best way you can do that.
If you need to group all urls like /{service}/{route_id}/book in a "family" of urls you can do that splitting the url and check if it is part of the family, but when a new family is added in your code you need to update the filter (or configure something in an external file or database).

explicitly get post parameter or query parameter in controller method

I am using the #RequestParam annotation to instruct Spring MVC to inject a "web request parameter" (as the Javadoc calls them) into a method call in my code:
#RequestMapping(path="/signup-submit", method=RequestMethod.POST)
public String signupSubmit(#RequestParam(value="originURL", required=true) String originURL ...) {
...
I am bothered by the fact that this annotation apparently works both for POST parameters and for URL query parameters. I.e. looking at the above code it is not possible to say whether the originURL is a POST body parameter or is a URL query parameter. Is there an annotation I can use to explicitly get it from either one or the other?
This is why I also place "web request parameter" in quotation marks as I don't think this is a technical term and I guess it is used in the loose sense of "some parameter either passed in the POST method body or as a query parameter in the URL".
Contrary to what cularis said there can be both in the parameter map.
The best way I see is to proxy the parameterMap and for each parameter retrieval check if queryString contains "&?=".
Note that parameterName needs to be URL encoded before this check can be made, as Qerub pointed out.
That saves you the parsing and still gives you only URL parameters.
The way can be getting query string..
public List<Topic> getTopics(HttpServletRequest req) {
String queryString = req.getQueryString();
.....
About "Web-Request-Parameter" (#RequestHeader), you are right as it consists of both params i.e. either are part of query or a part of request body.
Not the exact solution to your problem. But seems like there's an easy hack for it. You can use #RequestBody. This only looks for request body. If param is not found in request body, you can be sure that it is from request param.

Is there a way to access HttpRequest type inside a #Controller method

I have tried to find the answer to this, but I cannot seem to find what I am looking for. So I apologize if this question already exists.
PROBLEM:
I want to be able to access the request type of a request inside of a generic method within my Controller.
DESCRIPTION:
Using Spring ROO and Spring MVC, I have developed a small web service that will respond with certain tidbits from a database when queried. In one of my controller classes, I have some methods that handle some variety of GET, PUT, POST, etc., for the URIs that are mapped within the #RequestMapping parameter.
For example:
#RequestMapping(method = RequestMethod.Get, value = "/foo/bar")
#ResponseBody
public ResponseEntity<String> getFooBar() {
// stuff
}
If a request is made to the web service that it is not currently mapped, a 405 error is returned (which is correct), but I want to return more information along with a 405 response. Maybe respond with something like:
"I know you tried to execute a [some method], but this path only handles [list of proper methods]."
So I wrote a short method that only has the RequestMapping:
#RequestMapping(value = "/foo/bar")
I have found that the method with this mapping will catch all unhandled request types. But I am having trouble accessing the information of the request, specifically the type, from within the method.
QUESTION:
A. How can I access the request type from within the method? OR
B. Is this the right approach? What would be the right approach?
EDIT
ANSWER:
I added a HttpServletRequestobject to the method parameters. I was able to access the method type from that.
I tried using HttpRequest, but it didn't seem to like that much.
Thanks all!
You can add a method parameter of HttpServletRequest, but I think you'd be better off continuing to reply with 405. A client should then make an HTTP OPTIONS call (see How to handle HTTP OPTIONS with Spring MVC?) and you can return the list of allowed methods there.
A. you can access request if you mentioned it as parameter in controller method
public ... getFooBar(HttpRequest request) {
...
}
B. you do not need to add any other description as the 405 status is descriptive.
In answer to "A", just add "HttpRequest req" as an additional argument to your controller methods. Spring will automatically inject a reference to the request, and you can play with headers to your heart's content.
In answer to "B" - "What would be the right approach", how about this?
In order to return that 405, Spring has raised a MethodArgumentNotValidException. You can provide custom handling for this like so:
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public MyMethodArgumentMessage handleMathodArgumentNotValidException(
MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
MyMethodArgumentMessage myMessage =
new MyMethodArgumentMessage(result.getFieldErrors());
return myMessage;
}
You should take a look at the #ExceptionHandler annotation. This lets you add methods such as the following to your controller. You can define your own exceptions and appropriate custom handlers for them. I use it to return well-structured XML and JSON from REST services. Although for it to work, you need to throw specific exceptions from your controller methods.
A good walk-through of using this was provided by Petri Kainulkainen in his blog:
http://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-adding-validation-to-a-rest-api/

Passing a JSONObject as a parameter on Spring MVC

I wish to implement a REST service using Spring MVC where I pass in the following object at the URL "/url/lookup/{jsonparm}":
{"url":"http://bubba.com/foo/bar", "max_hops":3}
I tried the following:
#RequestMapping(value = "/url/lookup/{jsonparam}", method = RequestMethod.GET)
#ResponseBody
public String urlLookup(#PathVariable("jsonparam") String jsonparam) {
// just to see if I can get the parms
logger.debug("urlLookup get request : " + jsonparam.toString());
JSONObject resp = new JSONObject();
return resp.toString(); // return an empty JSONObject for now
}
So I invoke this by calling
http://localhost:8080/v1/wsp/url/lookup/%7B%22max_hops%22%3A3%2C%22url%22%3A%22http%3A%2F%2Fbubba.com%2Ffoo%2Fbar%22%7D
No luck see the following in my Jetty log:
WARNING: No mapping found for HTTP request with URI [/v1/wsp/url/lookup/{"max_hops":3,"url":"http://bubba.com/foo/bar"}] in DispatcherServlet with name 'rest'
Notes:
the url prefix localhost:8080/v1/wsp/ is correct and my Servlet and Request Mapping are also correct
I have updated the question to use a #PathVariable as one of the responders suggested
Thanks.
You're confusing GET and POST methods.
Either:
Use POST method and actually post the JSON contents to the controller. You can debug this using any REST client, eg. Advanced Rest Client for Chrome.
Use GET method (as you are currently). But you have to pass the JSON value as an actual parameter called jsonparam. So, your example should change to:
http://localhost:8080/v1/wsp/url/lookup/jsonparam=%7B%22max_hops%22%3A3%2C%22url%22%3A%22http%3A%2F%2Fbubba.com%2Ffoo%2Fbar%22%7D
The latter is less common.
Edit:
On second look at your URL, I suspect you're confusing two Spring annotations:
#PathVariable("jsonparam") and:
#RequestParam("jsonparam")
You're using #RequestParam while your URL indicates need for #PathVariable.
Edit2:
However, as can be read here: http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-uri-templates the path variable can be passed a value of any simple type. I believe JSON isn't one of them, hence your problems.
I would strongly recommend using POST for interchanging JSON values. However, if that is not an option, I would recommend sticking with GET method, #RequestParam for accessing parameter value, and passing the JSON value like in the corrected example above.
You should use #PathVariable instead of #RequestParam:
public String urlLookup(#PathVariable("jsonparam") String jsonparam){
}
because you have #RequestMapping(value = "/url/lookup/{jasonparam}"
And you have a typo in your #RequestMapping value. It should be /url/lookup/{jsonparam} instead of {jasonparam}

Categories

Resources