Spring Boot Controller Multiple parameters (<List> and multipart file) object - java

Hello i am looking if i can handle with only one RestController method multiple params...
with controllers method it could be done... but i couldnt find project with 2 like that.
#PostMapping(value ="upload")
public upload(#RequestParam MultipartFile file,#RequestParam List<String> myParams ){
some code here ....
return;
}
I am just wondering if is also a good practise ... having two deferent type of objects in same controller and if its possible,,, any idea????

Simple answer: Yes, that's possible.
But as you asked for good practice, here's some context:
It is very helpful to understand how HTTP actually transports data.
If your request uses GET as request method, parameters are added to the URL as a query string. That could look like this: http://example.com/index?param1=value1&param2=value2
In this case, Spring maps the key-value pairs from the query string to your method arguments. But this will only work for text.
If you're using POST, the data is sent inside the request body. How that is encoded depends on the media type of your data. For example, the default media type application/x-www-form-urlencoded would encode the data to the same query string as above.
If you want to upload mixed-type form data like a file/blob along with some textual parameters, your data should be encoded with multipart/form-data.
As long as the request body contains a key-value format, Spring Boot will still be able to distinguish and map the parameters via #RequestParam (If the keys don't differ from your attribute names, you don't even need to assign a name to the value attribute).
I highly recommend you to take a look at the #RequestBody and #RequestPart annotations as i think it often is best practice to use a model class (DTO) for the whole request body (or rather the form, semantically), especially if there are a lot of parameters to process.

You will need to specify the names of the variables.
#PostMapping(value ="upload")
public upload(
#RequestParam(value = "file") MultipartFile file,
#RequestParam(value = "myParams") List<String> myParams
){
some code here ....
return;
}

Related

Mapping request params/ path variable to Dto in GET request in Spring

I'd like to create a controller which will return Page object. I will need page Pageable to have with page number and its size (it cannot be larger than 50) and some variables which will be search criteria. If this was a POST request, it could look as follows:
public Page<SomeDto) getDto(#RequestBody #Valid RequestDto requestDto, Page pageRequest)
However, it is a GET request so #RequestBody cannot/shouldn't be used.
Additionally, I would like to have something akin to #Valid so that Spring will automatically reject bad request.
I'm wondering whether:
it is possible to and if so how to implement this and have already created Dto from values taken from URL (#PathVariable or #RequestParam) as if it was POST request which maps body to object.
I'd like to avoid using some kind of that code:
public String updateFoos(#RequestParam Map<String,String> allParams) {}
if what is described in 1st is not possible, what solution would be closest as to meeting those requirements?
You can have a HTTP body (and therefore a #RequestBody) for every HTTP requests no matter what HTTP method is used. However, it is not a good REST API design to use a HTTP body for GET request (cf. https://martinfowler.com/articles/richardsonMaturityModel.html)
You can do bean validation with #PathVariable or #RequestParam arguments, you just have to put the annotation you want next to these annotations

Is it possible to customize how spring parses query parameters?

I've been using a generic map for query parameters in a spring-boot (4.3.3.RELEASE) application, which has been live and taking traffic for a while now. This application has dozens of different endpoints and recently I've seen that a number requests result in errors due to improperly formatted query parameters.
I've been seeing a significant number of the requests are coming through like http://web.com/url/path?param1=1?param2=2. Unfortunately, I don't control or know why these urls are showing up, and they are all resulting in error pages for the users using them.
Can spring be customized to parse maps of query parameters using both '&' and '?' as separators?
I suspect this can be supported with a custom interceptor to inspect modify the incoming urls, but I wanted to know if there is an easier solution.
This is an example the one endpoint
#RequestMapping(value = "/url/path", method = {GET, POST})
public ResponseEntity<Void> handleRequest(
#RequestHeader HttpHeaders requestHeaders,
#RequestParam MultiValueMap<String, String> requestParams) throws Exception
{
....
}

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).

Spring 3.1 or Later #RequestMapping Consumes/Produces

I have a question in regards to the consumes and produces part of the #RequestMapping. I have an endpoint that I want to accept both JSON and XML and return JSON when JSON is passed in and return XML when XML is passed in. Is there anything special that I have to do to make this work?
Sample code is listed below.
#RequestMapping(value = "/something", method = PUT,
consumes = {APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE},
produces = {APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE})
public SomeObject updateSomeObject(SomeObject acct) {
return doStuff(acct);
}
Will this work the way I'm expecting or do I need two endpoints updateSomeObjectXML and updateSomeObjectJson to handle both cases?
Thanks,
Mike
The article from the Spring blog - Content Negotiation using Spring MVC - provides details on how content negotiation works with Spring MVC, in brief if you want the same endpoint to handle XML and JSON, your mapping is correct, to summarize from the article:
Use path extension - you can send a json to /something.json and xml to /something.xml and expect the same thing on the way back
Use the Accept header, use a value of application/json or application/xml and use Content-Type to specify the submitted media type.
Short answer:
Annotate the method with #ResponseBody, and the method parameter with #RequestBody, and it will work (no need for 2 methods).
Explanation:
First, produces and consumes attributes are used to narrow the mapping types. By default the first HttpMessageConverter found, that matches the media type requested, will be used.
Second, client requests a media type by giving the media type in:
- Accept request header
- URL sufix (http: //....//some .xml => "application/xml" media type requested)
- URL format parameter (.../some?format=xls)
Third, produces in combination with #ResponseBody will produce the object in the requested media type (nice for GET requests, when you need to send something back to the client), and consumes in combination with #RequestBody will consume the object with the requested media type (nice for POST requests, when you need to get something from the client).
Four, when #ResponseBody not used, HttpMessageConverters are not used. Rather ViewResolvers kick in and produce a view (HTML, PDF...), and the return type should follow the rules that accompany ViewResolvers (check default view resolver and InternalResourceViewResolver for more).
Hope it helps.
Other sources:
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#consumes--
http://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc
Well,
consumes/produces takes String[] as a parameter (see RequestMapping from Spring's documentation) so I believe it will work. You can also try headers = "content-type=application/json,application/xml".

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