I am learning Spring MVC from Spring in Action 3rd Edition and came across usage of path variables. I was not clear on how it works based on the example given in the book, please help me in understanding the concept here:
#RequestMapping(method=RequestMethod.POST)
public String addSpitterFromForm(#Valid Spitter spitter, BindingResult bindingResult) {
if(bindingResult.hasErrors()){
return"spitters/edit";
}
spitterService.saveSpitter(spitter);
return "redirect:/spitters/" + spitter.getUsername();
}
As for the path that it’s redirecting to, it’ll take the form of /spitters/{username} where {username} represents the username of the Spitter that was just submitted. For example, if the user registered under the name habuma, then they’d be redirected to /spitters/habuma after the form submission.
In above statement, it says the request is redirected to /spitters/habuma where habuma is user name.
#RequestMapping(value="/{username}",method=RequestMethod.GET)
public String showSpitterProfile(#PathVariable String username, Model model){
model.addAttribute(spitterService.getSpitter(username));
return "spitters/view";
}
For example, if the request path is /username/habuma, then habuma will be passed in to showSpitterProfile() for the username.
and here it says the showSpitterProfile() method handles requests for /username/habuma which is contradicting with statement that is mentioned earlier.
It looks like the first statement itself is correct, but please tell me if the method showSpitterProfile handles both the URLs i.e /splitters/habuma and /username/habuma or /spitters/username/habuma?
There is no /username path component if the #RequestMapping on the class level (not shown in your question) is only #RequestMapping("/spitter"). There is probably a typo in the book. Correct sentence would be:
For example, if the request path is /spitter/habuma, then habuma will be passed in to showSpitterProfile() for the username.
Related
Here is some code:
1.
#GetMapping(path = "/register", produces = MediaType.TEXT_HTML_VALUE)
public String register(#RequestParam("name") String name,
RedirectAttributes redirectAttributes) {
// call to service
redirectAttributes.addAttribute("name", name);
return "redirect:/success";
}
This endpoint gets hit first and registers the user with provided name and redirects to the success page. It also supplies the user name.
2.
#GetMapping(value = "/success", produces = MediaType.TEXT_HTML_VALUE)
public String success(#ModelAttribute("name") String name, Model model) {
model.addAttribute("name", name);
return "success";
}
This endpoint changes the path in browser URL tab to /success and displays success.html template. It also sets the username in model so that on we can show on UI that ${name} registered successfully.
success.html is a template in /templates folder, which we want to show once this operation is over on /success page.
Everything works as expected. The problem? name of the use shows up in URL on /success page.
So after registration, while we expect the URL to be just /success, it actually is /success?name=John. It is possible to hide the request parameter part? Or to send the data in body somehow rather than request parameter.
Thanks in advance, let me know if any other detail is required.
Sure. It can be done for example by using redirectAttributes.addFlashAttribute("name", name) instead of redirectAttributes.addAttribute("name", name) in the first controller.
You can get more info here.
Before asking question first i describe the scenario. I have multiple servlet contexts. suppose /**book**/review url is corresponding to book-servlet.xml and /**report**/update corresponds to report-servlet.xml.
Here are two controllers
#Controller
#SessionAttributes(BookController.COMMAND_NAME)
public class BookController {
public static final String COMMAND_NAME = "login";
#GetMapping("/book/review")
public String show(ModelMap modelMap) {
modelMap.put(COMMAND_NAME, getBook());
return "redirects:" + "/report/update"; //it redirects to second controller method
}
}
#Controller
#SessionAttributes(ReportController.COMMAND_NAME)
public class ReportController {
public static final String COMMAND_NAME = "report";
#GetMapping("/report/update")
public String show(ModelMap modelMap) {
modelMap.put(COMMAND_NAME, getReport());
return "redirects:" + "/report/done";
}
}
Observe this two controller. When i put getBook() in model it stores this object in session after method excution since #SessionAttributes(BookController.COMMAND_NAME) is added above class definition. But, after redirection to /report/update (different servlet context) we are also trying to put getReport() in model. And after handler method excution spring should put the object in session as it does for first case.Unfortunately for second controller it doesn't store it in session.
My question is if the first controller method can successfully save it in session why the second controller can't? Or is it for servlet context switching / multiple get requests or something like this. Please i need to know the reason it behaves weirdly. I am now totally confused about #SessionAttributes. It should work same for both controllers.
After studying on this i found that it is wise to use RedirectAttributes for this type of redirect scenario. Cause ModelMap is intended for scenario where you use this ModelMap attributes to rendering a view. And FlashAttribute will survive after immediate redirection to another handler method and then be erased. So, i did my solution using RedirectAttrinute support from spring.
I want to return a model from my Spring MVC controller, which will be visible to all pages in my application.
So, I need a variable which return some user details.
This is my code:
#RequestMapping(value = "/*", method = RequestMethod.GET)
public void status(Model model){
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
String username = auth.getName();
Manager mgr = managerService.findOne(username);
model.addAttribute("buget", mgr.getTeam().getTeamBuget());
}
And in my jsp page, i write something like this:
<li> <c:out value="${buget}" /> <span class="glyphicon glyphicon-euro"></span></li>
I want to be able to print the ${buget} in every page from my app.
So, my method don't work, nothing is appear in my pages and the biggest problem
for me is that I don't get any exceptions or errors which could help me. Who can help me with some advices?
I'm not entirely sure what it is that you're trying to do. Do you mean that you want buget to be part of every page that you hit? If so, you have to insert it into the model. Based on your code, I'm assuming that you have the mistaken impression that status is going to be called regardless of whatever page you hit. Spring will resolve to the most-specific handler and so if you have another handler method in another controller that is more specific, Spring will use that. But even if the one you have was called, how would Spring know that it has to call the most-specific one next? So how would you add page-dependent model attributes to the model?
If you want buget to be part of every response, you can use #ControllerAdvice (see here for a blog post with more details):
#ControllerAdvice
public class BugetControllerAdvice {
#ModelAttribute
public void addBugetToModel(Model model) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();
Manager mgr = managerService.findOne(username);
model.addAttribute("buget", mgr.getTeam().getTeamBuget());
}
}
Spring will now call this method before every handler-method in every controller. You can also take a look at the Spring Reference for more information.
i would like to redirect a request something like this
localhost:8080 /firstSpringProject/{uniqueusername}
to a specific controller named 'profile':
#RequestMapping(value="/profile")
public String profiles(Model model){
based on the uniqueusername i would like to render a profile page
return "profile";
}
I am using spring mvc; how can I resolve this situation is there any other way to do this?
Spring documentation says on redirect view:
Note that URI template variables from the present request are
automatically made available when expanding a redirect URL and do not
need to be added explicitly neither through Model nor
RedirectAttributes. For example:
#RequestMapping(value = "/files/{path}", method = RequestMethod.POST)
public String upload(...) {
// ...
return "redirect:files/{path}";
}
Keep in mind that version lower than 3.1.4 are affected by a memory leak due to caching redirect views.
If you are using Spring 3.1.3 or lower and you are doing this
return "redirect : profile?username="+username;
you will see OutOfMemoryError sometime.
I think you may use spring path variable here. You have to create a controller method that will take username as per your URL requirement and will redirect to profile method with username parameter.
#RequestMapping(value="/{username}")
public String getUserName(Model model,#PathVariable("username") String username){
//process username here and then redirect to ur profile method
return "redirect : profile?username="+username;
}
#RequestMapping(value="/profile")
public String profiles(Model model,String username){
//have a username and render a profile page
return "profile";
}
Thank you
I want to use a normal spring mvc controler and request mapping using path variables.
I do not want to forward or redirect, just change the string that user sees.
#RequestMapping(value = "/Foo/{id}/*", method = RequestMethod.GET)
public ModelAndView getFoo(#PathVariable final String friendlyUrl) {
//how can I rewite the url that user sees ?
}
(the same behaviour as when you change the title of an existing question on stackoverflow)
If you watch the traffic in wireshark, firebug or something you see, that stackoverflow sends a HTTP 301 Moved Permanently to the final URL.
You could do the same.
For this you need the HttpServletResponse, you can add it to the method signature to get it injected.
Set the permanent redirect:
String rightUrl = urlCompleter.complete(friendlyUrl);
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
response.setHeader("Location", rightUrl);
Where you need to implement urlCompleter on your own, eg. look in the database table of entries and locate the right url component.