Set the base URL in the page when using Spring MVC 3 - spring

I am learning the Spring MVC 3 now.
I meet some problems when set the URLs in my page, since the URL in the page are relatived to the current page, so I want to set the base URL in each page.
When I use Structs 2, I create a BaseAction like this:
public class BaseAction{
public BaseAction(){
string baseURL=getServletContext.getHost()+"...."+.....;
}
public getBaseURL(){xxxxx}
}
Then in the page:
<base href='<s:prototype value="baseURL"/>' />
Now, in Spring MVC 3, since the a new instance of related controller is not created for each request, so I do not know how to make it?
Finally, I think I can use the interceptor, so I create a interceptor:
public class BaseURLInterceptor extends HandlerInterceptorAdapter{
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
return true;
}
//after the handler is executed
public void postHandle(
HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView)
throws Exception {
modelAndView.getModel().setObject("baseURL",requset.getHost()+"......");
}
}
In the page:
I can use :
<base href="${baseURL}" />
It works, however when a view is redirected, it will add this value to the URL.
For example:
#Controller
#RequsetMapping("/user")
public class UserController{
#RequsetMapping("edit/{userid}")
public String edit(#PathVariable String uid)
//edit logic
return "redirect:list"
}
#RequsetMapping("list")
public String list(){
//get users list
return "user_list"
}
}
When I make the submit button in the user edit page, I will redirect to :
http://localhost:8080/app/user/list?baseURL=http://localhost:8080
Where the parameter in the url ?baseURL=http://localhost:8080 is not what I want.
How to fix it?
Or what is the better way to solve the URL path using Spring MVC 3?

May I do not correct understand the problem, but if you want to have the base url in the jsp for some links than your approach is the wrong one.
normaly you would do something like this:
test
And for redirects in the controller you can specify how the url has to be interpreted if you use RedirectView instead of returning the String "redirect:xxx": the redirect view constructor RedirectView(String url, boolean contextRelative) can be used to specify context relative to the current ServletContext or relative to the web server.

If I understand the problem correctly this is what you're looking for <base href="<c:url value="${webappRoot}" />

Related

Spring sending user to a view when no view is being requested

