288

I have an HttpServletRequest object.

How do I get the complete and exact URL that caused this call to arrive at my servlet?

Or at least as accurately as possible, as there are perhaps things that can be regenerated (the order of the parameters, perhaps).

Shashank Agrawal
  • 25,161
  • 11
  • 89
  • 121
flybywire
  • 261,858
  • 191
  • 397
  • 503

12 Answers12

460

The HttpServletRequest has the following methods:

  • getRequestURL() - returns the part of the full URL before query string separator character ?
  • getQueryString() - returns the part of the full URL after query string separator character ?

So, to get the full URL, just do:

public static String getFullURL(HttpServletRequest request) {
    StringBuilder requestURL = new StringBuilder(request.getRequestURL().toString());
    String queryString = request.getQueryString();

    if (queryString == null) {
        return requestURL.toString();
    } else {
        return requestURL.append('?').append(queryString).toString();
    }
}
Chetan Gole
  • 703
  • 2
  • 14
  • 26
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • 3
    You copied the description from getRequestURI (wrong) but use getRequestURL in the code (right). – Vinko Vrsalovic Feb 08 '10 at 14:56
  • 22
    You need to conditionally check if the query string is empty. – Adam Gent Sep 15 '10 at 20:43
  • 8
    You're also mutating the StringBuffer backing the request URL. If the request implementation isn't making a defensive copy that's a very good way to introduce strange behavior and bugs in other parts of code that expect it in it's original form. – Ken Blair May 22 '12 at 16:53
  • Note to users of this answer: This answer does not include the used scheme in the "fullURL" variable. – eleotlecram Nov 26 '12 at 13:36
  • 1
    It should, according to spec: "Reconstructs the URL the client used to make the request. The returned URL contains a protocol, server name, port number, and server path, but it does not include query string parameters." – Bozho Nov 26 '12 at 17:58
  • 2
    If you have a proxy between you and the users, you may also have to deal with the `X-Forwarded-For` and `X-Forwarded-Proto` headers. – Christian Trimble Mar 24 '13 at 00:11
  • 5
    Use StringBuilder instead of StringBuffer – Gladwin Burboz Mar 28 '13 at 15:27
  • @"C. Trimble": X-Forwarded-For header is used to get client information and nothing about the requested URL. This header value is also not considered reliable. – Gladwin Burboz Mar 28 '13 at 15:32
  • 20
    @KenBlair: A StringBuffer is returned on purpose, so that you easily can add on more stash. Since this is specified on the javadoc, it would be extremely absurd of the implementation to expect that the returned StringBuffer would not be modified by the caller - hence, this is cool. – stolsvik Jul 15 '13 at 12:53
  • 1
    I found some problems with redirections as "If this request has been forwarded using RequestDispatcher.forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse), the server path in the reconstructed URL must reflect the path used to obtain the RequestDispatcher, and not the server path specified by the client." – borjab Oct 22 '14 at 09:04
  • This shows the port (:80, :443), which may not be desired in case the url is shown to 'non-technical' users. See answer by Mat Banik for this case. – Christophe Roussy Apr 28 '15 at 12:46
  • 1
    @borjab If the request has been forwarded (or include, error, etc.; see DispatcherType) then the 'new' url parts are set as request attributes under the keys defined in the static fields of RequestDispatcher. Thus you can build the URL from the values obtained by calling request.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH) and so on. – allenru Jan 16 '16 at 00:19
  • 1
    @GB Using `StringBuilder` is clearly not a reasonable option here. – shmosel Mar 18 '16 at 03:19
  • 1
    This answer doesn't work if the URL contains a local part (which is common on pages with anchors and single page apps). For example, if the actual URL is `http://foo.com/?name=alvin#messages` this solution may (depending on the servlet implementation) reconstruct the url as `http://foo.com/#messages?name=alvin`. Since everything after the `#` is considered the local part, the parameters will be ignored in this form and may not even be sent to the server if this URL is used for subsequent requests. – Alvin Thompson Dec 29 '16 at 15:01
  • To be more clear, some servlet implementations (I'm using Spring 4 here) will return `http://foo.com/#messages` from `getRequestUrl` if the local part (`#messages`) was sent to the server. You need to check for that and move the local part after the parameters if present. – Alvin Thompson Dec 29 '16 at 15:18
  • @AlvinThompson Do you have an example how this could happen? Normally browsers do not sent the local part to the server. Is this related to a framework you were using? – Johannes Stadler Feb 17 '19 at 09:02
  • 1
    @JohannesStadler Yup, AFAIK none of the major browsers will send the local part nowadays. At the time I wrote my comment one of the browsers (I can't remember which) still did send the local part under some circumstances. But in any case, you should really still check for this because (a) the user may be using old hardware/software, (b) the request may not come from a browser, and (c) why not? – Alvin Thompson Feb 18 '19 at 16:14
  • I haven't checked, but it wouldn't surprise me if browsers still did send the local part if the request was initiated via javascript. – Alvin Thompson Feb 18 '19 at 16:16
  • This example is awesome, it works in Java 11, this question is also 12 years old, cu the dude some slack, I'm happy that someone cared enough to fix the answer. – Chris Apr 26 '22 at 15:25
166

I use this method:

public static String getURL(HttpServletRequest req) {

    String scheme = req.getScheme();             // http
    String serverName = req.getServerName();     // hostname.com
    int serverPort = req.getServerPort();        // 80
    String contextPath = req.getContextPath();   // /mywebapp
    String servletPath = req.getServletPath();   // /servlet/MyServlet
    String pathInfo = req.getPathInfo();         // /a/b;c=123
    String queryString = req.getQueryString();          // d=789

    // Reconstruct original requesting URL
    StringBuilder url = new StringBuilder();
    url.append(scheme).append("://").append(serverName);

    if (serverPort != 80 && serverPort != 443) {
        url.append(":").append(serverPort);
    }

    url.append(contextPath).append(servletPath);

    if (pathInfo != null) {
        url.append(pathInfo);
    }
    if (queryString != null) {
        url.append("?").append(queryString);
    }
    return url.toString();
}
Igor Mukhin
  • 15,014
  • 18
  • 52
  • 61
MatBanik
  • 26,356
  • 39
  • 116
  • 178
  • 9
    This is a helpful answer for a quick reference to all the bits of info available on the HttpServletRequest. However, I think you'd want to check the scheme in deciding whether to add the port piece to the result. "https" would be 443, for example. – Peter Cardona Jun 20 '12 at 00:52
  • 2
    a small optimization would be to use a StringBuilder instead of StringBuffer, just a hint – Chris Oct 22 '13 at 15:32
  • That depends on the fact if you require synchronization. I figured better safe than sorry. So if anyone needs the code to be faster they should use StringBuilder but if you want thread safety well stick with what is in this answer. – MatBanik Oct 22 '13 at 19:24
  • 11
    Just a comment: thread safety is not an issue in this specific example because `url` is declared, instantiated, and used only inside the method, so it can't be accessed from threads other than the one that called the method. – Diogo Kollross Jun 12 '14 at 15:32
  • 1
    As mentioned, thread safety is not an issue here since you are creating an instance of your `StringBuffer` for each call and not sharing it with any other threads. This should be changed to be a `StringBuilder`. – Gray Jul 21 '15 at 21:20
  • 2
    Any reason not to use `getRequestURI` ? – Christophe Roussy Nov 12 '15 at 15:01
32

In a Spring project you can use

UriComponentsBuilder.fromHttpRequest(new ServletServerHttpRequest(request)).build().toUriString()
Peter Szanto
  • 7,568
  • 2
  • 51
  • 53
28
// http://hostname.com/mywebapp/servlet/MyServlet/a/b;c=123?d=789

public static String getUrl(HttpServletRequest req) {
    String reqUrl = req.getRequestURL().toString();
    String queryString = req.getQueryString();   // d=789
    if (queryString != null) {
        reqUrl += "?"+queryString;
    }
    return reqUrl;
}
Teja Kantamneni
  • 17,402
  • 12
  • 56
  • 86
  • 5
    You're ignoring the advantage of `StringBuffer`. – BalusC Feb 08 '10 at 14:45
  • Yes. I accept it, but its only two additional objects I guess. – Teja Kantamneni Feb 08 '10 at 15:36
  • 10
    It's *one* additional object (a StringBuilder) and does not mutate the underlying StringBuffer that is returned. I would actually prefer this over the "accepted" answer, the JVM will optimize the difference away regardless and this does not have any of the risk of introducing bugs. – Ken Blair May 22 '12 at 16:55
  • (request.getRequestURL().toString() + ((request.getQueryString() != null) ? ("?" + request.getQueryString()) : "")) – Alex Feb 15 '17 at 20:35
7

HttpUtil being deprecated, this is the correct method

StringBuffer url = req.getRequestURL();
String queryString = req.getQueryString();
if (queryString != null) {
    url.append('?');
    url.append(queryString);
}
String requestURL = url.toString();
Vinko Vrsalovic
  • 330,807
  • 53
  • 334
  • 373
5

Combining the results of getRequestURL() and getQueryString() should get you the desired result.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
2

You can write a simple one liner with a ternary and if you make use of the builder pattern of the StringBuffer from .getRequestURL():

private String getUrlWithQueryParms(final HttpServletRequest request) { 
    return request.getQueryString() == null ? request.getRequestURL().toString() :
        request.getRequestURL().append("?").append(request.getQueryString()).toString();
}

But that is just syntactic sugar.

Johannes Stadler
  • 737
  • 12
  • 24
1

You can use filter .

@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
        HttpServletRequest test1=    (HttpServletRequest) arg0;
       
     test1.getRequestURL()); it gives  http://localhost:8081/applicationName/menu/index.action
     test1.getRequestURI()); it gives applicationName/menu/index.action
     String pathname = test1.getServletPath()); it gives //menu/index.action
      
  
    if(pathname.equals("//menu/index.action")){ 
        arg2.doFilter(arg0, arg1); // call to urs servlet or frameowrk managed controller method


       // in resposne 
       HttpServletResponse httpResp = (HttpServletResponse) arg1;
       RequestDispatcher rd = arg0.getRequestDispatcher("another.jsp");     
       rd.forward(arg0, arg1);
}

