I'm editing a web project that uses Spring and I need to adding some of Spring's annotations. Two of the ones I'm adding are #RequestBody and #RequestParam. I've been poking around a little and found this, but I still don't completely understand how to use these annotations. Could anyone provide an example?
Controller example:
#Controller
class FooController {
#RequestMapping("...")
void bar(#RequestBody String body, #RequestParam("baz") baz) {
//method body
}
}
#RequestBody: variable body will contain the body of the HTTP request
#RequestParam: variable baz will hold the value of request parameter baz
#RequestParam annotated parameters get linked to specific Servlet request parameters. Parameter values are converted to the declared method argument type.
This annotation indicates that a method parameter should be bound to a web request parameter.
For example Angular request for Spring RequestParam(s) would look like that:
$http.post('http://localhost:7777/scan/l/register', {params: {"username": $scope.username, "password": $scope.password, "auth": true}}).
success(function (data, status, headers, config) {
...
})
#RequestMapping(method = RequestMethod.POST, produces = "application/json", value = "/register")
public Map<String, String> register(Model uiModel,
#RequestParam String username, #RequestParam String password, boolean auth,
HttpServletRequest httpServletRequest) {...
#RequestBody annotated parameters get linked to the HTTP request body. Parameter values are converted to the declared method argument type using HttpMessageConverters.
This annotation indicates a method parameter should be bound to the body of the web request.
For example Angular request for Spring RequestBody would look like that:
$scope.user = {
username: "foo",
auth: true,
password: "bar"
};
$http.post('http://localhost:7777/scan/l/register', $scope.user).
success(function (data, status, headers, config) {
...
})
#RequestMapping(method = RequestMethod.POST, produces = "application/json", value = "/register")
public Map<String, String> register(Model uiModel,
#RequestBody User user,
HttpServletRequest httpServletRequest) {...
Hope this helps.
Related
If I have a method that has like 10 request parameters and I may not always need all of them
#GetMapping("/whatever")
public ResponseEntity<String> sendSomethingBack(#RequestParam String optionalRequestParam1,
#RequestParam String optionalRequestParam2,
...
#RequestParam String optionalRequestParam10)
So in this header I'd like something that is like
#GetMapping("/whatever")
public ResponseEntity<String> sendSomethingBack(#RequestParam RequestParamBuilder requestParamBuilder)
and then it would just build an object for me with all valid parameters sent through filled out and the rest being null or something
You can have multiple parameters without defining their names by just using a Map:
#GetMapping("/whatever")
public ResponseEntity<String> sendSomethingBack(#RequestParam Map<String, Object> params) {
log.info("Params: {}", params.entrySet();
}
How to make the call:
curl --location --request GET 'http://localhost:8080/whatever?integer=45&string="some text"&boolean=true'
Output:
Params: [integer=45, string="some text", boolean=true]
If you wanted the parameters to be passed into an object, you can use a POJO as you were but remove the #RequestParam notation:
#GetMapping("/whatever")
public ResponseEntity<String> sendSomethingBack(RequestParamBuilder requestParamBuilder)
Then create a class for RequestParamBuilder. You can mark the fields inside the POJO as #NotNull etc to handle validation and build out the object with only the params that were included in the request, but the POJO must be annotated with #Valid if you wish for spring to validate it this way:
#GetMapping("/whatever")
public ResponseEntity<String> sendSomethingBack(#Valid RequestParamBuilder requestParamBuilder)
I'm trying to write a simple HTTP REST service using Spring 4.
I'm having troubles sending data to a POST endpoint
#RequestMapping(value = "/onlyPost", produces = "application/json", method
= RequestMethod.POST)
public #ResponseBody ResponseEntity<?> createUser(#RequestParam("value1")
String param1, #RequestParam("value2") String param2) {
....
}
While trying to send data with Postman, I receive a 400 message (obviously the values are setted in the request's body)
"message": "Required String parameter 'value1' is not present",
What I have noticed is that the issue is somewhat related to the headers, because when I remove the postman's header (Content-Type: application/json) everything works fine.
I tried for more than one hour fixing this by myself with no results. Any hints?
#RequestParam is used to read a URL query parameter.
http://localhost:8080/springmvc/onlyPost?value1=foo&value2=bar
For instance, in the URL above, value1 and value2 are query parameters that you can read using that annotation.
But if you want to read a JSON request instead, you need to change the method to:
#RequestMapping(value = "/onlyPost", method = RequestMethod.POST,
consumes = "application/json")
public #ResponseBody ResponseEntity<?> createUser(#RequestBody User user) {
....
}
where User is a POJO holding the two fields:
public class User {
private String value1;
private String value2;
// getters and setters...
}
HTTP 400 is returned when your request is badly formatted, i.e. missing required request parameters
#RequestParam is for URL Params, if you want to pass them like that, you call
<api_url>/onlyPost?value1=<value1>&value2=<value2>
but... if you want to create user you should rather use #RequestBody and put your user data there. Something like that:
#RequestMapping(value = "/users", produces = "application/json", method
= RequestMethod.POST)
public #ResponseBody ResponseEntity<?> createUser(#RequestBody User user) {
[...]
}
if you are creating REST api you should use concrete endpoints, here is a pretty cool reading with some tips: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
I have a Spring boot app where I have an API that takes other urls as path params. For example:
host:port/{eid} is my base path and after this I can have URLs like
host:port/{eid}/abc
host:port/{eid}/abc/pqr/
host:port/{eid}/abc/pqr/b=2
host:port/{eid}/abc/pqr/xyz
host:port/{eid}/abc/pqr/xyz?a=1
...and so on...
I would like to define a controller that I can map to all the above URLs and that should work something like
#RequestMapping(value = "/{eid}/{urlParts}", method = RequestMethod.GET)
public ResponseEntity<Object> share(
#PathVariable String eid,
#PathVariable String urlParts) {
......
}
I tried using #PathVariable Map<String, String> path and also #RequestMapping(value = "/{eid}/{urlParts:.+}"
but couldn't get the expected result.
Is there any solution to receive path slash(/) in path param.
Note: I can not URL encode the slash(/) in the URL. That's not an option for me.
I know the query is too old but still it's useful and this answer can help others.
You can get the full url parts using request attribute as below.
#RequestMapping(value = "/{eid}/**", method = RequestMethod.GET)
public ResponseEntity<Object> share(#PathVariable String eid, HttpServletRequest request) {
Object uriObject = request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
if (null != uriObject) {
String urlParts = uriObject.toString().replaceFirst("^/" eid + "/", "");
}
....
}
why don't you try #RequestParam to take url if you working with jsp or other stuff..
#PathVariable means that the annotated method argument should be extracted from the path of the invoked URL. #RequestParam means that the annotated method argument must be extracted from the request parameters. None of these annotations cause the annotated arguments to be put in the request, session or application scope.
so you use your map also...
${username} means "write the value of the username attribute (found in page, or request, or session, or application scope) in the response". Since you didn't include any username attribute in any of those scopes, it doesn't write anything.
The code would work if the method returned a ModelAndView object, and the model contained a username attribute and a studentid attribute.
you can refer below code and link :
First URL : localhost:8080/projectName/test?firstname=john
Second URL :localhost:8080/projectName/test?firstname=john&secondname=roy
#Controller
#RequestMapping("/test")
public class TestController {
#RequestMapping(value = { "/test/{firstname}/test" }, method = { RequestMethod.GET })
public String someMethod(#PathVariable("firstname") String firstname){
return someMethod(firstValue )
}
#RequestMapping(value = { "/test/{firstname}/{otherString}/test" }, method = { RequestMethod.GET })
public String someOtherMethod(#PathVariable("firstname") String firstname, #PathVariable("secondname") String secondValue) {
return someMethod(firstValue + "/" + secondValue)
}
}
so I am not sure if there is a direct spring implementation to doing this however, you could us a mixture of things.
#RequestParam - returns a map of the URL params (succeeding the ?)
#PathVariable - return the eid
HttpServletRequest - use the request to return the URI and strip host:port/{eid} and anything after ? , then use Arrays.asList(str.split("/")); (remember this is a wrapper of an array use new ArrayList<Sting>(Arrays.asList(str.split("/"))) )
#RequestMapping(value = "/{eid}", method = RequestMethod.GET)
public ResponseEntity<Object> share(
#PathVariable String eid,
#RequestParam Map<String,String> allRequestParams,
HttpServletRequest request) {
......
}
I have implemented a Request filter (extends OncePerRequestFilter)
and I return a HttpServletRequestWrapper where I have overridden the
ACCEPT header.
When the controller method is invoked:
#ResponseBody
public Foo getFoo(HttpServletRequest request,
#RequestHeader(value=ACCEPT) String injectedAcceptHeader,
#PathVariable ("fooId") String fooId) {
log.info("injected accept header: {}", injectedAcceptHeader);
log.info("accept header value from request: {}",
request.getHeader(ACCEPT));
...
The injectedAcceptHeader is not the same as the one from the Request.
Is this expected behaviour?
I'm using JSR 303 #Valid to validate multiple request params in a controller, the params posted along with a MultipartFile.
The validation part of this seems to be working,
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public #ResponseBody
Response upload(#RequestParam(value = "file", required = true) MultipartFile file,
#Valid ValidBean bean) {
//method
}
ValidBean is a collection of Strings, Longs and a List<String>.
public class ValidBean{
#NotNull
String someString;
#Size(min = 1, max=10)
String anotherString;
//getters, setters, random been goodness
}
It seems that the client is getting rejected if the posted params do not match what is defined in ValidBean.
Where I am having an issue is with my global #ControllerAdvice ValidationHandler.
#ControllerAdvice
public class ValidationHandler {
#ExceptionHandler
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public #ResponseBody
StatusContext handleArgumentNotValid( MethodArgumentNotValidException error ){
//format and return response
}
My #ExceptionHandler only seems to be used in methods that use #RequestBody #Valid
So this method returns a formatted response,
#RequestMapping(value = "/works", method = RequestMethod.POST)
public #ResponseBody
Formatted addUser(#RequestBody #Valid ValidBean user)
And this one does not,
#RequestMapping(value = "/noGood", method = RequestMethod.POST)
public #ResponseBody
NotFormatted addUser(#Valid ValidBean user)
Though both do seem to actually perform validation.
The documentation for #Valid #RequestBody has this to say:
Just like with #ModelAttribute parameters, an Errors argument can be
used to examine the errors. If such an argument is not declared, a
MethodArgumentNotValidException will be raised
that is the reason why your #ExceptionHandler with a method signature of MethodArgumentNotValidException gets called for #Valid #RequestBody.
On the other hand without #RequestBody but with #Valid, without an additional BindingResult parameter, a BindException gets generated, this will not be handled by the current specific signature of your #ExceptionHandler. The fix may be to make your #ExceptionHandler a little broader or add another #ExceptionHandler for BindException. Even better may be to just add BindingResult as an additional parameter.