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
Related
Using spring MVC I receive multipart files in the controller this way
#RestController
public class FilesController {
#PostMapping(path = ("/files"), consumes = {"multipart/form-data", "multipart/mixed"})
public Reference createFile(
#RequestPart(value = "description") FileDescription fileDesc,
#RequestPart(value = "attachments", required = false) List<MultipartFile> attachments) {
Some parts of the multipart request may contain headers like "Content-ID", "Content-Location" and so on. But spring interface MultipartFile doesn't provide a method to get any header I want, only getContentType as I see. How I can get all provided headers?
Important point is that in request I could have multipart/mixed as a part of multipart/form-data. So every part of the message has its own map of headers. If I use #RequestHeader, I can see main headers of the request, but there are no headers of a specific part of multipart.
There might be another way, but the one I know of is to ask for a MultipartHttpServletRequest in your method signature.
#PostMapping(path = ("/files"), consumes = {"multipart/form-data", "multipart/mixed"})
public Reference createFile(MultipartHttpServletRequest multipartRequest)
You can ask for other arguments if need be.
This object allows you to access details of the multipart in a finer-grained way. For example, you can access each part's header using getMultipartHeaders(String paramOrFileName). You also have methods to access the files content this way, so you would not typically need to keep you #RequestPart inside the method signature.
We can also use javax.servlet.http.Part instead of MultipartFile. Interface Part has getHeader method.
#RequestPart(value = "attachments", required = false) List<Part> attachments
You can get all request headers by using this
#RequestHeader Map<String,String> headers After that, you can search for the header you are looking for.
You can use #RequestHeader annotation to retrieve all the headers from the request, like this:
#RestController
public class FilesController {
#PostMapping(path = ("/files"), consumes = {"multipart/form-data", "multipart/mixed"})
public Reference createFile(
#RequestHeader Map<String, String> headersMap
) {
// Use headersMap here
}
or if you want a single header's value, then you can specify the name of the header in #RequestHeader annotation, like this:
#RestController
public class FilesController {
#PostMapping(path = ("/files"), consumes = {"multipart/form-data", "multipart/mixed"})
public Reference createFile(
#RequestHeader("Content-ID") String contentId,
#RequestHeader("Content-Location") String contentLocation,
) {
// Use contentId, contentLocation here
}
long story short: I'm creating API that is supposed to be 100% REST.
I'm trying to overwrite default response for the following case:
I've got a method in my #RestController that has #RequestBody as an attribute
#RequestMapping(value = {"register"}, method = RequestMethod.POST, produces = "application/hal+json")
public Resource<User> registerClient(#RequestBody User user, HttpServletRequest request)
and the method is working just fine if I send a proper request. But there is a problem when I don't. When a request has empty body, I get a generic Tomcat error page for status 400 and I need it to send just a string or a JSON object instead.
So far I tried to add Exception Handlers in my RestControllerAdvice for all Spring exceptions from package org.springframework.web.binding, but it didn't work either.
I'm already aware that for some security-related errors one have to create handlers in configuration, but I don't know if this is the case.
Did anyone face similar issues? Is there something I'm missing?
The solution was to simply put required = false in RequestBody annotation. After that, I could easily add some logic to throw custom exception and handle it in ControllerAdvice.
#RequestMapping(value = {"register"}, method = RequestMethod.POST, produces = "application/hal+json")
public Resource<User> registerClient(#RequestBody(required = false) User user, HttpServletRequest request){
logger.debug("addClient() requested from {}; registration of user ({})", getClientIp(request), user);
if(user == null){
throw new BadRequestException()
.setErrorCode(ErrorCode.USER_IS_NULL.toString())
.setErrorMessage("Wrong body or no body in reqest");
} (...)
Firstly I suggest you to use BindingResult as a parameter of the POST call and check if it returns an error or not.
#RequestMapping(value = {"register"}, method = RequestMethod.POST, produces = "application/hal+json")
public ResponseEntity<?> registerClient(#RequestBody User user, HttpServletRequest request, BindingResult brs)
if (!brs.hasErrors()) {
// add the new one
return new ResponseEntity<User>(user, HttpStatus.CREATED);
}
return new ResponseEntity<String>(brs.toString(), HttpStatus.BAD_REQUEST);
}
Secondly, the call can throw some of errors, a good practice is to carch them and return them itself or transform them to your own exception object. The advantage is it secures a call of all the update/modify methods (POST, PUT, PATCH)
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
return new ResponseEntity<List<MethodArgumentNotValidException>>(e, HttpStatus.BAD_REQUEST);
}
#ExceptionHandler({HttpMessageNotReadableException.class})
#ResponseBody
public ResponseEntity<?> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
return new ResponseEntity<List<HttpMessageNotReadableException>>(e, HttpStatus.BAD_REQUEST);
}
Your control will never reach to your request method under normal circumstances.
If you want a looking good page you can make use of web.xml and configure it to produce your answer.
<error-page>
<error-code>404</error-code>
<location>/pages/resource-not-found.html</location>
</error-page>
Generally, if you want to go past this 400 problem, you will have to add a few annotiations to your User.java to avoid any unknown fields while de-serializing.
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 am new to Spring and I want to write a controller which will take Collection/Iterable as arguments. Like this:
#RequestMapping(value = "friends", method = RequestMethod.POST)
public #ResponseBody Callable<Iterable<User>>
getFriendsOfUser(#RequestParam(required = true, name = "mobiles") Iterable<String> mobs) {
// return callable
}
There is no compilation error, but I cannot make it work. Can you say how will this work? And how shall be the request to this api be constructed?
public String getFriendsOfUser(#RequestParam(required = true, value = "mobiles") String[] mobiless){
....
}
and your mobile should be
mobiles=myValue1&mobiles=myValue2&mobiles=myValue3
or
mobiles=myvalue1,myValue2,myValue3
still if you have any doubt post your front-end code and Ajax call.
You've mapped a POST method so you might need #RequestBody instead of #RequestParam
#RequestParam is, as the name implies, for request parameters: [host]/endpoint?param=foo&secondParam=bar
whereas
#RequestBody is for JSON/XML or any other type content sent as the request's body.
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.