172

I'm making a simple, very lightweight front-controller. I need to match request paths to different handlers (actions) in order to choose the correct one.

On my local machine HttpServletRequest.getPathInfo() and HttpServletRequest.getRequestURI() return the same results. But I'm not sure what will they return in the production environment.

So, what's the difference between these method and what should I choose?

oberlies
  • 11,503
  • 4
  • 63
  • 110
Roman
  • 64,384
  • 92
  • 238
  • 332
  • 2
    You may find [this answer](http://stackoverflow.com/questions/3541077/design-patterns-web-based-applications/3542297#3542297) useful as well. – BalusC Feb 08 '11 at 12:58
  • @BalusC: thanks, I've been already using some tips from that answer. – Roman Feb 08 '11 at 15:34
  • This explains the difference with a nice diagram: https://agiletribe.wordpress.com/2016/02/23/httpservletrequest-path-decoding/ – AgilePro Aug 01 '19 at 22:00

4 Answers4

548

I will put a small comparison table here (just to have it somewhere):

Servlet is mapped as /test%3F/* and the application is deployed under /app.

http://30thh.loc:8480/app/test%3F/a%3F+b;jsessionid=S%3F+ID?p+1=c+d&p+2=e+f#a

Method              URL-Decoded Result           
----------------------------------------------------
getContextPath()        no      /app
getLocalAddr()                  127.0.0.1
getLocalName()                  30thh.loc
getLocalPort()                  8480
getMethod()                     GET
getPathInfo()           yes     /a?+b
getProtocol()                   HTTP/1.1
getQueryString()        no      p+1=c+d&p+2=e+f
getRequestedSessionId() no      S%3F+ID
getRequestURI()         no      /app/test%3F/a%3F+b;jsessionid=S+ID
getRequestURL()         no      http://30thh.loc:8480/app/test%3F/a%3F+b;jsessionid=S+ID
getScheme()                     http
getServerName()                 30thh.loc
getServerPort()                 8480
getServletPath()        yes     /test?
getParameterNames()     yes     [p 2, p 1]
getParameter("p 1")     yes     c d

In the example above the server is running on the localhost:8480 and the name 30thh.loc was put into OS hosts file.

Comments

  • "+" is handled as space only in the query string

  • Anchor "#a" is not transferred to the server. Only the browser can work with it.

  • If the url-pattern in the servlet mapping does not end with * (for example /test or *.jsp), getPathInfo() returns null.

If Spring MVC is used

  • Method getPathInfo() returns null.

  • Method getServletPath() returns the part between the context path and the session ID. In the example above the value would be /test?/a?+b

  • Be careful with URL encoded parts of @RequestMapping and @RequestParam in Spring. It is buggy (current version 3.2.4) and is usually not working as expected.

30thh
  • 10,861
  • 6
  • 32
  • 42
  • 31
    I'm printing your answer and put it up as a poster on our office. That's how useful it is! – Ibrahim Arief Dec 17 '15 at 08:05
  • 2
    `If the url-pattern in the servlet mapping does not end with * (for example /test or *.jsp), getPathInfo() returns null.` brilliant. – Boris Treukhov Oct 27 '16 at 15:56
  • 1
    I believe both `getRequestURI()` and `getRequestURL()` should return non-decoded jsessionid, in this case `S%3F+ID`. At least it does on Tomcat/8.5.6. – Gediminas Rimsa Jan 08 '17 at 14:11
83

getPathInfo() gives the extra path information after the URI, used to access your Servlet, where as getRequestURI() gives the complete URI.

I would have thought they would be different, given a Servlet must be configured with its own URI pattern in the first place; I don't think I've ever served a Servlet from root (/).

For example if Servlet 'Foo' is mapped to URI '/foo' then I would have thought the URI:

/foo/path/to/resource

Would result in:

RequestURI = /foo/path/to/resource

and

PathInfo = /path/to/resource
trojanfoe
  • 120,358
  • 21
  • 212
  • 242
  • 22
    worth mentioning about decoding behavior. getRequestURI() does not decode the string. Where getPathInfo() does decode. – Kavindu Dodanduwa Jul 06 '16 at 05:05
  • 2
    In some cases `getRequestURI()` gives me the string `"/foo/path/to/resource"` as expected, but `getPathInfo()` for the same `HttpServletRequest` object gives me `null`. What in the world is going on? **EDIT: It is answered below by the user "30thh".** – Snackoverflow Mar 14 '19 at 17:35
42

Let's break down the full URL that a client would type into their address bar to reach your servlet:

http://www.example.com:80/awesome-application/path/to/servlet/path/info?a=1&b=2#boo

The parts are:

  1. scheme: http
  2. hostname: www.example.com
  3. port: 80
  4. context path: awesome-application
  5. servlet path: path/to/servlet
  6. path info: path/info
  7. query: a=1&b=2
  8. fragment: boo

The request URI (returned by getRequestURI) corresponds to parts 4, 5 and 6.

(incidentally, even though you're not asking for this, the method getRequestURL would give you parts 1, 2, 3, 4, 5 and 6).

Now:

  • part 4 (the context path) is used to select your particular application out of many other applications that may be running in the server
  • part 5 (the servlet path) is used to select a particular servlet out of many other servlets that may be bundled in your application's WAR
  • part 6 (the path info) is interpreted by your servlet's logic (e.g. it may point to some resource controlled by your servlet).
  • part 7 (the query) is also made available to your servlet using getQueryString
  • part 8 (the fragment) is not even sent to the server and is relevant and known only to the client

The following always holds (except for URL encoding differences):

requestURI = contextPath + servletPath + pathInfo

The following example from the Servlet 3.0 specification is very helpful:


Note: image follows, I don't have the time to recreate in HTML:

enter image description here

Marcus Junius Brutus
  • 26,087
  • 41
  • 189
  • 331
18

Consider the following servlet conf:

   <servlet>
        <servlet-name>NewServlet</servlet-name>
        <servlet-class>NewServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>NewServlet</servlet-name>
        <url-pattern>/NewServlet/*</url-pattern>
    </servlet-mapping>

Now, when I hit the URL http://localhost:8084/JSPTemp1/NewServlet/jhi, it will invoke NewServlet as it is mapped with the pattern described above.

Here:

getRequestURI() =  /JSPTemp1/NewServlet/jhi
getPathInfo() = /jhi

We have those ones:

  • getPathInfo()

    returns
    a String, decoded by the web container, specifying extra path information that comes after the servlet path but before the query string in the request URL; or null if the URL does not have any extra path information

  • getRequestURI()

    returns
    a String containing the part of the URL from the protocol name up to the query string

jmj
  • 237,923
  • 42
  • 401
  • 438