Spring MVC send object as #RequestBody [duplicate] - java

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

Related

How to design API in Spring MVC?

I have a Spring MVC controller but I'm not sure that it is a good or bad design. As far as I know, api versioning is missing but apart from that I implemented Swagger for documentation and added SpringSecurity and tried to follow YARAS(Yet Another RESTful API Standard) to build it but I need another eye on that to comment it.
#Slf4j
#Controller
#RequestMapping
#RequiredArgsConstructor
public class XGameController implements GameController {
private final GameService gameService;
private final ObjectMapper mapper;
#RequestMapping(value = "/", method= RequestMethod.GET)
public String index() {
return "game";
}
#RequestMapping(value = "/login", method= RequestMethod.GET)
public String login() {
return "login";
}
#Secured("ROLE_USER")
#RequestMapping(value = "/games", method= RequestMethod.POST)
public String initializeGame(Model model) {
log.info("New XGame is initializing...");
Game game = new Game();
game = gameService.initializeGame(game.getId());
try {
model.addAttribute("game", mapper.writeValueAsString(game));
} catch (JsonProcessingException e) {
log.error(e.getMessage());
}
log.info("New XGame is initialized successfully!");
return "game";
}
#Secured("ROLE_USER")
#RequestMapping(value = "/games/{gameId}", method= RequestMethod.PUT)
public #ResponseBody Game play(#PathVariable("gameId") String gameId,
#RequestParam Integer pitNumber,
#RequestParam String action) {
log.info("Sowing stone is triggered...");
return gameService.executeGameRules(UUID.fromString(gameId), pitNumber);
}
#RequestMapping(value = "/403", method= RequestMethod.GET)
public String error403() {
return "/error/403";
}
}
My swagger snapshot;
I would make some changes.
In /games/{gameId} I would use PATCH instead of PUT. The reason is that PUT is intended to completely replace the resource (in your case, the Game). This does not seem to be what you are doing in this endpoint. PATCH is intended to partially update a resource, which seems much more suited to what you are doing here.
Still in /games/{gameId} I would use the request body to provide the needed data instead of query parameters. It simply doesn't seem right. Query parameters are way more suited to GET requests than to POST, PUT or PATCH.
I would rename /403 to something else that actually gives some context about what 403 is. Having said this, I would go with /error-pages/403. Additionally, I would also consider removing this endpoint from the swagger specification.
Other than this, it seems fine to me.
Some advices :
Use a path that represents the context or the idea of your controller and you can add the version
#RequestMapping("/V1/xgame")
Use specialized annotations such as : #GetMapping, #PostMapping etc...
For /403 use a meaning full name such as /errors
Use custom message that you will return to the users. For that you need a ControllerAdvice.
Google on patterns and best practices Rest API design
Read some books for better undertanding.
Firstly, instead of #RequestMapping use a specific Mapping(Get, Post,etc.) and the use of type of mapping is up to you which you find more particular to the cause of using it
if you are redirecting from a page to homepage try to use return "redirect:"/url"" instead of just returning HTML file directly.
Rename your method for error, RequestMapping value to some more reasonable name.
instead of using return "/error/403"
use return "redirect:/error/403"

InitBinder for method

I have a rest controller:
#RestController
public abstract class CrudController {
#RequestMapping(
path = "delete",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON,
consumes = MediaType.APPLICATION_JSON)
public ResponseEntity<ResponseDTO<Void>> delete(
#RequestBody IdDTO request,
#RequestHeader("X-auth-token") String token,
BindingResult bindingResult
) {
//delete logic
}
#RequestMapping(
path = "read",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON,
consumes = MediaType.APPLICATION_JSON)
public ResponseEntity<ResponseDTO<DTO>> read(
#RequestBody IdDTO<User> request,
#RequestHeader("X-auth-token") String token,
BindingResult bindingResult
) {
//read logic
}
}
I wanted to add Spring Validation. It is obvious that for read method and for delete method should be different validators. I want to know it it possible to make two #InitBinder one for read and another for delete?
I think you're not quite familiar with usage of BindingResult. It is used in process of binding object from request to Java object.
It searches the class validator, or uses validation annotations used on class' fields. Object you want to bind needs to be annotated with #Valid.
For example:
#PostMapping
public ResponseEntity<Note> addNote(#Valid Note note, BindingResult result) {
if(result.hasErrors())
return ResponseEntity.badRequest().build();
noteService.save(note);
return ResponseEntity.ok(noteService.getNoteById(note.getId()));
}
What you're trying to do is validate something, but not quite bind it.
I also suggest using different http methods for delete and read(GET) in the same path rather than POST method in different paths.

Spring MVC - parameter binding

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/

#RequestBody and #ResponseBody annotations in Spring

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

How to create a default method in SpringMVC using annotations?

I can't find a solution to this, and it's driving me crazy. I have #Controller mapped that responds to several methods using #RequestMapping. I'd like to tag one of those methods as default when nothing more specific is specified. For example:
#Controller
#RequestMapping("/user/*")
public class UserController {
#RequestMapping("login")
public String login( MapModel model ) {}
#RequestMapping("logout")
public String logout( MapModel model ) {}
#RequestMapping("authenticate")
public String authenticate( MapModel model ) {}
}
So /user/login -> login method, /user/logout -> logout, etc. I'd like to make it so that if someone goes to /user then it routes to one of these methods. However, I don't see anything on #RequestMapping that would allow me to specify one of these methods as a default handler. I also don't see any other annotations that might be used on the class either to do this. I'm beginning to suspect it doesn't exist.
I'm using Spring 2.5.6. Is this solved in 3.0.0? I might just hack Spring to make it work because it's tremendously annoying this isn't more straightforward.
Thanks in Advance.
Take a look at this answer:
Spring MVC and annotated controllers issue
What if you annotate a method with:
#RequestMapping(method = RequestMethod.GET)
You can see an example here:
Spring 3.0 MVC + Hibernate : Simplified with Annotations – Tutorial
The same behavior can be seen here:
Spring Framework 3.0 MVC by Aaron Schram (look at page 21)
Short answer: I do not know how to simply specify one method as default with a simple tag.
But there is this ...
I do not know in which version of Spring this was implemented, but you can provide multiple values to #RequestMapping in 3.1.2. So I do this:
#Controller
#RequestMapping("/user")
public class UserController {
#RequestMapping(value = {"", "/", "/list"}, method = RequestMethod.GET)
public String listUsers(ModelMap model) { }
#RequestMapping(value = "/add", method = RequestMethod.POST)
public ModelAndView add(HttpServletRequest request, ModelMap model) { }
}
The following URLs then map to listUsers():
http://example.com/user
http://example.com/user/
http://example.com/user/list
I would create one default method without RequestMapping's value in there. Please see method defaultCall() below. You can then simply call them with URL: [yourhostname]/user
#Controller
#RequestMapping("/user")
public class UserController {
#RequestMapping(method = RequestMethod.GET)
public String defaultCall( MapModel model ) {
//Call the default method directly, or use the 'forward' String. Example:
return authenticate( model );
}
#RequestMapping("login")
public String login( MapModel model ) {}
#RequestMapping("logout")
public String logout( MapModel model ) {}
#RequestMapping("authenticate")
public String authenticate( MapModel model ) {}
}
Ref: Spring Framework Request Mapping
Simply using #RequestMapping("**") on your default method should work. Any more specific mappings should still pick up their requests. I use this method for setting up default methods sometimes. Currently using Spring v4.3.8.RELEASE.

Categories

Resources