donot forget to put <dispatcher>FORWARD</dispatcher> in filter mapping in web.xml

Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
abishkar bhattarai
  • 7,371
  • 8
  • 49
  • 66
  • for the record...test1.getRequestURI()); it gives /applicationName/menu/index.action (i.e. it contains the leading slash) – Stevko Dec 22 '15 at 23:19
1

Use the following methods on HttpServletRequest object

java.lang.String getRequestURI() -Returns the part of this request's URL from the protocol name up to the query string in the first line of the HTTP request.

java.lang.StringBuffer getRequestURL() -Reconstructs the URL the client used to make the request.

java.lang.String getQueryString() -Returns the query string that is contained in the request URL after the path.

Kishor Prakash
  • 8,011
  • 12
  • 61
  • 92
0

Somewhat late to the party, but I included this in my MarkUtils-Web library in WebUtils - Checkstyle-approved and JUnit-tested:

import javax.servlet.http.HttpServletRequest;

public class GetRequestUrl{
    /**
     * <p>A faster replacement for {@link HttpServletRequest#getRequestURL()}
     *  (returns a {@link String} instead of a {@link StringBuffer} - and internally uses a {@link StringBuilder})
     *  that also includes the {@linkplain HttpServletRequest#getQueryString() query string}.</p>
     * <p><a href="https://gist.github.com/ziesemer/700376d8da8c60585438"
     *  >https://gist.github.com/ziesemer/700376d8da8c60585438</a></p>
     * @author Mark A. Ziesemer
     *  <a href="http://www.ziesemer.com.">&lt;www.ziesemer.com&gt;</a>
     */
    public String getRequestUrl(final HttpServletRequest req){
        final String scheme = req.getScheme();
        final int port = req.getServerPort();
        final StringBuilder url = new StringBuilder(256);
        url.append(scheme);
        url.append("://");
        url.append(req.getServerName());
        if(!(("http".equals(scheme) && (port == 0 || port == 80))
                || ("https".equals(scheme) && port == 443))){
            url.append(':');
            url.append(port);
        }
        url.append(req.getRequestURI());
        final String qs = req.getQueryString();
        if(qs != null){
            url.append('?');
            url.append(qs);
        }
        final String result = url.toString();
        return result;
    }
}