I have written a book catalog in Spring.
It collects books (pdf, epub, mobi, ebook) from a directory, collects some metadata from them, stores them in a DB and then puts them in a List that is made available to my views:
#Slf4j
#Controller
public class BookCatalogController {
// == Fields ==
private final BookService bookService;
#Autowired
public BookCatalogController(BookService bookService){this.bookService = bookService; }
// == Model attributes ==
#ModelAttribute
public List<Book> bookData(){ return bookService.getBooksFromMemory(); }
public static final File bookDirectory= new File("D:\\edu_repo\\ebooks_test\\");
.
.
.
// Catalog Simple View
#GetMapping(Mappings.CATALOG_SIMPLE)
public String catalogSimple(Model model){
log.info("catalogSimple method called");
// This is adding the entire BookManager book list into the model.
model.addAttribute(AttributeNames.BOOK_DATA, bookData());
return ViewNames.CATALOG_SIMPLE;
}
// Catalog Detail View
#GetMapping(Mappings.CATALOG_DETAIL)
public String catalogDetail(Model model){
log.info("catalogDetail method called");
// This is adding the entire BookManager book list into the model.
model.addAttribute(AttributeNames.BOOK_DATA, bookData());
return ViewNames.CATALOG_DETAIL;
}
.
.
.
#GetMapping(Mappings.LOAD_BOOKS)
public void loadBooks(Model model) {
bookService.loadBooksFromDirectory(bookDirectory);
}
}
Obviously I'm not using #GetMapping(Mappings.LOAD_BOOKS) properly as you can see in the error below:
The error:
There was an unexpected error (type=Internal Server Error, status=500).
Error resolving template [load-books], template might not exist or might not be accessible by any of the configured Template Resolvers
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [load-books], template might not exist or might not be accessible by any of the configured Template Resolvers
How does one invoke a method like I am doing but without Spring trying to redirect the user to another view?
I'm not expecting the page to update at all since I'm not returning a View!
When you click a link in your browser with a load-books anchor, your browser sends it to the server and waits for result, which causes your page to be reloaded. Once the request to a load-books endpoint reached to the server, Spring MVC handles this and starting to looking up an appropriate controller with its method. It founds public void loadBooks(Model model) in your case. When Spring MVC invokes the method, it expects to obtain a view name to resolve and return back to your browser.
Since you haven't provided a View or String as a return type, Spring MVC used the endpoint's path as a view name (I'm not seeing your Mappings.LOAD_BOOKS constant, but it supposed to be load-books).
If you're not going to return any view back to the browser, you can annotate the method like that:
#GetMapping(Mappings.LOAD_BOOKS)
#ResponseBody
public void loadBooks(Model model) {
which tells Spring to treat void as a response body.
But it's not preventing a page refreshing, you'll just see an empty page after clicking the link. In order to fix this you can redirect a user to another page by returning the following string (without ResponseBody annotation on the method)
return "redirect:/path-to-redirect";
When Spring MVC sees this prefix it redirects you to another controller, but user going to notice that too.
If you really don't want to see a blank page for a moment, you'll have to use some JavaScript to perform AJAX request to the server when button is clicked.
Actually, it seems that you want to preload some files in a service by a given path. If it's all you want to do, you can use Spring's runners like that:
#Component
class Preloader implements ApplicationRunner {
private final BookCatalogService bookService;
#Autowired
public Preloader(BookCatalogService service) {
this.bookService = service;
}
#Override
public void run(ApplicationArguments args) throws Exception {
bookService.loadBooksFromDirectory(BookCatalogController.bookDirectory);
}
}
Spring automatically calls all registered runners when application is ready, so your code will be executed without having a user to visit load-books endpoint.

flash attribute not working depending on the the redirect URL

I tested the below using Spring web MVC 4.3.5.
The following controller shows an example of a flash attribute working properly after a redirect:
#Controller
#RequestMapping("/redirection")
public class RedirectionController {
private static final Logger logger = Logger.getLogger(RedirectionController.class);
#RequestMapping(path="/pageA", method=RequestMethod.GET)
public String foo(RedirectAttributes redirectAttributes
, HttpServletRequest request
) {
redirectAttributes.addFlashAttribute("foo", "bar");
return String.format("redirect:%s", "/redirection/pageB.do");
}
#RequestMapping(path="/pageB", method=RequestMethod.GET)
public String boo(final HttpServletRequest request, final Model model) {
printModelAttributes(model);
return "page-b";
}
private static void printModelAttributes(final Model model) {
Map<String, Object> map = model.asMap();
logger.info(String.format("model attributes are: %s", map.keySet()));
}
}
When we visit the /pageA path with our browser we are automatically redirected to /pageB and the controller method for /pageB sees the flash attribute as expected:
14:35:18,740 INFO [webgui.RedirectionController] (http-/127.0.0.1:8082-3) model attributes are: [foo]
However, if I change the return statement the produces the redirect String from:
return String.format("redirect:%s", "/redirection/pageB.do");
… to:
return String.format("redirect:%s", "/.."+request.getContextPath()+"/redirection/pageB.do");
… we are still redirected to the same /pageB path but this time the flash attribute is missing:
14:34:51,892 INFO [webgui.RedirectionController] (http-/127.0.0.1:8082-3) model attributes are: []
I am assuming that this is due to Spring MVC trying to be clever and trying to guess if the redirection will take us to another application (different context path) in which case it does not bother placing the flash attributes in the session as they won't be available anyways in the other application.
Apparently the presence of the /.. in the redirect String causes Spring MVC to think that we are changing application.
My questions are:
is this feature documented?
is my explanation plausible or is this a bug?

How to use send.redirect() while working with Spring MVC

I was trying to redirect to a dynamic page from Interceptors and Handler Mapping program. I have already defined a controller which handles and redirects (/hello.htm) through model (I have only this controller in my program). Until this point it is working fine. Apart from this, I registered a handler which will redirect to a page once it satisfies some condition.
public class WorkingHoursInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("In Working Hours Interceptor-pre");
Calendar c=Calendar.getInstance();
if(c.get(Calendar.HOUR_OF_DAY)<10||c.get(Calendar.HOUR_OF_DAY)>20){
response.sendRedirect("/WEB-INF/jsp/failure.jsp");
return false;
}
return true;
..............
..............
}
But once it comes to response.sendRedirect, it is showing resource not found even though the mentioned page is present. I tried to redirect to "WEB-INF/jsp/hello.jsp" as well but keeps showing the same error. If the condition in the interceptor is not satisfied, the program works fine.
Below is shown the only controller present in the program.
#Controller
public class MyController {
#RequestMapping("/hello.htm")
public ModelAndView sayGreeting(){
String msg="Hi, Welcome to Spring MVC 3.2";
return new ModelAndView("WEB-INF/jsp/hello.jsp","message",msg);
}
}
(The controller for handling hello.html works fine if I change the interceptor condition)
Instead of redirecting, if I just print a message in the console, the program works fine. But once it comes to redirect it shows the error. Do I need to specify a separate controller to handle this request? Will this redirection request go to the dispatcher-servlet?
You need to add redirect: prefix in the view name, the code for redirect will look like:
#RequestMapping(value = "/redirect", method = RequestMethod.GET)
public String redirect() {
return "redirect:finalPage";
}
OR
#RequestMapping(value = "/redirect", method = RequestMethod.GET)
public ModelAndView redirect() {
return new ModelAndView("redirect:finalPage");
}
You may get a detail description from here:
enter link description here

Using Spring Security in order to display different content for the same URL based upon whether or not the user is authenticated

I use Spring MVC, Spring Security and Apache Tiles and I have the following issue:
I want unauthenticated users to land on the home URL of my website (i.e. www.mywebsite.com/) where a login form will be diplayed to them so that they can authenticate from there.
Then, once a user is authenticated, I would like for completely different page content to be displayed to them on the home URL of the website (still www.mywebsite.com/) possibly using another template/jsp.
What I am seeking to achieve is basically to be able to display different content for the same URL based upon whether or not the user is authenticated - all this using Spring security and Spring MVC.
I have researched Spring Security but was not able to find a solution to the problem described above. Others have run into similar issues (see: Spring security - same page to deliver different content based on user role)
Can anyone please provide pointers or advice as to how to implement this?
One solution I can think of is to check in your MVC controller the user principal from the request and if authenticated/has role to return one ModelAndView, otherwise return another:
#Controller
public class MyController{
public ModelAndView doSomething(HttpServletRequest request, HttpServletResponse response){
if(request.getUserPrincipal() != null && request.isUserInRole("someRole"){
return new ModelAndView("view1");
}
else {
return new ModelAndView("view2");
}
}
}
I was given a nice and elegant solution by a member of the Spring forum. Here it is:
#RequestMapping("/")
#PreAuthorize("isAuthenticated()")
public String authenticatedHomePage() {
return "authenticatedHomePage";
}
#RequestMapping("/")
public String homePage() {
return "homePage";
}
It is quite nice because it relies upon Spring Security. See here (blog post)
I can think of a couple of ways of achieving what you want.
Firstly you could make use of the Spring Security taglib to conditionally render content in your View template dependent upon whether or not the user is correct authenticated. More information on the Spring Security taglib is here. Crudely, this would leave your View template looking something like:
if(user is authenticated)
render content for authenticated user
else
render log-in form
That feels a little bit blunt however as your Controller would always be creating the model regardless of whether or not your user was correctly authenticated. You would also need this logic in your View templates whenever you wanted to show a log-in form.
A separate approach would be to create a HandlerInterceptor implementation that forwards all requests to the Controller responsible for rendering the log-in page, until the user has fully authenticated. You could use the preHandle() method of your HandlerInterceptor to do this:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class MyHandlerInterceptor implements HandlerInterceptor
{
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
SecurityContext sc = SecurityContextHolder.getContext();
boolean userAuthenticated = true;
/* Some logic in here to determine if the user is correctly authenticated */
if(!userAuthenticated)
{
request.getRequestDispatcher("/login").forward(request, response);
return false;
}
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception
{
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
{
}}
You can then configure Spring MVC to map your HandlerInterceptor implementation onto the URLs that you require this functionality for. This avoids you having to duplicate the logic across all Controllers and is easily testable.
you can try to use
<security:authorize access=”isAnonymous()”>
not authenticated page
</security:authorize>
<security:authorize access=”isAuthenticated()”>
authenticated page
</security:authorize>
or you can return "redirect: page_for_not_auth" in your controller as view name to redirect response to another controller/method wich handles not autheticated requests.
You should do something like this :
#RequestMapping("/")
public String generalHomePage() {
...
}
#RequestMapping("/")
#PreAuthorize("isAuthenticated()")
public String secureHomePage() {
...
}

Change default homepage for Roo generated app using SpringMVC

By default when the web app starts it starts with the home page generated by roo with view name as "index"
Suppose i add new custom controller using following command,
web mvc controller ~.web.ViewHomeController --preferredMapping /homepage1
It generates the following code,
#RequestMapping("/homepage1/**")
#Controller
public class ViewHomeController {
#RequestMapping
public void get(ModelMap modelMap, HttpServletRequest request,
HttpServletResponse response) {
}
#RequestMapping(method = RequestMethod.POST, value = "{id}")
public void post(#PathVariable Long id, ModelMap modelMap,
HttpServletRequest request, HttpServletResponse response) {
}
#RequestMapping
public String index() {
return "home/homepage1";
}
}
I want the "home/homepage1" page to be the default page to be shown when the Roo Application starts.
Can i please get some guidance/details on changes i need to make to enable "home/homepage1" as default homepage for my application.
Thanks for help in advance. I am using latest version of Spring ROO, 1.1.4.
Thanks
In your webmvc-config.xml, replace the following section:
<!-- selects a static view for rendering without the need for an explicit controller -->
<mvc:view-controller path="/" view-name="index" />
with a view name you prefer.

Resources