How come this code just works? I didn't specify any custom converter or annotation (like #RequestBody or #ModelAttribute) before argument ? Request is filled correctly from this GET call:
http://localhost:8080/WS/foo?token=C124EBD7-D9A5-4E21-9C0F-3402A1EE5E9B&lastSync=2001-01-01T00:00:00&pageNo=1
Code:
#RestController
#RequestMapping(value = "/foo")
public class FooController {
#RequestMapping(method = RequestMethod.GET)
public Result<Foo> excursions(Request request) {
// ...
}
}
Request is just POJO with getters and setters. I use it to shorten argument code because plenty methods uses those same arguments ...
public class Request {
private String token;
#DateTimeFormat(pattern = IsoDateTime.DATETIME)
private Date lastSync;
private Integer pageNo;
// getters and setters
}
This was my original method before introducing Request.
#RestController
#RequestMapping(value = "/foo")
public class FooController {
#RequestMapping(method = RequestMethod.GET)
public Result<Foo> excursions(#RequestParam String token, #RequestParam #DateTimeFormat(pattern = IsoDateTime.DATETIME) Date lastSync, #RequestParam Integer pageNo) {
// ...
}
}
Request parameters will be mapped to POJOs, as it is happening in your case, by default. Additionally, if you use #ModelAttribute, an attribute in the Model will be created. That attribute can be then used in views, e.g. JSPs, to access the object.
#RequestBody annotation tells that the body of the request is NOT a set of form parameters like
token=C124EBD7-D9A5-4E21-9C0F-3402A1EE5E9B&lastSync=2001-01-01T00:00:00&pageNo=1
but is in some other format, such as JSON.
This is a feature provided by Spring MVC:
Customizable binding and validation. Type mismatches as application-level validation errors that keep the offending value, localized date and number binding, and so on instead of String-only form objects with manual parsing and conversion to business objects.
You can see it in the doc: http://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/htmlsingle/
Related
I'm developing a REST API backend with Spring for a Slack App. I was able to receive messages from Slack (the slash commands) but I'm not able to properly receive component interactions (button clicks).
The official documentation says:
Your Action URL will receive a HTTP POST request, including a payload body parameter, itself containing an application/x-www-form-urlencoded JSON string.
therefore I have written the following #RestController:
#RequestMapping(method = RequestMethod.POST, value = "/actions", headers = {"content-type=application/x-www-form-urlencoded"})
public ResponseEntity action(#RequestParam("payload") ActionController.Action action) {
return ResponseEntity.status(HttpStatus.OK).build();
}
#JsonIgnoreProperties(ignoreUnknown = true)
class Action {
#JsonProperty("type")
private String type;
public Action() {}
public String getType() {
return type;
}
}
however I get the following error:
Failed to convert request element: org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type 'controllers.ActionController$Action'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'controllers.ActionController$Action': no matching editors or conversion strategy found
What does it mean, and how to resolve?
You receive a string that contains a JSON content. You don't receive a JSON input as application/x-www-form-urlencoded is used as content type and not application/json as stated :
Your Action URL will receive a HTTP POST request, including a payload
body parameter, itself containing an application/x-www-form-urlencoded
JSON string.
So change the parameter type to String and use Jackson or any JSON library to map the String to your Action class :
#RequestMapping(method = RequestMethod.POST, value = "/actions", headers = {"content-type=application/x-www-form-urlencoded"})
public ResponseEntity action(#RequestParam("payload") String actionJSON) {
Action action = objectMapper.readValue(actionJSON, Action.class);
return ResponseEntity.status(HttpStatus.OK).build();
}
As pvpkiran suggests, you could have replaced #RequestParam by #RequestBody if you could pass the JSON string directly in the body of the POST request, and not as a value of a parameter but it seems that is not the case there.
Indeed by using #RequestBody, the body of the request is passed through an HttpMessageConverter to resolve the method argument.
To answer to your comment, Spring MVC doesn't provide a very simple way to achieve your requirement : mapping the String JSON to your Action class.
But if you really need to automatize this conversion you have a lengthy alternative as stated in the Spring MVC documentation such as Formatters (emphasis is mine) :
Some annotated controller method arguments that represent String-based
request input — e.g. #RequestParam, #RequestHeader, #PathVariable,
#MatrixVariable, and #CookieValue, may require type conversion if the
argument is declared as something other than String.
For such cases type conversion is automatically applied based on the
configured converters. By default simple types such as int, long,
Date, etc. are supported. Type conversion can be customized through a
WebDataBinder, see DataBinder, or by registering Formatters with the
FormattingConversionService, see Spring Field Formatting.
By creating a formatter (FormatterRegistry subclass) for your Action class you could add that in the Spring web config as documented :
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void addFormatters(FormatterRegistry registry) {
// ... add action formatter here
}
}
and use it in your parameter declaration :
public ResponseEntity action(#RequestParam("payload") #Action Action actionJ)
{...}
For simplicity, you could use the code block below. #Request body maps the the payload to the Action class. It also validates to make sure that the type is not blank. The #Valid and #NotBlank is from javax.validation package.
#PostMapping("actions")
public ResponseEntity<?> startApplication(#RequestBody #Valid Action payload) {
// use your payload here
return ResponseEntity.ok('done');
}
class Action {
#NotBlank
private String type;
public Action() {
}
public String getType() {
return type;
}
}
Can someone explain the #RequestBody and #ResponseBody annotations in Spring 3? What are they for? Any examples would be great.
There is a whole Section in the docs called 16.3.3.4 Mapping the request body with the #RequestBody annotation. And one called 16.3.3.5 Mapping the response body with the #ResponseBody annotation. I suggest you consult those sections. Also relevant: #RequestBody javadocs, #ResponseBody javadocs
Usage examples would be something like this:
Using a JavaScript-library like JQuery, you would post a JSON-Object like this:
{ "firstName" : "Elmer", "lastName" : "Fudd" }
Your controller method would look like this:
// controller
#ResponseBody #RequestMapping("/description")
public Description getDescription(#RequestBody UserStats stats){
return new Description(stats.getFirstName() + " " + stats.getLastname() + " hates wacky wabbits");
}
// domain / value objects
public class UserStats{
private String firstName;
private String lastName;
// + getters, setters
}
public class Description{
private String description;
// + getters, setters, constructor
}
Now if you have Jackson on your classpath (and have an <mvc:annotation-driven> setup), Spring would convert the incoming JSON to a UserStats object from the post body (because you added the #RequestBody annotation) and it would serialize the returned object to JSON (because you added the #ResponseBody annotation). So the Browser / Client would see this JSON result:
{ "description" : "Elmer Fudd hates wacky wabbits" }
See this previous answer of mine for a complete working example: https://stackoverflow.com/a/5908632/342852
Note: RequestBody / ResponseBody is of course not limited to JSON, both can handle multiple formats, including plain text and XML, but JSON is probably the most used format.
Update
Ever since Spring 4.x, you usually won't use #ResponseBody on method level, but rather #RestController on class level, with the same effect.
Here is a quote from the official Spring MVC documentation:
#RestController is a composed annotation that is itself meta-annotated
with #Controller and #ResponseBody to indicate a controller whose
every method inherits the type-level #ResponseBody annotation and,
therefore, writes directly to the response body versus view resolution
and rendering with an HTML template.
#RequestBody : Annotation indicating a method parameter should be bound to the body of the HTTP request.
For example:
#RequestMapping(path = "/something", method = RequestMethod.PUT)
public void handle(#RequestBody String body, Writer writer) throws IOException {
writer.write(body);
}
#ResponseBody annotation can be put on a method and indicates that the return type should be written straight to the HTTP response body (and not placed in a Model, or interpreted as a view name).
For example:
#RequestMapping(path = "/something", method = RequestMethod.PUT)
public #ResponseBody String helloWorld() {
return "Hello World";
}
Alternatively, we can use #RestController annotation in place of #Controller annotation. This will remove the need to using #ResponseBody.
for more details
Below is an example of a method in a Java controller.
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public HttpStatus something(#RequestBody MyModel myModel)
{
return HttpStatus.OK;
}
By using #RequestBody annotation you will get your values mapped with the model you created in your system for handling any specific call. While by using #ResponseBody you can send anything back to the place from where the request was generated. Both things will be mapped easily without writing any custom parser etc.
#RestController is a composed annotation that is itself meta-annotated with #Controller and #ResponseBody to indicate a controller whose every method inherits the type-level #ResponseBody annotation and, therefore, writes directly to the response body versus view resolution and rendering with an HTML template
So instead of marking your class as #Controller use #RestController instead and remove the #requestbody annotation from you class
heres an example:
#RestController
public class MomController {
#RequestMapping("/sugar") // maped to the url /sugar
public String addSugar() {
return "here is your sugar";
}
}
package com.programmingfree.springshop.controller;
import java.util.List;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.programmingfree.springshop.dao.UserShop;
import com.programmingfree.springshop.domain.User;
#RestController
#RequestMapping("/shop/user")
public class SpringShopController {
UserShop userShop=new UserShop();
#RequestMapping(value = "/{id}", method = RequestMethod.GET,headers="Accept=application/json")
public User getUser(#PathVariable int id) {
User user=userShop.getUserById(id);
return user;
}
#RequestMapping(method = RequestMethod.GET,headers="Accept=application/json")
public List<User> getAllUsers() {
List<User> users=userShop.getAllUsers();
return users;
}
}
In the above example they going to display all user and particular id details now I want to use both id and name,
1) localhost:8093/plejson/shop/user <---this link will display all user details
2) localhost:8093/plejson/shop/user/11 <----if i use 11 in link means, it will display particular user 11 details
now I want to use both id and name
localhost:8093/plejson/shop/user/11/raju <-----------------like this
it means we can use any one in this please help me out.....
I know there are validators in spring. However, these validators can only be bound to a single object. Say a Pojo in request body. However, I have a scenario where I have a get request and I want to validate a date range: I have a start date and the end date as #requestparams. How should I validate these?
Also there is a validator applied for the same #restcontroller: for post request, say Employeevalidtor. Can I invoke multiple validators for different objects in the same #restcontroller?
You can use separate validators but they have to me manually instantiated by passing the corresponding objects to be validated.
I assume you are talking about request binding validations. The same validations can be obtained with Spring Validators for #RequestParam and #PathVariables as mentioned in this post
Adding the relevant piece here. The controller will look something like this:
#RestController
#Validated
public class RegistrationController {
#RequestMapping(method = RequestMethod.GET,
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
#ResponseStatus(HttpStatus.OK)
public Map search(#Email #RequestParam("email") String email) {
return emailMessage(email);
}
}
Note the #Validated method at the class level (which can also be declared at the method level).
Let Spring MVC will map your request parameters to a pojo encapsulating all the related inputs and then add a validator for that.
#RestController
#RequestMapping("/myUrl")
public class MytController {
private final MyIntervalValidator validator;
#InitBinder
public void initBinder(WebDataBinder binder){
binder.setValidator(validator);
}
#GetMapping
public void doSomthing(#Valid #RequestParam MyInterval interval){...}
class MyInterval implements Serializable{
private Date startDate;
private Date endDate;
}
import org.springframework.validation.Validator;
class MyIntervalValidator implements Validator{
#Override
public boolean supports(Class<?> clazz) {
return MyInterval.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
final MyInterval params = (MyInterval) target;
....
}
}
Can someone explain the #RequestBody and #ResponseBody annotations in Spring 3? What are they for? Any examples would be great.
There is a whole Section in the docs called 16.3.3.4 Mapping the request body with the #RequestBody annotation. And one called 16.3.3.5 Mapping the response body with the #ResponseBody annotation. I suggest you consult those sections. Also relevant: #RequestBody javadocs, #ResponseBody javadocs
Usage examples would be something like this:
Using a JavaScript-library like JQuery, you would post a JSON-Object like this:
{ "firstName" : "Elmer", "lastName" : "Fudd" }
Your controller method would look like this:
// controller
#ResponseBody #RequestMapping("/description")
public Description getDescription(#RequestBody UserStats stats){
return new Description(stats.getFirstName() + " " + stats.getLastname() + " hates wacky wabbits");
}
// domain / value objects
public class UserStats{
private String firstName;
private String lastName;
// + getters, setters
}
public class Description{
private String description;
// + getters, setters, constructor
}
Now if you have Jackson on your classpath (and have an <mvc:annotation-driven> setup), Spring would convert the incoming JSON to a UserStats object from the post body (because you added the #RequestBody annotation) and it would serialize the returned object to JSON (because you added the #ResponseBody annotation). So the Browser / Client would see this JSON result:
{ "description" : "Elmer Fudd hates wacky wabbits" }
See this previous answer of mine for a complete working example: https://stackoverflow.com/a/5908632/342852
Note: RequestBody / ResponseBody is of course not limited to JSON, both can handle multiple formats, including plain text and XML, but JSON is probably the most used format.
Update
Ever since Spring 4.x, you usually won't use #ResponseBody on method level, but rather #RestController on class level, with the same effect.
Here is a quote from the official Spring MVC documentation:
#RestController is a composed annotation that is itself meta-annotated
with #Controller and #ResponseBody to indicate a controller whose
every method inherits the type-level #ResponseBody annotation and,
therefore, writes directly to the response body versus view resolution
and rendering with an HTML template.
#RequestBody : Annotation indicating a method parameter should be bound to the body of the HTTP request.
For example:
#RequestMapping(path = "/something", method = RequestMethod.PUT)
public void handle(#RequestBody String body, Writer writer) throws IOException {
writer.write(body);
}
#ResponseBody annotation can be put on a method and indicates that the return type should be written straight to the HTTP response body (and not placed in a Model, or interpreted as a view name).
For example:
#RequestMapping(path = "/something", method = RequestMethod.PUT)
public #ResponseBody String helloWorld() {
return "Hello World";
}
Alternatively, we can use #RestController annotation in place of #Controller annotation. This will remove the need to using #ResponseBody.
for more details
Below is an example of a method in a Java controller.
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public HttpStatus something(#RequestBody MyModel myModel)
{
return HttpStatus.OK;
}
By using #RequestBody annotation you will get your values mapped with the model you created in your system for handling any specific call. While by using #ResponseBody you can send anything back to the place from where the request was generated. Both things will be mapped easily without writing any custom parser etc.
#RestController is a composed annotation that is itself meta-annotated with #Controller and #ResponseBody to indicate a controller whose every method inherits the type-level #ResponseBody annotation and, therefore, writes directly to the response body versus view resolution and rendering with an HTML template
So instead of marking your class as #Controller use #RestController instead and remove the #requestbody annotation from you class
heres an example:
#RestController
public class MomController {
#RequestMapping("/sugar") // maped to the url /sugar
public String addSugar() {
return "here is your sugar";
}
}
package com.programmingfree.springshop.controller;
import java.util.List;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.programmingfree.springshop.dao.UserShop;
import com.programmingfree.springshop.domain.User;
#RestController
#RequestMapping("/shop/user")
public class SpringShopController {
UserShop userShop=new UserShop();
#RequestMapping(value = "/{id}", method = RequestMethod.GET,headers="Accept=application/json")
public User getUser(#PathVariable int id) {
User user=userShop.getUserById(id);
return user;
}
#RequestMapping(method = RequestMethod.GET,headers="Accept=application/json")
public List<User> getAllUsers() {
List<User> users=userShop.getAllUsers();
return users;
}
}
In the above example they going to display all user and particular id details now I want to use both id and name,
1) localhost:8093/plejson/shop/user <---this link will display all user details
2) localhost:8093/plejson/shop/user/11 <----if i use 11 in link means, it will display particular user 11 details
now I want to use both id and name
localhost:8093/plejson/shop/user/11/raju <-----------------like this
it means we can use any one in this please help me out.....
I'm running a webapp in Spring Web MVC 3.0 and I have a number of controller methods whose signatures are roughly as follows:
#RequestMapping(value = "/{level1}/{level2}/foo", method = RequestMethod.POST)
public ModelAndView createFoo(#PathVariable long level1,
#PathVariable long level2,
#RequestParam("foo_name") String fooname,
#RequestParam(value = "description", required = false) String description);
I'd like to add some validation - for example, description should be limited to a certain length or fooname should only contain certain characters. If this validation fails, I want to return a message to the user rather than just throw some unchecked exception (which would happen anyway if I let the data percolate down to the DAO layer). I'm aware of JSR303 but have not worked with it and don't quite understand how to apply it in a Spring context.
From what I understand, another option would be to bind the #RequestBody to an entire domain object and add validation constraints there, but currently my code is set up to accept individual parameters as shown above.
What is the most straightforward way to apply validation to input parameters using this approach?
This seems to be possible now (tried with Spring 4.1.2), see https://raymondhlee.wordpress.com/2015/08/29/validating-spring-mvc-request-mapping-method-parameters/
Extract from above page:
Add MethodValidationPostProcessor to Spring #Configuration class:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
Add #Validated to controller class
Use #Size just before #RequestParam
#RequestMapping("/hi")
public String sayHi(#Size(max = 10, message = "name should at most 10 characters long") #RequestParam("name") String name) {
return "Hi " + name;
}
Handle ConstraintViolationException in an #ExceptionHandler method
There's nothing built in to do that, not yet anyway. With the current release versions you will still need to use the WebDataBinder to bind your parameters onto an object if you want automagic validation. It's worth learning to do if you're using SpringMVC, even if it's not your first choice for this task.
It looks something like this:
public ModelAndView createFoo(#PathVariable long level1,
#PathVariable long level2,
#Valid #ModelAttribute() FooWrapper fooWrapper,
BindingResult errors) {
if (errors.hasErrors() {
//handle errors, can just return if using Spring form:error tags.
}
}
public static class FooWrapper {
#NotNull
#Size(max=32)
private String fooName;
private String description;
//getset
}
If you have Hibernate Validator 4 or later on your classpath and use the default dispatcher setup it should "Just work."
Editing since the comments were getting kind of large:
Any Object that's in your method signature that's not one of the 'expected' ones Spring knows how to inject, such as HttpRequest, ModelMap, etc, will get data bound. This is accomplished for simple cases just by matching the request param names against bean property names and calling setters. The #ModelAttribute there is just a personal style thing, in this case it isn't doing anything. The JSR-303 integration with the #Valid on a method parameter wires in through the WebDataBinder. If you use #RequestBody, you're using an object marshaller based on the content type spring determines for the request body (usually just from the http header.) The dispatcher servlet (AnnotationMethodHandlerAdapter really) doesn't have a way to 'flip the validation switch' for any arbitrary marshaller. It just passes the web request content along to the message converter and gets back a Object. No BindingResult object is generated, so there's nowhere to set the Errors anyway.
You can still just inject your validator into the controller and run it on the object you get, it just doesn't have the magic integration with the #Valid on the request parameter populating the BindingResult for you.
If you have multiple request parameters that need to be validated (with Http GET or POST). You might as well create a custom model class and use #Valid along with #ModelAttribute to validate the parameters. This way you can use Hibernate Validator or javax.validator api to validate the params. It goes something like this:
Request Method:
#RequestMapping(value="/doSomething", method=RequestMethod.GET)
public Model dosomething(#Valid #ModelAttribute ModelRequest modelRequest, BindingResult result, Model model) {
if (result.hasErrors()) {
throw new SomeException("invalid request params");
}
//to access the request params
modelRequest.getFirstParam();
modelRequest.getSecondParam();
...
}
ModelRequest class:
class ModelRequest {
#NotNull
private String firstParam;
#Size(min = 1, max = 10, message = "You messed up!")
private String secondParam;
//Setters and getters
public void setFirstParam (String firstParam) {
this.firstParam = firstParam;
}
public String getFirstParam() {
return firstParam;
}
...
}
Hope that helps.