Probably the fastest and most robust answer here so far behind Mat Banik's - but even his doesn't account for potential non-standard port configurations with HTTP/HTTPS.

See also:

ziesemer
  • 27,712
  • 8
  • 86
  • 94
0

When the request is being forwarded, e.g. from a reverse proxy, the HttpServletRequest.getRequestURL() method will not return the forwarded url but the local url. When the x-forwarded-* Headers are set, this can be easily handled:

public static String getCurrentUrl(HttpServletRequest request) {
    String forwardedHost = request.getHeader("x-forwarded-host");

    if(forwardedHost == null) {
        return request.getRequestURL().toString();
    }

    String scheme = request.getHeader("x-forwarded-proto");
    String prefix = request.getHeader("x-forwarded-prefix");

    return scheme + "://" + forwardedHost + prefix + request.getRequestURI();
}

This lacks the Query part, but that can be appended as supposed in the other answers. I came here, because I specifically needed that forwarding stuff and can hopefully help someone out with that.

Jazzschmidt
  • 989
  • 12
  • 27
  • 1
    I don't believe this is true if the required Tomcat valves are setup correctly. https://tomcat.apache.org/tomcat-8.5-doc/api/org/apache/catalina/valves/RemoteIpValve.html – Jonathan S. Fisher Sep 06 '22 at 21:22
