Home > Net >  Convert absolute or relative URL like “example.com/profile” to absolute URL, including protocol
Convert absolute or relative URL like “example.com/profile” to absolute URL, including protocol

Time:11-30

I want to give users the option to input a URL into an input field. At the moment it is really strict as it only accepts absolute URLs like this: https://example.com/a.profile.

The users gave feedback that they also want to be able to just type a relative URL like the following: example.com/a.profile or www.example.com/a.profile.

How do you take an arbitrarily relative or absolute URL and make sure that the output is an absolute URL? The https: protocol can be assumed for all URLs.

CodePudding user response:

I now implemented the following class, although I am not sure if I have considered all edge cases. But maybe it helps future users.

const FULL_URL_REGEX =
    /(http|https):\/\/(\w :{0,1}\w*@)?(\S )(:[0-9] )?(\.\w\w )(\/|\/([\w#!:.? =&%@!\-\/]))?/;
const HALF_URL_REGEX =
    /(\w :{0,1}\w*@)?(\S )(:[0-9] )?(\.\w\w )(\/|\/([\w#!:.? =&%@!\-\/]))?/;
const ASSUME_PROTOCOL = 'https';

export class URLTools {
    public static urlIsValid(url: string): boolean {
        return FULL_URL_REGEX.test(url) || HALF_URL_REGEX.test(url);
    }

    public static guaranteeFullUrl(potentiallyHalfUrl: string) {
        if (HALF_URL_REGEX.test(potentiallyHalfUrl)) {
            return `${ASSUME_PROTOCOL}://${potentiallyHalfUrl}`;
        } else if (FULL_URL_REGEX.test(potentiallyHalfUrl)) {
            return potentiallyHalfUrl;
        } else {
            throw Error('Invalid URL');
        }
    }
}

CodePudding user response:

Don’t mess around with regular expressions. Use the URL API instead:

// Solution:

const asAbsoluteURL = (url, base = location) => {
    let absoluteURL;
    
    try {
      absoluteURL = new URL(url);
    }
    catch {
      absoluteURL = new URL(`//${url}`, base);
    }
    
    return absoluteURL;
  };

// Usage:

const absoluteURL1 = asAbsoluteURL("example.com");
const absoluteURL2 = asAbsoluteURL("example.com", "http://something");
const absoluteURL3 = asAbsoluteURL("https://example.com");

console.log({    // Run this snippet and look into your browser console
  absoluteURL1,  //   for the results (hit [F12]).
  absoluteURL2,
  absoluteURL3,
  "1. If you want strings": "use concatenation: "   absoluteURL1,
  "2. Alternatively": `use template literals: ${absoluteURL2}`,
  "3. Or use the String function explicitly": String(absoluteURL3)
});

// Interactive demonstration:

addEventListener("input", () => {
  const relativeURL = document.getElementById("relativeURL").value,
    baseURL = document.getElementById("baseURL").value || location;
  let result;
  
  try {
    result = formatCode`Relative URL ${relativeURL} is converted to absolute URL ${asAbsoluteURL(relativeURL, baseURL)}.`
  }
  catch {
    result = formatCode`Either the relative URL ${relativeURL} is not a valid relative URL or the base URL ${baseURL} is not a valid absolute URL itself.`;
  }
  
  document.getElementById("output").replaceChildren(...result);
});

const formatCode = (text, ...codeTexts) => text.flatMap((text, index) => [ text, Object.assign(document.createElement("code"), { textContent: codeTexts[index] }) ]).slice(0, -1);
#output { margin-top: 0.4em; line-height: 1.4em; white-space: pre-wrap; }
code { background: #ddd; padding: 0.2em 0.5em; border-radius: 0.2em; }
<input id="relativeURL" type="text" placeholder="example.com">
<input id="baseURL" type="text" placeholder="https://example.com">
<div id="output">Type a relative URL in the left input and an absolute URL as a base in the right input (or leave it blank to use the current location).</div>

This code just tries new URL(" your URL "). This only works for absolute URLs, so if it works, great! Return it.

Otherwise, it’ll throw an error, but with trycatch we simply catch it and try the next option: new URL("// your URL ", base). The second argument is an absolute URL base to be used for the non-absolute URL1. With a valid second argument provided, the first argument can now be a relative URL. If a relative URL starts with //, it is interpreted as a protocol-relative URL, so the next component, your URL string, must be the host name.

The base used here is the same as the current location; in the Stack snippet above that’s "https://stacksnippets.net/js". You can provide any base such as "http://example.com" or "http://localhost" or even "ftp://anything" as a second argument to asAbsoluteURL.

Call Result (URL object with this href)
asAbsoluteURL("example.com") "https://example.com/"
asAbsoluteURL("//example.com") "https://example.com/"
asAbsoluteURL("www.example.com") "https://www.example.com/"
asAbsoluteURL("https://example.com") "https://example.com/"
asAbsoluteURL("example.com", "http://localhost") "http://example.com/"

1: To be clear:

  • example.com is a path-relative URL, because with the base https://example.net/path/file it would result in https://example.net/path/example.com.
  • /example.com is an origin-relative URL, because with the base https://example.net/path/file it would result in https://example.net/example.com.
  • //example.com is a protocol-relative URL, because with the base https://example.net/path/file it would result in https://example.com.
  • Related