23

I am writing a web application using Spring MVC. I am using annotations for the controllers, etc. Everything is working fine, except when it comes to actual links in the application (form actions, <a> tags, etc.) Current, I have this (obviously abbreviated):

//In the controller
@RequestMapping(value="/admin/listPeople", method=RequestMethod.GET)

//In the JSP
<a href="/admin/listPeople">Go to People List</a>

When I directly enter the URL like "http://localhost:8080/MyApp/admin/listPeople", the page loads correctly. However, the link above does not work. It looses the application name "MyApp".

Does anyone know if there is a way to configure Spring to throw on the application name on there?

Let me know if you need to see any of my Spring configuration. I am using the standard dispatcher servlet with a view resolver, etc.

CoolBeans
  • 20,016
  • 10
  • 81
  • 98
Snowy Coder Girl
  • 5,162
  • 10
  • 36
  • 65

7 Answers7

50

You need to prepend context path to your links.

// somewhere on the top of your JSP
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>

...
<a href="${contextPath}/admin/listPeople">Go to People List</a>
craftsman
  • 13,785
  • 17
  • 61
  • 82
  • +1 Thanks, I thought of this but I'd rather do it with Spring somehow. – Snowy Coder Girl Aug 07 '11 at 01:29
  • 2
    There is no "Spring" way of doing this :) Only option that saves you from prepending ${contextPath} to your links is that you deploy your application as root, so that it's context path is "/" (as Kevin has described); but I would not recommend this approach because it will make your application dependent of its deployment. – craftsman Aug 07 '11 at 01:33
  • I am looking into whether a filter would help accomplish this. Like, using a RequestDispatcher. – Snowy Coder Girl Aug 07 '11 at 01:39
  • I don't think so. Your filters reside in your application and they can process only those requests which are sent to your application. If a request does not have the correct context path (MyApp in your case), servlet container will not send it to your application at all. – craftsman Aug 07 '11 at 01:44
  • Dang. Well works for the links, but still not as nice as I'd like. I'd like an automatic way to apply it to the whole app. I know it's somehow possible (my old work did it). We used straight on "/admin/listPeople" and stuff. And sometimes just "listPeople". – Snowy Coder Girl Aug 07 '11 at 02:00
  • I am going to rephrase this question as more generic. I agree on further inspection that the application (and Spring) cannot solve this. Hopefully making the question server oriented would help. – Snowy Coder Girl Aug 07 '11 at 02:38
  • Is there any difference in using contextPath instead of spring:url? – vault Nov 19 '14 at 13:53
14

The c:url tag will append the context path to your URL. For example:

<c:url value="/admin/listPeople"/>

Alternately, I prefer to use relative URLs as much as possible in my Spring MVC apps as well. So if the page is at /MyApp/index, the link <a href="admin/listPeople"> will take me to the listPeople page.

This also works if you are deeper in the URL hierarchy. You can use the .. to traverse back up a level. So on the page at/MyApp/admin/people/aPerson, using <a href="../listPeople"> will like back to the list page

Jason Gritman
  • 5,051
  • 3
  • 27
  • 38
6

I prefer to use BASE tag:

<base href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/" />

Then, all your links can be like:

<a href="admin/listPeople">Go to People List</a>
sinuhepop
  • 18,521
  • 15
  • 63
  • 103
  • I advise to read this before using the base tag: http://stackoverflow.com/questions/1889076/is-it-recommended-to-use-the-base-html-tag – Amr Mostafa Jan 26 '14 at 09:27
4

As i have just been trying to find the answer to this question and this is the first google result.

This can be done now using the MvcUriComponentsBuilder

This is part of the 4.0 version of Spring MVC

http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.html

The method needed is fromMappingName

From the documentation :

Create a URL from the name of a Spring MVC controller method's request mapping. The configured HandlerMethodMappingNamingStrategy determines the names of controller method request mappings at startup. By default all mappings are assigned a name based on the capital letters of the class name, followed by "#" as separator, and then the method name. For example "PC#getPerson" for a class named PersonController with method getPerson. In case the naming convention does not produce unique results, an explicit name may be assigned through the name attribute of the @RequestMapping annotation.

This is aimed primarily for use in view rendering technologies and EL expressions. The Spring URL tag library registers this method as a function called "mvcUrl".

For example, given this controller:

@RequestMapping("/people")
 class PersonController {

   @RequestMapping("/{id}")
   public HttpEntity getPerson(@PathVariable String id) { ... }

 }

A JSP can prepare a URL to the controller method as follows:

<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>

 <a href="${s:mvcUrl('PC#getPerson').arg(0,"123").build()}">Get Person</a>
Chris Eccles
  • 141
  • 5
0

You could use a servletRelativeAction. I'm not sure what versions this is available in (I'm using 4.0.x currently) and I haven't seen much documentation on this, but if you look at the code backing the spring form you can probably guess. Just make sure the path you pass it starts with a "/".

Example:

<form:form class="form-horizontal" name="form" servletRelativeAction="/j_spring_security_check"  method="POST">

See org.springframework.web.servlet.tags.form.FormTag:

protected String resolveAction() throws JspException {
    String action = getAction();
    String servletRelativeAction = getServletRelativeAction();
    if (StringUtils.hasText(action)) {
        action = getDisplayString(evaluate(ACTION_ATTRIBUTE, action));
        return processAction(action);
    }
    else if (StringUtils.hasText(servletRelativeAction)) {
        String pathToServlet = getRequestContext().getPathToServlet();
        if (servletRelativeAction.startsWith("/") && !servletRelativeAction.startsWith(getRequestContext().getContextPath())) {
            servletRelativeAction = pathToServlet + servletRelativeAction;
        }
        servletRelativeAction = getDisplayString(evaluate(ACTION_ATTRIBUTE, servletRelativeAction));
        return processAction(servletRelativeAction);
    }
    else {
        String requestUri = getRequestContext().getRequestUri();
        ServletResponse response = this.pageContext.getResponse();
        if (response instanceof HttpServletResponse) {
            requestUri = ((HttpServletResponse) response).encodeURL(requestUri);
            String queryString = getRequestContext().getQueryString();
            if (StringUtils.hasText(queryString)) {
                requestUri += "?" + HtmlUtils.htmlEscape(queryString);
            }
        }
        if (StringUtils.hasText(requestUri)) {
            return processAction(requestUri);
        }
        else {
            throw new IllegalArgumentException("Attribute 'action' is required. " +
                    "Attempted to resolve against current request URI but request URI was null.");
        }
    }
}
0

I usually configure tomcat to use context root of "/" or deploy the war as ROOT.war. Either way the war name does not become part of the URL.

Kevin
  • 23,123
  • 15
  • 92
  • 146
-1

Since it's been some years I thought I'd chip in for others looking for this. If you are using annotations and have a controller action like this for instance:

@RequestMapping("/new")   //<--- relative url
public ModelAndView newConsultant() {
    ModelAndView mv = new ModelAndView("new_consultant");
    try {
        List<Consultant> list = ConsultantDAO.getConsultants();
        mv.addObject("consultants", list);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return mv;
}

in your .jsp (view) you add this directive

<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>

and simply use

<spring:url value="/new" var="url" htmlEscape="true"/>
<a href="${url}">New consultant</a>

where

value's value should match @RequestMapping's argument in the controller action and

var's value is the name of the variable you use for href

HIH

Scaramouche
  • 2,950
  • 1
  • 16
  • 42