我想让用户可以选择在输入字段中输入URL。目前它是非常严格的,因为它只接受像这样的绝对url:https://example.com/a.profile
.
用户给出的反馈是,他们也希望能够像这样键入一个相对URL:example.com/a.profile
或www.example.com/a.profile
。
如何获取任意的相对URL或绝对URL并确保输出是绝对URL?可以假定https:
协议适用于所有url。
不要乱用正则表达式。使用URL
API代替:
// 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>
此代码只是尝试new URL("
您的URL")
。这只适用于绝对url,所以如果它工作,伟大的!返回它。
否则,它会抛出一个错误,但是对于try
-catch
,我们只是捕获它并尝试下一个选项:new URL("//
您的URL", base)
。第二个参数是一个绝对URL基,用于非绝对URL1。提供了有效的第二个参数后,第一个参数现在可以是一个相对URL。如果一个相对URL以//
开头,它将被解释为协议相对URL,因此下一个组件(URL字符串)必须是主机名。
此处使用的base
与当前的location
相同;在上面的Stack代码片段中,它是"https://stacksnippets.net/js"
。您可以提供任何基,如"http://example.com"
或"http://localhost"
,甚至"ftp://anything"
作为asAbsoluteURL
的第二个参数。
Call | Result (URL object with thishref ) |
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/" |
我现在实现了下面的类,尽管我不确定是否考虑了所有的边缘情况。但也许它可以帮助未来的用户。
const FULL_URL_REGEX =
/(http|https)://(w+:{0,1}w*@)?(S+)(:[0-9]+)?(.ww+)(/|/([w#!:.?+=&%@!-/]))?/;
const HALF_URL_REGEX =
/(w+:{0,1}w*@)?(S+)(:[0-9]+)?(.ww+)(/|/([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');
}
}
}