Wie teste ich, ob eine URL-Zeichenfolge absolut oder relativ ist?

75

Wie kann ich eine URL testen, wenn es sich um einen relativen oder absoluten Pfad in Javascript oder jQuery handelt? Ich möchte entsprechend behandeln, je nachdem, ob die übergebene URL ein lokaler oder externer Pfad ist.

if (urlString starts with http:// or https://)
 //do this
TruMan1
quelle

Antworten:

38
var pat = /^https?:\/\//i;
if (pat.test(urlString))
{
    //do stuff
}

Verwenden Sie für protokollbezogene URLs diesen regulären Ausdruck:

/^https?:\/\/|^\/\//i

strah
quelle
14
Dies beantwortet die gestellte Frage, aber Sie können auch protokollbezogene URLs berücksichtigen, die mit beginnen //.
Gerryster
2
Was ist, wenn die URL "file: //" enthält? BOOM! Tragödie. Die Antwort von @Philipp ist zuverlässiger.
Skay
1
Die akzeptierte Antwort ist zumindest 2019 ungültig. Chrome akzeptiert http: example.com gerne.
Gene S
175

SCHNELL

Wenn Sie nur testen müssen http://oder https://dann ist der effizienteste Weg:

if (urlString.indexOf('http://') === 0 || urlString.indexOf('https://') === 0)

UNIVERSAL

Ich würde jedoch einen universelleren, protokollunabhängigen Ansatz ohne Berücksichtigung der Groß- und Kleinschreibung vorschlagen :

var r = new RegExp('^(?:[a-z]+:)?//', 'i');
r.test('http://example.com'); // true - regular http absolute URL
r.test('HTTP://EXAMPLE.COM'); // true - HTTP upper-case absolute URL
r.test('https://www.exmaple.com'); // true - secure http absolute URL
r.test('ftp://example.com/file.txt'); // true - file transfer absolute URL
r.test('//cdn.example.com/lib.js'); // true - protocol-relative absolute URL
r.test('/myfolder/test.txt'); // false - relative URL
r.test('test'); // false - also relative URL

Erläutern Sie die RegExp

^(?:[a-z]+:)?//

^- Beginn der Zeichenfolge
(?:- Beginn einer nicht erfassten Gruppe
[a-z]+- ein beliebiges Zeichen von 'a' bis 'z' 1 oder mehrmals
:- Zeichenfolge (Doppelpunkt)
)?- Ende der nicht erfassten Gruppe. Gruppe, die 0 oder 1 Mal erscheint
//- Zeichenfolge (zwei Schrägstriche)
'i'- Flag ohne Berücksichtigung der Groß- und Kleinschreibung

Geo
quelle
warum az? Kann der Domainname nicht 0-9 und einen Bindestrich im Domainnamen haben?
Atul Gupta
3
richtig, aber wir suchen hier nicht nach Domainnamen, oder? Dies wird immer noch funktionieren:/^(?:[a-z]+:)?\/\//i.test('https://www.ex-maple-123.com');
Geo
Kann das Schema Ziffern enthalten? Wir alle kennen http, https, ftp und mailto. Definiert jemand benutzerdefinierte Schemata für interne Tools? Ich denke, OneNote und Outlook funktionieren unter Windows.
Yzorg
1
Dies erfasst keine "mailto:" - URLs. Nicht dass ich wüsste, ob Mailto-URLs absolut oder relativ sind ;-)
Peter
1
new RegExp('^(//|[a-z]+:)', 'i')sollte für die Anpassung arbeiten mailto:, about:, tel:usw. , einschließlich der bestehenden Testfälle. Die Idee hier ist, weiterhin protokollbezogene absolute URLs bereitzustellen und gleichzeitig die vorhandene Funktionalität zum Erkennen absoluter URLs zu erweitern, ohne die doppelten Schrägstriche ( //) überprüfen zu müssen . Somit r.test('mailto:[email protected]') === true, r.test('https:example.com') === trueund so weiter.
Matt Borja
22

Ursprüngliche Antwort

Eine sehr schnelle und sehr flexible Prüfung ist:

if (url.indexOf('://') > 0 || url.indexOf('//') === 0 ) {
    // URL is absolute; either "http://example.com" or "//example.com"
} else {
    // URL is relative
}

Dies erkennt eine absolute URL, wenn:

  • Die URL enthält ": //" irgendwo nach dem ersten Zeichen oder
  • URL beginnt mit "//" (protokollbezogen)

  • Kein Regex.
  • Keine jQuery oder andere Abhängigkeit.
  • Keine fest codierten Protokollnamen, die die Bedingung zwischen Groß- und Kleinschreibung unterscheiden.
  • Keine String-Manipulation (zB toLowerCase oder ähnliches).
  • Für Web-URLs oder interne Protokolle können nur Überprüfungen auf "relativ oder absolut" verwendet werden, jedoch keine anderen Überprüfungen.

Update 1 (Vollfunktionsbeispiel)

Hier ist eine kurze Funktion , dass die Renditen true / false für die angegebene URL:

function isUrlAbsolute(url) { 
    return (url.indexOf('://') > 0 || url.indexOf('//') === 0);
}

Und dasselbe in ES6:

const isUrlAbsolute = (url) => (url.indexOf('://') > 0 || url.indexOf('//') === 0)

Update 2 (URLs innerhalb des URL-Parameters)

Um URLs zusätzlich im Format zu adressieren, /redirect?target=http://example.orgempfehle ich, diesen Code zu verwenden:

function isUrlAbsolute(url) {
    if (url.indexOf('//') === 0) {return true;} // URL is protocol-relative (= absolute)
    if (url.indexOf('://') === -1) {return false;} // URL has no protocol (= relative)
    if (url.indexOf('.') === -1) {return false;} // URL does not contain a dot, i.e. no TLD (= relative, possibly REST)
    if (url.indexOf('/') === -1) {return false;} // URL does not contain a single slash (= relative)
    if (url.indexOf(':') > url.indexOf('/')) {return false;} // The first colon comes after the first slash (= relative)
    if (url.indexOf('://') < url.indexOf('.')) {return true;} // Protocol is defined before first dot (= absolute)
    return false; // Anything else must be relative
}

Und das gleiche in Kurzform und ES 6

// Traditional JS, shortened
function isUrlAbsolute(url) {
    return url.indexOf('//') === 0 ? true : url.indexOf('://') === -1 ? false : url.indexOf('.') === -1 ? false : url.indexOf('/') === -1 ? false : url.indexOf(':') > url.indexOf('/') ? false : url.indexOf('://') < url.indexOf('.') ? true : false;
}

// ES 6
const isUrlAbsolute = (url) => (url.indexOf('//') === 0 ? true : url.indexOf('://') === -1 ? false : url.indexOf('.') === -1 ? false : url.indexOf('/') === -1 ? false : url.indexOf(':') > url.indexOf('/') ? false : url.indexOf('://') < url.indexOf('.') ? true : false)

Hier einige Testfälle:

// Test
console.log( isUrlAbsolute('http://stackoverflow.com') ) // -> true
console.log( isUrlAbsolute('//stackoverflow.com') ) // -> true
console.log( isUrlAbsolute('stackoverflow.com') ) // -> false
console.log( isUrlAbsolute('Ftp://example.net') ) // -> true
console.log( isUrlAbsolute('/redirect?target=http://example.org') ) // -> false

Update 3 (relative URLs klären)

Ich habe einige Kommentare zu ungültiger Ausgabe gesehen:

  • Die Lösung gibt false für zurück localhost
  • Antwort schlägt fehl http:example.com

Diese URLs sind jedoch tatsächlich relative URLs . Es ist einfach zu testen:

  1. Erstellen Sie beispielsweise einige Ordner auf Ihrer localhost-Webroot a/b/c/
  2. Erstellen Sie eine index.html-Datei und fügen Sie den folgenden Link ein: <a href="localhost">test</a>
  3. Öffnen Sie die Indexseite in Ihrem Browser: http: //localhost/a/b/c/index.html und klicken Sie auf den Link. Sie enden auf http: // localhost / a / b / c / localhost (und nicht auf http: // localhost ).
  4. Gleiches passiert, wenn Sie den Link http:example.comin Ihre index.html-Datei einfügen. Sie enden auf http: //localhost/a/b/c/example.com anstelle von http://example.com
Philipp
quelle
4
Nee. Ich habe gerade einen Fehler in meinem Projekt aufgespürt und festgestellt, dass es sich auch um eine solche Funktion handelt. Die Webseite hatte eine URL wie /redirect?target=http://example.org
BeniBela
@ BeniBela, Sie könnten dies beheben, indem Siefunction isUrlAbsolute(url) { var firstSlash = url.indexOf('/'); var colonDoubleSlash = url.indexOf('://'); return ((firstSlash > 0 && colonDoubleSlash > 0 && colonDoubleSlash < firstSlash) || url.indexOf('//') === 0); }
Sebastian
@ BeniBela Sie haben Recht, dies kann in einigen Fällen passieren. Ich habe den obigen Code aktualisiert, um dies zu handhaben. Ich empfehle jedoch dringend, alle Abfrageparameter per URL zu codieren, dh zu verwenden/redirect?target=http%3A%2F%2Fexample.com
Philipp
Dies beantwortet die Frage, testet jedoch nicht wirklich, ob die Eingabe absolut ist. Zum Beispiel kommt "/ aaa / bbb" als "relativ" zurück, wenn es tatsächlich absolut ist.
N73k
@ N73k eigentlich betrachte ich dein Beispiel "/ aaa / bbb" relativ zur Domain. <img src="/aaa/bbb">Dh wenn Sie auf site1.com und site2.com haben, sind beide Bilder unterschiedlich (dh relativ). Während <img src="//site1.com/aaa/bbb">ist für alle Domänen identisch (was absolut ist)
Philipp
17

Verwenden Sie einen regulären Ausdruck:

if (/^(?:[a-z]+:)?\/\//i.test(url))
SLaks
quelle
Dies scheint die universellste Antwort zu sein. Es fehlt nur eine protokollbezogene URL (zB //cdn.example.com/libary.js)
Geo
Obwohl in der Frage nur http und https erwähnt werden, muss eine allgemeine Lösung möglicherweise auch eine "mailto:" - URL berücksichtigen, die keine Schrägstriche enthält.
Mikebridge
@ Mikebridge sagen Sie, dass mailto:das manchmal absolut oder relativ sein kann?
Geo
1
@Geo: Nein; er sagt, das mailto:ist absolut, obwohl es keine /Charaktere hat.
SLaks
Bitte nehmen Sie hier am
Geo
13

Noch mehr universeller RFC-kompatibler URI-Ansatz:

(?:^[a-z][a-z0-9+.-]*:|\/\/) Regex Erklärung

Die anderen hier aufgeführten Lösungen würden für Links wie fehlschlagen mailto:[email protected]

RFC 3986 definiert ein Schema als:

scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )

3.1. Schema https://tools.ietf.org/html/rfc3986#section-3.1

Während die protokollbezogene URL gemäß Abschnitt 4.2 technisch gültig ist, hat Paul Irish den umgekehrten Weg eingeschlagen und betrachtet dies als Anti-Muster. Siehe http://www.paulirish.com/2010/the-protocol-relative-url/

4.2. Relative Referenz http://tools.ietf.org/html/rfc3986#section-4.2

Wenn Sie den regulären Ausdruck ohne Verwendung der protokollbezogenen URL möchten:

^[a-z][a-z0-9+.-]*:

Eine vollständige Liste anderer Arten gültiger Uri-Edge-Fälle finden Sie in der Liste hier: https://en.wikipedia.org/wiki/URI_scheme

Evan
quelle
3
Sollte das ^außerhalb der Gruppe gehen? Wie geschrieben würde es //in der nicht startenden Position übereinstimmen (also würde eine relative URL wie #//übereinstimmen). Es ist auch wichtig anzugeben, dass bei diesem regulären Ausdruck die Groß- und Kleinschreibung nicht berücksichtigt werden soll, damit die vollständige Definition wie folgt aussieht /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.
Sethobrien
Ich gehe davon aus, dass Ein-Zeichen-Schemata als Laufwerksbuchstaben betrachtet werden sollten. So Id ersetzen *mit +.
Knu
12

Wenn heutzutage viele Dienste eine protokollbezogene URL verwenden (z. B. //cdn.example.com/libary.js ), ist diese Methode sicherer:

var isAbsolute = new RegExp('^([a-z]+://|//)', 'i');

if (isAbsolute.test(urlString)) {
  // go crazy here
}
rgtk
quelle
1
Um URLs wie ' HTTP://WWW.GOOGLE.COM ' abzufangen , sollten Sie verwenden'^([A-Za-z]+://|//)'
Dean Meehan
3
Setzen Sie einfach das iFlag, um den Fall zu ignorieren. Antwort bearbeitet. Vielen Dank.
rgtk
9

Abhängig von Ihren Anforderungen denke ich, dass eine zuverlässigere Möglichkeit, dies festzustellen, darin besteht, die integrierte URL-Schnittstelle zu verwenden, um einige URL-Objekte zu erstellen und die Ursprünge zu vergleichen.

new URL(document.baseURI).origin === new URL(urlToTest, document.baseURI).origin;

Auf diese Weise kann der Browser all dies für Sie analysieren und herausfinden, ohne sich um die Nebenwirkungen von Randfällen kümmern zu müssen.

Brad
quelle
Dies ist eine großartige Neuerung zu den anderen Ententypisierungslösungen. Ich frage mich jedoch, warum Sie nicht vorschlagen new URL(document.baseURI).origin === new URL(urlToTest,document.baseURI).origin? Wäre dies nicht passender für Fälle, in denen die Webseite eine enthält <base>?
Menschlichkeit und
1
@humanityANDpeace Ja, gute Idee! Ich habe die Antwort mit Ihren Verbesserungen aktualisiert.
Brad
5
var external = RegExp('^(https?:)?//');
if(external.test(el)){
    // do something
}

BEARBEITEN:

Mit dem nächsten regulären Ausdruck können Sie sogar überprüfen, ob der Link zu derselben Domain oder zu einer externen Domain führt:

var external = RegExp('^((f|ht)tps?:)?//(?!' + location.host + ')');
if(external.test(el)){
    // do something
}
David
quelle
Sie müssen den .Zeichen entkommen, die mit ziemlicher Sicherheit im Hostnamen enthalten sind. Andernfalls stimmt foo.example.com auch mit fooXexample.com überein
Quentin
5

Verwenden Sie keine einfachen Dinge wie Regexp usw. Diese Dinge wurden von so vielen anderen Menschen gelöst. Besonders die Randfälle.

Schauen Sie sich URI.js an , es sollte den Job machen: http://medialize.github.io/URI.js/docs.html#is

var uri = new URI("http://example.org/");
uri.is("absolute") === true;
koppor
quelle
5
Nützlich, wenn Sie viele Manipulationen vornehmen mussten, aber es scheint übertrieben, eine JS-Bibliothek nur dafür zu verwenden.
Evan Donovan
4

Hier ist eine ziemlich robuste Lösung für die Browserumgebung:

Lassen Sie den Browser alles erledigen. Es sind keine komplizierten / fehleranfälligen regulären Ausdrücke erforderlich.

const isAbsoluteUrl = (url) => {
  const link = document.createElement('a');
  link.href = url;
  return link.origin + link.pathname + link.search + link.hash === url;
};
Etienne Martin
quelle
2
var adress = 'http://roflmao.com';
if (adress.substr(0,7) == 'http://' || adress.substr(0,8) == 'https://') {
    //
}
OptimusCrime
quelle
ja, das ist wahr. Ich benutze keinen Regex, weil ich daran lutsche. Wird HTTP in modernen Browsern nicht in http konvertiert?
OptimusCrime
2

Keine der genannten Lösungen löste einen redirect_urlHack, bei dem der Hacker eintrat /\/example.comoder /\\/example.com. Folgendes habe ich mir ausgedacht, um festzustellen, ob unsere Weiterleitungs-URL relativ war:

var isRelative = !redirectUrl.match(/(\:|\/\\*\/)/);  // Don't allow "//" (with optional "\"'s) or ":"
Dustin
quelle
1

Die folgende Funktion wird aufgerufen, wenn ein Klickereignis für einen Hyperlink auftritt, dh ein 'a'-Tag. Wenn das Tag eine URL enthält, die relativ ist oder denselben Host enthält, wird diese neue Seite in dieselbe Browserregisterkarte geladen. Wenn sie eine andere URL enthält, wird die Seite geladen in neuer Browser-Registerkarte

jQuery(document).ready(function() {
    $('a').click(function(){

        var a = this;
        var a_href = $(this).attr('href');
        var regex = new RegExp('^(?:[a-z]+:)?//', 'i');     

        if(a.host == location.host || regex.test(a_href) == false){
            a.target = '_self';
        }else{
            a.target = '_blank';
        }
    }); 
});
Prajyot
quelle
0

Es sollte nicht mit einem Schrägstrich oder Hash beginnen, und es sollte keinen doppelten Schrägstrich enthalten, wenn kein Fragezeichen oder Hash vorangestellt ist. Ich würde das nicht mit einem einzelnen regulären Ausdruck testen, es wäre sehr kompliziert, "kein doppelter Schrägstrich" zu finden.

function test(s) {
    return s.charAt(0) != "#"
      && s.charAt(0) != "/"
      && ( s.indexOf("//") == -1 
        || s.indexOf("//") > s.indexOf("#")
        || s.indexOf("//") > s.indexOf("?")
    );
}

wäre einfacher, klarer und imho schneller.

Bergi
quelle
0

Sie können einen try, catch-Block verwenden, um dabei zu helfen. Anstatt einen regulären Ausdruck zu verwenden, können Sie bei jedem Schritt die URL- Schnittstelle verwenden.

isExternalUrl (urlString) {
  try {
    const url = new URL(urlString) // THROW ON MISSING SCHEME

    // DOES THIS URL ORIGINATE FROM THIS WEBSITE?
    if (url.origin !== new URL(document.URL, document.baseURI).origin) {
      return true // IS EXTERNAL URL
    }
  } catch (_e) {
    // THROWS WHEN URL DOES NOT HAVE A SCHEME
    new URL(urlString, document.baseURL) // THROW AN EXCEPTION IF THE URL IS TRULY MALFORMED IN SOME WAY
  }

  return false
}
Jonathan
quelle
-1
var isExternalURL = url.toLowerCase().indexOf('http://') === 0 || url.toLowerCase().indexOf('https://') === 0 ;
Rinjan
quelle