¿Cómo analizo una URL en el nombre de host y la ruta en javascript?

379

Me gustaria tomar una cuerda

var a = "http://example.com/aa/bb/"

y procesarlo en un objeto tal que

a.hostname == "example.com"

y

a.pathname == "/aa/bb"
freddiefujiwara
fuente
11
En caso de que esté trabajando en la URL actual, puede acceder hostnamey pathnamedirectamente desde el locationobjeto.
rvighne
1
¿Qué pasa con "lastPathPart"?
Victor
No regex, pero el módulo Python tldextract hace exactamente esto: github.com/john-kurkowski/tldextract
Oliver Oliver

Respuestas:

395

La forma moderna:

new URL("http://example.com/aa/bb/")

Devuelve un objeto con propiedades hostnamey pathname, junto con algunos otros .

El primer argumento es una URL relativa o absoluta; si es relativo, debe especificar el segundo argumento (la URL base). Por ejemplo, para una URL relativa a la página actual:

new URL("/aa/bb/", location)

Además de los navegadores, esta API también está disponible en Node.js desde v7 hasta require('url').URL.

rvighne
fuente
77
¡Agradable! Sin embargo, las URL relativas lo rompen ... :( new URL('/stuff?foo=bar#baz')->SyntaxError: Failed to construct 'URL': Invalid URL
lakenen
56
Tecnología experimental: ¡IE no admite esto! developer.mozilla.org/en-US/docs/Web/API/URL/…
cwouter
10
@cwouter: Sin embargo, funciona en Edge, que reemplaza a IE
rvighne
44
esta es la forma de hacerlo, edge ya tiene 3 versiones, es decir, no importa
Claudiu Creanga
77
El hecho de que JavaScript no tenga una forma integrada de analizar URL que funciona en navegadores o servidores es bastante triste ...
Skitterm
365
var getLocation = function(href) {
    var l = document.createElement("a");
    l.href = href;
    return l;
};
var l = getLocation("http://example.com/path");
console.debug(l.hostname)
>> "example.com"
console.debug(l.pathname)
>> "/path"
freddiefujiwara
fuente
14
¿Estás seguro de que esta es una solución compatible con varios navegadores?
cllpse
70
Cabe señalar que, si bien esto puede ayudar / responder al póster original, esta respuesta solo funcionará para las personas que realizan JS en un navegador, ya que depende del DOM para hacer su trabajo.
Adam Batkin
44
Otro ejemplo de simplicidad, junto con el ingenio.
Saeed Neamati
26
No funciona en IE si el href es relativo. l.hostname estará vacío. Si solo proporciona URL completas, esto funcionará.
Derek Prior el
77
Incluso con URL absolutas, IE (probado en IE 11) se comporta de manera diferente a Chrome y Firefox. IE pathnameelimina la barra inclinada principal, mientras que los otros navegadores no. Entonces terminará con /patho path, dependiendo de su navegador.
TrueWill
299

encontrado aquí: https://gist.github.com/jlong/2428561

var parser = document.createElement('a');
parser.href = "http://example.com:3000/pathname/?search=test#hash";

parser.protocol; // => "http:"
parser.host;     // => "example.com:3000"
parser.hostname; // => "example.com"
parser.port;     // => "3000"
parser.pathname; // => "/pathname/"
parser.hash;     // => "#hash"
parser.search;   // => "?search=test"
parser.origin;   // => "http://example.com:3000"
Joseph Oster
fuente
11
Tenga en cuenta que si solo desea obtener las partes analizadas de la ubicación actual del navegador, las primeras dos líneas se convierten parser = location;y todas las líneas siguientes funcionan. Lo probé en Chrome e IE9 justo ahora.
Lee Meador
99
También tenga en cuenta que pathnameno incluye la barra inclinada principal en IE. Imagínate. : D
nevelis
3
Para IE, use "/" + parser.pathname
sbose
Advertencia: volverá http:incluso si pasa solo domain.coma href (sin ningún protocolo). Quería usar esto para verificar si faltaba el protocolo y, de ser así, podría agregarlo, pero supone que http: por lo tanto, no pude usarlo para este propósito.
Max Hodges
El nombre de host en realidad incluye el protocolo. Prueba en la última versión de Chrome.
AndroidDev
109

Aquí hay una función simple que usa una expresión regular que imita el a comportamiento de etiqueta.

Pros

  • comportamiento predecible (sin problemas de navegador cruzado)
  • no necesita el DOM
  • Es muy corto.

Contras

  • La expresión regular es un poco difícil de leer.

-

function getLocation(href) {
    var match = href.match(/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/);
    return match && {
        href: href,
        protocol: match[1],
        host: match[2],
        hostname: match[3],
        port: match[4],
        pathname: match[5],
        search: match[6],
        hash: match[7]
    }
}

-

getLocation("http://example.com/");
/*
{
    "protocol": "http:",
    "host": "example.com",
    "hostname": "example.com",
    "port": undefined,
    "pathname": "/"
    "search": "",
    "hash": "",
}
*/

getLocation("http://example.com:3000/pathname/?search=test#hash");
/*
{
    "protocol": "http:",
    "host": "example.com:3000",
    "hostname": "example.com",
    "port": "3000",
    "pathname": "/pathname/",
    "search": "?search=test",
    "hash": "#hash"
}
*/

EDITAR:

Aquí hay un desglose de la expresión regular

var reURLInformation = new RegExp([
    '^(https?:)//', // protocol
    '(([^:/?#]*)(?::([0-9]+))?)', // host (hostname and port)
    '(/{0,1}[^?#]*)', // pathname
    '(\\?[^#]*|)', // search
    '(#.*|)$' // hash
].join(''));
var match = href.match(reURLInformation);
Rems
fuente
44
No funciona con ninguna URL relativa. ¿Seguiste RFC-3986 al hacer la expresión regular? > getLocation ("// example.com/"); null> getLocation ("/ ruta /? búsqueda"); null> getLocation ("/ pathname /"); null> getLocation ("relativo"); nulo
gregers
2
Me gusta cómo esto no usa el DOM, pero Gregers tiene un buen punto. Sería bueno si esto puede manejar rutas relativas. Se requeriría usar window.location (un elemento a) para completar los espacios en blanco y agregar código. En ese caso, el método se volvería hipócrita. A menos que haya una alternativa, no estoy seguro de cómo se puede resolver esto perfectamente.
Turbo
Se agregó la clave href con la url original, esto proporciona coherencia en ese objeto de retorno con la implementación dom.
mattdlockyer
2
Si alguien necesita analizar las URL relativas aquí, está la expresión regular actualizada: / ^ (? :( https? \:) \ / \ /)? (([^: \ /? #] *) (?: \: ([0 -9] +))?) ([\ /] {0,1} [^? #] *) (\? [^ #] * |) (#. * |) $ /
shlensky
75
var loc = window.location;  // => "http://example.com:3000/pathname/?search=test#hash"

devuelve el currentUrl.

Si desea pasar su propia cadena como una URL ( no funciona en IE11 ):

var loc = new URL("http://example.com:3000/pathname/?search=test#hash")

Entonces puedes analizarlo como:

loc.protocol; // => "http:"
loc.host;     // => "example.com:3000"
loc.hostname; // => "example.com"
loc.port;     // => "3000"
loc.pathname; // => "/pathname/"
loc.hash;     // => "#hash"
loc.search;   // => "?search=test"
Peter Graham
fuente
60

La respuesta de freddiefujiwara es bastante buena, pero también necesitaba admitir URL relativas dentro de Internet Explorer. Se me ocurrió la siguiente solución:

function getLocation(href) {
    var location = document.createElement("a");
    location.href = href;
    // IE doesn't populate all link properties when setting .href with a relative URL,
    // however .href will return an absolute URL which then can be used on itself
    // to populate these additional fields.
    if (location.host == "") {
      location.href = location.href;
    }
    return location;
};

Ahora úselo para obtener las propiedades necesarias:

var a = getLocation('http://example.com/aa/bb/');
document.write(a.hostname);
document.write(a.pathname);

Ejemplo de JSFiddle: http://jsfiddle.net/6AEAB/

Claus
fuente
44
Esta debería ser la respuesta aceptada. Uso muy inteligente del manejo de URL relativo a absoluto. +1
L0j1k
Aparentemente no es la primera vez que un enlace JSFiddle murió: stackoverflow.com/questions/25179964/…
Claus
3
Esto funcionó muy bien, sin embargo, tuve una actualización que espero ayude a otros. Estoy usando esto para verificar el origen en una solicitud postMessage y cuando el puerto es un puerto predeterminado (80 o 443), no se agrega a la ruta. Lo comprobé condicionalmente al crear mi URL: var locationHost = (location.port !== '80' && location.port !== '443') ? location.host : location.hostname; var locationOrigin = location.protocol + '//' + locationHost;
rhoster
2
Hice este comentario en otra parte sobre una variante más popular de esta solución, pero como era mi solución favorita, quería repetirla aquí. En IE11, tener un nombre de usuario en href hará que todas estas lecturas de propiedades arrojen errores de seguridad. Ejemplo: " example.com " funcionará bien. Pero " [email protected] " o " username: [email protected] " intentará hacer referencia a una de las otras propiedades del elemento de anclaje (ejemplo: hash) para fallar y arrojar un error desagradable.
Clippy
17

js-uri (disponible en Google Code) toma una URL de cadena y resuelve un objeto URI a partir de ella:

var some_uri = new URI("http://www.example.com/foo/bar");

alert(some_uri.authority); // www.example.com
alert(some_uri);           // http://www.example.com/foo/bar

var blah      = new URI("blah");
var blah_full = blah.resolve(some_uri);
alert(blah_full);         // http://www.example.com/foo/blah
Rex M
fuente
¡¡¡Gracias!!! pero quiero uri = nueva ubicación (" example.com/aa/bb" ) typeof (window.location) == typeof (uri)
freddiefujiwara
Como window.location es una cadena, realmente no veo cómo eso sería posible o útil. ¿Por qué los tipos deben coincidir cuando se puede convertir fácilmente de uno a otro?
Rex M
developer.mozilla.org/en/DOM/window.location es una muy buena api !! así que espero convertir String en objeto window.location
freddiefujiwara
1
Configurar window.location cambia el navegador para que no suceda.
epascarello
1
Hmm eso es correcto. window.location no es una cadena, pero puede asignarse desde una cadena. No estoy seguro de si eso puede ser imitado, he intentado asignar el prototipo de ubicación a un nuevo objeto uri pero eso no funcionó.
Rex M
12

¿Qué pasa con la expresión regular simple?

url = "http://www.example.com/path/to/somwhere";
urlParts = /^(?:\w+\:\/\/)?([^\/]+)(.*)$/.exec(url);
hostname = urlParts[1]; // www.example.com
path = urlParts[2]; // /path/to/somwhere
Svestka
fuente
Intente analizar algo válido como //user:[email protected]/path/x?y=zy verá por qué la expresión regular simple no lo corta. Ahora, arroje algo inválido y también debería rescatarse de manera predecible.
Mikko Rantalainen
La expresión regular simple es para problemas simples :) Pero no me parece que una URL como esta no sea analizable por la expresión regular, solo necesitaría algunos ajustes más. Pero probablemente iría por alguna biblioteca si necesito algo más complejo y bulletroof.
svestka
12

hoy me encuentro con este problema y encontré: URL - API web MDN

var url = new URL("http://test.example.com/dir/subdir/file.html#hash");

Este regreso:

{ hash:"#hash", host:"test.example.com", hostname:"test.example.com", href:"http://test.example.com/dir/subdir/file.html#hash", origin:"http://test.example.com", password:"", pathname:"/dir/subdir/file.html", port:"", protocol:"http:", search: "", username: "" }

¡Espero que mi primera contribución te ayude!

A. Moynet
fuente
Respuesta duplicada
Martin van Driel
66
Sí, pero el chico de arriba acaba de actualizar su respuesta en 2017, yo lo
publico
Ah mi mal, lo siento
Martin van Driel
9

Aquí hay una versión que copié de https://gist.github.com/1847816 , pero la reescribí para que sea más fácil de leer y depurar. El propósito de copiar los datos de anclaje a otra variable llamada "resultado" es porque los datos de anclaje son bastante largos, por lo que copiar un número limitado de valores al resultado ayudará a simplificar el resultado.

/**
 * See: https://gist.github.com/1847816
 * Parse a URI, returning an object similar to Location
 * Usage: var uri = parseUri("hello?search#hash")
 */
function parseUri(url) {

  var result = {};

  var anchor = document.createElement('a');
  anchor.href = url;

  var keys = 'protocol hostname host pathname port search hash href'.split(' ');
  for (var keyIndex in keys) {
    var currentKey = keys[keyIndex]; 
    result[currentKey] = anchor[currentKey];
  }

  result.toString = function() { return anchor.href; };
  result.requestUri = result.pathname + result.search;  
  return result;

}
Biagio Arobba
fuente
6

El análisis de URL de navegador cruzado , resuelve el problema de ruta relativa para IE 6, 7, 8 y 9:

function ParsedUrl(url) {
    var parser = document.createElement("a");
    parser.href = url;

    // IE 8 and 9 dont load the attributes "protocol" and "host" in case the source URL
    // is just a pathname, that is, "/example" and not "http://domain.com/example".
    parser.href = parser.href;

    // IE 7 and 6 wont load "protocol" and "host" even with the above workaround,
    // so we take the protocol/host from window.location and place them manually
    if (parser.host === "") {
        var newProtocolAndHost = window.location.protocol + "//" + window.location.host;
        if (url.charAt(1) === "/") {
            parser.href = newProtocolAndHost + url;
        } else {
            // the regex gets everything up to the last "/"
            // /path/takesEverythingUpToAndIncludingTheLastForwardSlash/thisIsIgnored
            // "/" is inserted before because IE takes it of from pathname
            var currentFolder = ("/"+parser.pathname).match(/.*\//)[0];
            parser.href = newProtocolAndHost + currentFolder + url;
        }
    }

    // copies all the properties to this object
    var properties = ['host', 'hostname', 'hash', 'href', 'port', 'protocol', 'search'];
    for (var i = 0, n = properties.length; i < n; i++) {
      this[properties[i]] = parser[properties[i]];
    }

    // pathname is special because IE takes the "/" of the starting of pathname
    this.pathname = (parser.pathname.charAt(0) !== "/" ? "/" : "") + parser.pathname;
}

Uso ( demostración JSFiddle aquí ):

var myUrl = new ParsedUrl("http://www.example.com:8080/path?query=123#fragment");

Resultado:

{
    hash: "#fragment"
    host: "www.example.com:8080"
    hostname: "www.example.com"
    href: "http://www.example.com:8080/path?query=123#fragment"
    pathname: "/path"
    port: "8080"
    protocol: "http:"
    search: "?query=123"
}
acdcjunior
fuente
5

Para aquellos que buscan una solución moderna que funcione en IE, Firefox y Chrome:

Ninguna de estas soluciones que usan un elemento de hipervínculo funcionará igual en Chrome.Si pasa una URL no válida (o en blanco) a Chrome, siempre devolverá el host desde donde se llama el script. Entonces, en IE, quedará en blanco, mientras que en Chrome obtendrá localhost (o lo que sea).

Si está tratando de mirar al referente, esto es engañoso. Deberás asegurarte de que el host que recibiste estaba en la URL original para lidiar con esto:

    function getHostNameFromUrl(url) {
        // <summary>Parses the domain/host from a given url.</summary>
        var a = document.createElement("a");
        a.href = url;

        // Handle chrome which will default to domain where script is called from if invalid
        return url.indexOf(a.hostname) != -1 ? a.hostname : '';
    }
Rey de los hipócritas
fuente
¡Esto es algo muy importante a tener en cuenta!
2rs2ts
¡Sin embargo, esto rompe por completo las URL relativas!
lakenen
4

La forma AngularJS: violín aquí: http://jsfiddle.net/PT5BG/4/

<!DOCTYPE html>
<html>
<head>
    <title>Parse URL using AngularJS</title>
</head>
<body ng-app ng-controller="AppCtrl" ng-init="init()">

<h3>Parse URL using AngularJS</h3>

url: <input type="text" ng-model="url" value="" style="width:780px;">

<ul>
    <li>href = {{parser.href}}</li>
    <li>protocol = {{parser.protocol}}</li>
    <li>host = {{parser.host}}</li>
    <li>hostname = {{parser.hostname}}</li>
    <li>port = {{parser.port}}</li>
    <li>pathname = {{parser.pathname}}</li>
    <li>hash = {{parser.hash}}</li>
    <li>search = {{parser.search}}</li>
</ul>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js"></script>

<script>
function AppCtrl($scope) {

    $scope.$watch('url', function() {
        $scope.parser.href = $scope.url;
    });

    $scope.init = function() {
        $scope.parser = document.createElement('a');
        $scope.url = window.location;
    }

}
</script>

</body>
</html>
Joseph Oster
fuente
2
Será más angular si va a utilizar $documenty $windowservicios
Cherniv
3

Solución simple y robusta utilizando el patrón del módulo. Esto incluye una solución para IE donde pathnameno siempre tiene una barra diagonal ( /).

He creado un Gist junto con un JSFiddle que ofrece un analizador más dinámico. Le recomiendo que lo revise y envíe sus comentarios.

var URLParser = (function (document) {
    var PROPS = 'protocol hostname host pathname port search hash href'.split(' ');
    var self = function (url) {
        this.aEl = document.createElement('a');
        this.parse(url);
    };
    self.prototype.parse = function (url) {
        this.aEl.href = url;
        if (this.aEl.host == "") {
           this.aEl.href = this.aEl.href;
        }
        PROPS.forEach(function (prop) {
            switch (prop) {
                case 'hash':
                    this[prop] = this.aEl[prop].substr(1);
                    break;
                default:
                    this[prop] = this.aEl[prop];
            }
        }, this);
        if (this.pathname.indexOf('/') !== 0) {
            this.pathname = '/' + this.pathname;
        }
        this.requestUri = this.pathname + this.search;
    };
    self.prototype.toObj = function () {
        var obj = {};
        PROPS.forEach(function (prop) {
            obj[prop] = this[prop];
        }, this);
        obj.requestUri = this.requestUri;
        return obj;
    };
    self.prototype.toString = function () {
        return this.href;
    };
    return self;
})(document);

Manifestación

Salida

{
 "protocol": "https:",
 "hostname": "www.example.org",
 "host": "www.example.org:5887",
 "pathname": "/foo/bar",
 "port": "5887",
 "search": "?a=1&b=2",
 "hash": "section-1",
 "href": "https://www.example.org:5887/foo/bar?a=1&b=2#section-1",
 "requestUri": "/foo/bar?a=1&b=2"
}
{
 "protocol": "ftp:",
 "hostname": "www.files.com",
 "host": "www.files.com:22",
 "pathname": "/folder",
 "port": "22",
 "search": "?id=7",
 "hash": "",
 "href": "ftp://www.files.com:22/folder?id=7",
 "requestUri": "/folder?id=7"
}
Mr. Polywhirl
fuente
3

¿Por qué no lo usas?

        $scope.get_location=function(url_str){
        var parser = document.createElement('a');
        parser.href =url_str;//"http://example.com:3000/pathname/?search=test#hash";
        var info={
            protocol:parser.protocol,   
            hostname:parser.hostname, // => "example.com"
            port:parser.port,     // => "3000"
            pathname:parser.pathname, // => "/pathname/"
            search:parser.search,   // => "?search=test"
            hash:parser.hash,     // => "#hash"
            host:parser.host, // => "example.com:3000"      
        }
        return info;
    }
    alert( JSON.stringify( $scope.get_location("http://localhost:257/index.php/deploy/?asd=asd#asd"),null,4 ) );
tanthuc
fuente
3

También puede usar la parse_url()función del proyecto Locutus (anteriormente php.js).

Código:

parse_url('http://username:password@hostname/path?arg=value#anchor');

Resultado:

{
  scheme: 'http',
  host: 'hostname',
  user: 'username',
  pass: 'password',
  path: '/path',
  query: 'arg=value',
  fragment: 'anchor'
}
Andrey Rudenko
fuente
1
esa url no funcionó para mí, pero la encontré aquí github.com/hirak/phpjs/blob/master/functions/url/parse_url.js
Stan Quinn el
@StanQuinn, eso se debe a que php.js cambió su nombre a Locutus. He actualizado mi respuesta con un nuevo enlace.
Andrey Rudenko
3
function parseUrl(url) {
    var m = url.match(/^(([^:\/?#]+:)?(?:\/\/((?:([^\/?#:]*):([^\/?#:]*)@)?([^\/?#:]*)(?::([^\/?#:]*))?)))?([^?#]*)(\?[^#]*)?(#.*)?$/),
        r = {
            hash: m[10] || "",                   // #asd
            host: m[3] || "",                    // localhost:257
            hostname: m[6] || "",                // localhost
            href: m[0] || "",                    // http://username:password@localhost:257/deploy/?asd=asd#asd
            origin: m[1] || "",                  // http://username:password@localhost:257
            pathname: m[8] || (m[1] ? "/" : ""), // /deploy/
            port: m[7] || "",                    // 257
            protocol: m[2] || "",                // http:
            search: m[9] || "",                  // ?asd=asd
            username: m[4] || "",                // username
            password: m[5] || ""                 // password
        };
    if (r.protocol.length == 2) {
        r.protocol = "file:///" + r.protocol.toUpperCase();
        r.origin = r.protocol + "//" + r.host;
    }
    r.href = r.origin + r.pathname + r.search + r.hash;
    return m && r;
};
parseUrl("http://username:password@localhost:257/deploy/?asd=asd#asd");

Funciona con URL absolutas y relativas.

Nikolay
fuente
abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1
山 茶树 和 葡萄 树
@ 山 茶树 和 葡萄 树 He actualizado el código para manejar correctamente el subcomponente de información de usuario. Gracias por tu comentario, no noté ese problema antes
Nikolay
encanta esta expresión regular
Kunal
2

Deja de reinventar la rueda. Use https://github.com/medialize/URI.js/

var uri = new URI("http://example.org:80/foo/hello.html");
// get host
uri.host(); // returns string "example.org:80"
// set host
uri.host("example.org:80");
Hugo Sequeira
fuente
55
Porque cada vez que quieres resolver un problema ... ¿usas una biblioteca? Bien ... (no)
jiminikiz
44
No siempre (en realidad casi nunca), pero las URL son muy difíciles de analizar, hay muchos detalles en las RFC. Es mejor usar una biblioteca que haya sido utilizada y probada por miles.
Hugo Sequeira
¿Qué tal si usas lo que está incorporado, en lugar de que alguien más reinvente la rueda con una biblioteca? Ver stackoverflow.com/a/24006120/747739
Phil
No hay compatibilidad con IE11 para la función incorporada, por lo que esta biblioteca es excelente. Decir nunca usar una biblioteca es como decir que nunca deberíamos haber usado jQuery y simplemente escribir código nativo, lo cual es absolutamente ridículo. Cada desarrollador tiene un caso de uso diferente, no hay una 'mejor' forma, a veces la vainilla / nativa funciona mejor, a veces no ... algo que el 92% de los desarrolladores aún tiene que aprender.
tno2007
1

Simplemente use la biblioteca url.js (para web y node.js).

https://github.com/websanova/js-url

url: http://example.com?param=test#param=again

url('?param'); // test
url('#param'); // again
url('protocol'); // http
url('port'); // 80
url('domain'); // example.com
url('tld'); // com

etc...
Robar
fuente
1

un simple truco con la primera respuesta

var getLocation = function(href=window.location.href) {
    var l = document.createElement("a");
    l.href = href;
    return l;
};

esto puede usarse incluso sin argumento para averiguar el nombre de host actual getLocation (). hostname dará el nombre de host actual

sooraj
fuente