31

I am trying to create a simple HttpServer in Java to handle GET requests, but when I try to get the GET parameters for a request I noticed the HttpExchange class does not have a method for that.

Does anybody know an easy way to read the GET parameters (query string)?

This is how my handler looks like:

public class TestHandler{
  @Override
  public void handle(HttpExchange exc) throws IOxception {
    String response = "This is the reponse";
    exc.sendResponseHeaders(200, response.length());

    // need GET params here

    OutputStream os = exc.getResponseBody();
    os.write(response.getBytes());
    os.close();
  } 
}

.. and the main method:

public static void main(String[] args) throws Exception{
  // create server on port 8000
  InetSocketAddress address = new InetSocketAddress(8000);
  HttpServer server = new HttpServer.create(address, 0);

  // bind handler
  server.createContext("/highscore", new TestHandler());
  server.setExecutor(null);
  server.start();
}
halfer
  • 19,824
  • 17
  • 99
  • 186
Tibor
  • 651
  • 1
  • 8
  • 18
  • 1
    An easy way? Parse the URI; it's just a get request. If you have to handle posts, things like [this](http://leonardom.wordpress.com/2009/08/06/getting-parameters-from-httpexchange/) might help. – Dave Newton Jul 24 '12 at 22:16

5 Answers5

53

The following: httpExchange.getRequestURI().getQuery()

will return string in format similar to this: "field1=value1&field2=value2&field3=value3..."

so you could simply parse string yourself, this is how function for parsing could look like:

public Map<String, String> queryToMap(String query) {
    if(query == null) {
        return null;
    }
    Map<String, String> result = new HashMap<>();
    for (String param : query.split("&")) {
        String[] entry = param.split("=");
        if (entry.length > 1) {
            result.put(entry[0], entry[1]);
        }else{
            result.put(entry[0], "");
        }
    }
    return result;
}

And this is how you could use it:

Map<String, String> params = queryToMap(httpExchange.getRequestURI().getQuery()); 
System.out.println("param A=" + params.get("A"));
stefano
  • 336
  • 2
  • 8
anon01
  • 546
  • 6
  • 4
  • 1
    Important to know: The query could be null. You should check that. – MinecraftShamrock Apr 07 '14 at 15:32
  • 12
    Thanks for this answer! It fails for params whose value includes an ampersand, though. To fix, use `getRawQuery()` instead of `getQuery()`, then `param = java.net.URLDecoder.decode(param, "UTF-8")` after splitting on "&". – Evan Jan 17 '16 at 15:59
  • 2
    @Evan, I believe the decoding should happen when putting the value into the Map - that way you don't lose part of the value if there's an '=' in it. – Gurusharan S Dec 27 '16 at 05:15
  • This does not properly deencode the strings, `URLDecoder` should have been used. – Oliv Jan 12 '17 at 09:58
  • 1
    It should be `param.split("=", 2);` in case the value contains a *=*. (btw this solution does not support double keys like *x=111&x=222*) – Datz Oct 30 '18 at 20:25
  • @Datz, having the same key in query param is even valid? can you add any use case? – ssi-anik Dec 27 '21 at 12:19
  • 1
    @ssi-anik try for example this URL: `https://www.google.com/?q=hello&q=ssi-anik` Many framework/programming languages create an array of the values. (eg `q[0]=hello q[1]=ssi-anik` – Datz Dec 28 '21 at 20:34
3

This answer, contrary to annon01's, properly decodes the keys and values. It does not use String.split, but scans the string using indexOf, which is faster.

public static Map<String, String> parseQueryString(String qs) {
    Map<String, String> result = new HashMap<>();
    if (qs == null)
        return result;

    int last = 0, next, l = qs.length();
    while (last < l) {
        next = qs.indexOf('&', last);
        if (next == -1)
            next = l;

        if (next > last) {
            int eqPos = qs.indexOf('=', last);
            try {
                if (eqPos < 0 || eqPos > next)
                    result.put(URLDecoder.decode(qs.substring(last, next), "utf-8"), "");
                else
                    result.put(URLDecoder.decode(qs.substring(last, eqPos), "utf-8"), URLDecoder.decode(qs.substring(eqPos + 1, next), "utf-8"));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e); // will never happen, utf-8 support is mandatory for java
            }
        }
        last = next + 1;
    }
    return result;
}
Oliv
  • 10,221
  • 3
  • 55
  • 76
1

Building on the answer by @anon01, this is how to do it in Groovy:

Map<String,String> getQueryParameters( HttpExchange httpExchange )
{
    def query = httpExchange.getRequestURI().getQuery()
    return query.split( '&' )
            .collectEntries {
        String[] pair = it.split( '=' )
        if (pair.length > 1)
        {
            return [(pair[0]): pair[1]]
        }
        else
        {
            return [(pair[0]): ""]
        }
    }
}

And this is how to use it:

def queryParameters = getQueryParameters( httpExchange )
def parameterA = queryParameters['A']
Wim Deblauwe
  • 25,113
  • 20
  • 133
  • 211
1

Stumbled across this, and figured I'd toss a Java 8 / Streams implementation out here, whilst adding a few extra bits (not in previous answers).

Extra 1: I've added a filter to avoid processing any empty params. Something that should not happen, but it allows a cleaner implementation vs. not handling the issue (and sending an empty response). An example of this would look like ?param1=value1&param2=

Extra 2: I've leveraged String.split(String regex, int limit) for the second split operation. This allows a query parameter such as ?param1=it_has=in-it&other=something to be passed.

public static Map<String, String> getParamMap(String query) {
    // query is null if not provided (e.g. localhost/path )
    // query is empty if '?' is supplied (e.g. localhost/path? )
    if (query == null || query.isEmpty()) return Collections.emptyMap();

    return Stream.of(query.split("&"))
            .filter(s -> !s.isEmpty())
            .map(kv -> kv.split("=", 2)) 
            .collect(Collectors.toMap(x -> x[0], x-> x[1]));

}

Imports

import java.util.Map;
import java.util.Collections;
import java.util.stream.Stream;
import java.util.stream.Collectors;
Brian
  • 4,921
  • 3
  • 20
  • 29
0

If, like me, your query string allows the same name to occur multiple times, the you'll want the values to be lists (like in Netty's QueryStringDecoder). Adapted from the best answer here..

  final static Charset UTF_8 = Charset.forName("UTF-8");

  static Map<String, List<String>> queryMap(HttpExchange exchange) {
    return queryMap(exchange.getRequestURI().getRawQuery());
  }
  
  static Map<String, List<String>> queryMap(String query) {
    if (query == null || query.isEmpty()) {
      return Map.of();
    }
    Map<String, List<String>> result = new HashMap<>();
    for (String param : query.split("&")) {
      String[] entry = param.split("=");
      var name = entry[0];
      var list = result.get(name);
      if (list == null) {
        list = new ArrayList<>(2);
        result.put(name, list);
      }
      String value = entry.length > 1 ?
            URLDecoder.decode(entry[1], UTF_8) : "";
      
      list.add(value);
    }
    return result;
  }
Babak Farhang
  • 79
  • 1
  • 8