0

I had an usecase to generate cURL command (that I can use in terminal) from httpServletRequest instance. I created one method like this. You can directly copy-paste the output of this method directly in terminal

private StringBuilder generateCURL(final HttpServletRequest httpServletRequest) {
    final StringBuilder curlCommand = new StringBuilder();
    curlCommand.append("curl ");

    // iterating over headers.
    for (Enumeration<?> e = httpServletRequest.getHeaderNames(); e.hasMoreElements();) {
        String headerName = (String) e.nextElement();
        String headerValue = httpServletRequest.getHeader(headerName);
        // skipping cookies, as we're appending cookies separately.
        if (Objects.equals(headerName, "cookie")) {
            continue;
        }
        if (headerName != null && headerValue != null) {
            curlCommand.append(String.format(" -H \"%s:%s\" ", headerName, headerValue));
        }
    }

    // iterating over cookies.
    final Cookie[] cookieArray = httpServletRequest.getCookies();
    final StringBuilder cookies = new StringBuilder();
    for (Cookie cookie : cookieArray) {
        if (cookie.getName() != null && cookie.getValue() != null) {
            cookies.append(cookie.getName());
            cookies.append('=');
            cookies.append(cookie.getValue());
            cookies.append("; ");
        }
    }
    curlCommand.append(" --cookie \"" + cookies.toString() + "\"");

    // appending request url.
    curlCommand.append(" \"" + httpServletRequest.getRequestURL().toString() + "\"");
    return curlCommand;
}
Ashwin
  • 409
  • 4
  • 5