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.
Related
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"
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.....
In Jersey 2 it is possible to do this:
#GET
#PATH("user/{email}")
public IDto getUser(#NotNull #Email #PathParam("email") String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
But I cannot make something similar to work in Spring MVC, it seems that the validation is only done when providing an object in #RequestBody or using an SpringMVC Form, for example the following won't work:
#RequestMapping(value="/user/{email}", method = RequestMethod.GET)
public #ResponseBody IDto getUser(#NotNull #Email #PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
There are other similar questions, but those seem to be oriented to Spring MVC UI applications, in my case it is only a REST API which returns JSON response so I don't have any View to map/bind to the controller.
Seems it is possible, using #Validated.
Here's an example.
Based on OP's question, this should work:
#RestController
#Validated
public class MyController {
#GetMapping(value="/user/{email}")
public #ResponseBody IDto getUser(#NotNull #Email #PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
}
In plain Spring implementations, it may be required to manually register the validator bean:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
1- Simply add #Validated annotation at the top of your class.
2- Put whatever annotations for validations (#NotBlank, Min(1), etc.) before the #RequestParam annotation in your method signature.
The validated annotation from the org.springframework.validation.annotation.Validated package to validate a #PathVariable. Make sure the class annotated with #Validated.
#GetMapping("/name-for-day/{dayOfWeek}")
public String getNameOfDay(#PathVariable("dayOfWeek") #Min(1) #Max(7) Integer dayOfWeek) {
return dayOfWeek + "";
}
As far as I can tell, you cannot do this out-of-the-box with Spring.
Options:
Use a regular expression:
#RequestMapping(value="/user/{email:SOME_REXEXP}", method = RequestMethod.GET)
public #ResponseBody IDto getUser(#PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
Use Hibernate Validator to validate the method. Either call the validator manually, or make Spring call it for you using AOP. See https://github.com/gunnarmorling/methodvalidation-integration
Controller should be annotated with spring's #Validated
So update your code with
#Validated
#RequestMapping(value="/user/{email}", method = RequestMethod.GET)
public #ResponseBody IDto getUser(
#NotNull
#Email
#PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
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.....
The question sounds weird, I'm playing around with Spring MVC and am trying to move between two pages and basically I'm creating a JSP page using Spring Form JSTL's so it just uses a POST, and I use a controller to move from one page to the next. But Models are lost from page to page, and I'd like to hide the actual variable so QueryStrings are out of the question(as
far as I know). I know I can use a InternalResourceView, but only allows me to use a model.
I want to transfer a variable that will be exclusive to that page, what's the best way without a model or using QueryStrings?
I was planning on using SessionAttribute to easily define them, but was wondering, how do you remove a SessionAttribute created variable? I tried HttpSession.removeAttribute and it didn't seem to work.
You can also use SessionStatus.setComplete() like this:
#RequestMapping(method = RequestMethod.GET, value="/clear")
public ModelAndView clear(SessionStatus status, ModelMap model, HttpServletRequest request) {
model.clear();
status.setComplete();
return new ModelAndView("somePage");
}
or DefaultSessionAttributeStore.cleanUpAttribute like this:
#RequestMapping(method = RequestMethod.GET, value="/clear")
public ModelAndView clear(DefaultSessionAttributeStore status, WebRequest request, ModelMap model) {
model.remove("mySessionVar");
status.cleanupAttribute(request, "mySessionVar");
return new ModelAndView("somePage");
}
I use it like this on one of my forms that has mulitple sessionAttributes and I want to remove only one of them.
Yes... HttpSession.removeAttribute
You can use the removeAttribute method from the HttpSession class.
you can use WebRequest.removeAttribute(String name, int scope) that works with Spring #SessionAttributes. Quote from #SessionAttributes javadoc - "Alternatively, consider using the attribute management capabilities of the generic {#link org.springframework.web.context.request.WebRequest} interface."
Also look at my example.
#Controller
#SessionAttributes({"sessionAttr"})
public class MyController {
#ModelAttribute("sessionAttr")
public Object defaultSessionAttr() {
return new Object();
}
#RequestMapping(value = "...", method = RequestMethod.GET)
public String removeSessionAttr(WebRequest request, Model model) {
request.removeAttribute("sessionAttr", WebRequest.SCOPE_SESSION);
model.addAttribute("sessionAttr", defaultSessionAttr());
return "myView";
}
}