2

I have a relative url string, know host and protocol. How can I build an absolute url string?

Seems easy? Yes at first look, but until escaped characters coming. I have to build absolute url from 302 code http(s) response Location header.

lets consider an example

protocol: http
host: example.com
location: /path/path?param1=param1Data&param2= " 

First I tried to build url string like:

Sting urlString = protocol+host+location

Constructor of URL class not escapes spaces and double quotes:

new URL(urlString)

Constructors of URI class fail with exception:

new URI(urlString)

URI.resolve method also fails with exception

Then I found URI can escape params in query string, but only with few constructors like for example:

URI uri = new URI("http", "example.com", 
    "/path/path", "param1=param1Data&param2= \"", null);

This constructor needs path and query be a separate arguments, but I have a relative URL, and it not split by path and query parts.

I could consider to check if relative URL contains "?" question sign and think everything before it is path, and everything after it is query, but what if relative url not contain path, but query only, and query contains "?" sign? Then this will not works because part of query will be considered as path.

Now I cannot get how to build absolute url from relative url.

These accepted answers seems just wrong:

It could be nice to consider scenario when relative url was given in relation to url with both host and some path part:

initial url http://example.com/...some path... relative /home?...query here ...

It would be great to get java core solution, though it still possible to use a good lib.

cassiomolin
  • 124,154
  • 35
  • 280
  • 359
P_M
  • 2,723
  • 4
  • 29
  • 62
  • 1
    @AustinSchäfer Are you sure? Did you read the question? – P_M Mar 06 '18 at 12:51
  • Not completely. I am wrong about it being a duplicate, but the question name should probably be updated to reflect it being a special case. – Austin Schaefer Mar 06 '18 at 12:57
  • @AustinSchäfer I think actually the question is right, because I ask not about special case but much more in general, then others topics I mentioned. It could have sense to rename them instead, to not confuse. What you think? – P_M Mar 06 '18 at 13:16
  • I think you already answered your own question, use the URI constructor with separate path, query and fragment arguments. The first `?` should define the limits between path and query and the first `#` should define the limit of the fragment – Klaimmore Mar 06 '18 at 13:17
  • @whoever who put: This question may already have an answer here: to top of my question. Could you give me a favor and read the question before you do such things. Thanks. – P_M Mar 06 '18 at 13:18
  • @Klaimmore How to solve the case when relative url not contain path and "?" not at first query position, but query string contains "?" in parameter value or somewhere else? – P_M Mar 06 '18 at 13:21
  • @Pavlo the path is always present, it may be the empty string see https://tools.ietf.org/html/rfc3986#section-3.3 – Klaimmore Mar 06 '18 at 13:29

2 Answers2

5

The first ? indicates where the query string begins:

3.4. Query

[...] The query component is indicated by the first question mark (?) character and terminated by a number sign (#) character or by the end of the URI.

A simple approach (that won't handle fragments and assumes that the query string is always present) is as simple as:

String protocol = "http";
String host = "example.com";
String location = "/path/path?key1=value1&key2=value2";

String path = location.substring(0, location.indexOf("?"));
String query = location.substring(location.indexOf("?") + 1);

URI uri = new URI(protocol, host, path, query, null);

A better approach that can also handle fragments could be :

String protocol = "http";
String host = "example.com";
String location = "/path/path?key1=value1&key2=value2#fragment";

// Split the location without removing the delimiters
String[] parts = location.split("(?=\\?)|(?=#)");

String path = null;
String query = null;
String fragment = null;

// Iterate over the parts to find path, query and fragment
for (String part : parts) {
    
    // The query string starts with ?
    if (part.startsWith("?")) {
        query = part.substring(1); 
        continue;
    }
    
    // The fragment starts with #
    if (part.startsWith("#")) {
        fragment = part.substring(1);
        continue;
    }
    
    // Path is what's left
    path = part;
}

URI uri = new URI(protocol, host, path, query, fragment);
Community
  • 1
  • 1
cassiomolin
  • 124,154
  • 35
  • 280
  • 359
  • 1
    Thank you. I was not able to solve this because was not familiar with RFC3986 before. – P_M Mar 13 '18 at 08:31
0

The best way seems to be to create a URI object with the multi piece constructors, and then convert it to a URL like so:

URI uri = new URI("https", "sitename.domain.tld", "/path/goes/here", "param1=value&param2=otherValue");
URL url = uri.toURL();
Austin Schaefer
  • 695
  • 5
  • 19
  • As mentioned in question I consider this as one of possible ways to go, but how I will split relative url by parts to know path and query? – P_M Mar 06 '18 at 13:22
  • Find the third / in your string to find the sub-path, and find the first ? to find the query parameters. – Austin Schaefer Mar 06 '18 at 13:32