Como analiso uma URL no nome do host e no caminho em javascript?

379

Eu gostaria de pegar uma corda

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

e processá-lo em um objeto tal que

a.hostname == "example.com"

e

a.pathname == "/aa/bb"
freddiefujiwara
fonte
11
Caso esteja trabalhando no URL atual, você pode acessar hostnamee pathnamediretamente do locationobjeto.
rvighne
11
e quanto a "lastPathPart"?
Victor
Não regex mas o tldextract Módulo Python faz isso exatamente: github.com/john-kurkowski/tldextract
Oliver Oliver

Respostas:

395

A maneira moderna:

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

Retorna um objeto com propriedades hostnamee pathname, junto com alguns outros .

O primeiro argumento é uma URL relativa ou absoluta; se for relativo, você precisará especificar o segundo argumento (o URL base). Por exemplo, para um URL relativo à página atual:

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

Além dos navegadores, esta API também está disponível no Node.js. desde a v7 até require('url').URL.

rvighne
fonte
7
Agradável! URLs relativos quebram isso ... :( new URL('/stuff?foo=bar#baz')->SyntaxError: Failed to construct 'URL': Invalid URL
lakenen
56
Tecnologia experimental: o IE não suporta isso! developer.mozilla.org/pt-BR/docs/Web/API/URL/…
cwouter
10
@cwouter: Ele faz o trabalho na borda no entanto, que substitui o IE
rvighne
4
esta é a maneira de fazê-lo, borda já está 3 versões em cima ou seja por isso não importa
Claudiu Creanga
7
O fato de que o JavaScript não tem um built-in maneira de URLs de análise que funciona em navegadores ou servidores é muito 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
fonte
14
Tem certeza de que esta é uma solução compatível com vários navegadores?
cllpse
70
Deve-se notar que, embora isso possa ajudar / responder ao pôster original, essa resposta funcionará apenas para pessoas que trabalham com JS em um navegador, uma vez que depende do DOM para fazer seu trabalho.
Adam Batkin
4
Outro exemplo de simplicidade, ao lado de engenhosidade.
Saeed Neamati
26
Não funciona no IE se o href for relativo. l.hostname estará vazio. Se você estiver fornecendo apenas URLs completos, isso funcionará.
Derek Anterior
7
Mesmo com URLs absolutas, o IE (testado no IE 11) se comporta de maneira diferente do Chrome e do Firefox. O IE pathnameremove a barra principal, enquanto os outros navegadores não. Então, você terminará com /pathou path, dependendo do seu navegador.
TrueWill
299

encontrado aqui: 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
fonte
11
Observe que, se você deseja apenas obter as partes analisadas do local atual do navegador, as primeiras duas linhas se tornam parser = location;e todas as linhas a seguir funcionam. Tentei no Chrome e IE9 agora.
31513 Lee Meador
9
Observe também que pathnamenão inclui a barra principal no IE. Vai saber. : D
nevelis
3
Para o IE, use "/" + parser.pathname
sbose 27/02
Aviso: ele retornará http:mesmo se você passar apenas domain.compara href (sem nenhum protocolo). Eu queria usar isso para verificar se o protocolo estava ausente e, se sim, poderia adicioná-lo, mas ele pressupõe que http: portanto, não foi possível usá-lo para esse fim.
Max Hodges
O nome do host realmente inclui o protocolo. Teste na versão mais recente do Chrome.
AndroidDev 23/08
109

Aqui está uma função simples usando um regexp que imita o acomportamento da tag.

Prós

  • comportamento previsível (sem problemas entre navegadores)
  • não precisa do DOM
  • é muito curto.

Contras

  • O regexp é um pouco difícil de ler

-

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:

Aqui está um detalhamento da expressão 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
fonte
4
Não funciona com nenhum URL relativo. Você seguiu o RFC-3986 ao fazer o regexp? > getLocation ("// exemplo.com/"); null> getLocation ("/ nome do caminho /? pesquisa"); null> getLocation ("/ nome do caminho /"); null> getLocation ("relativo"); null
gregers
2
Eu gosto de como isso não usa o DOM, mas gregers tem um bom argumento. Seria bom se isso pudesse lidar com caminhos relativos. Seria necessário usar window.location (um elemento a) para preencher os espaços em branco e adicionar código. Nesse caso, o método se tornaria hipócrita. A menos que haja uma alternativa, não tenho certeza de como isso pode ser resolvido perfeitamente.
Turbo
Adicionada a chave href com o URL original, isso fornece consistência nesse objeto de retorno com a implementação dom.
mattdlockyer
2
Se alguém precisar analisar URLs relativos, aqui está o regexp atualizado: / ^ (? :( https? \:) \ / \ /)? (([[:: / /? #] *) (?: \: ([0 -9] +))?) ([\ /] {0,1} [^? #] *) (\? [^ #] * |) (#. * |) $ /
shlensky
75
var loc = window.location;  // => "http://example.com:3000/pathname/?search=test#hash"

retorna o currentUrl.

Se você deseja passar sua própria string como um URL ( não funciona no IE11 ):

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

Então você pode analisá-lo 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
fonte
60

A resposta de freddiefujiwara é muito boa, mas eu também precisava oferecer suporte a URLs relativos no Internet Explorer. Eu vim com a seguinte solução:

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;
};

Agora use-o para obter as propriedades necessárias:

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

Exemplo do JSFiddle: http://jsfiddle.net/6AEAB/

Claus
fonte
4
Essa deve ser a resposta aceita. Uso muito inteligente de manipulação de URL relativa ao absoluto. +1
L0j1k
Aparentemente, não é a primeira vez que uma ligação jsFiddle morreu: stackoverflow.com/questions/25179964/...
Claus
3
Isso funcionou muito bem, no entanto, eu tive uma atualização que espero que ajude outras pessoas. Estou usando isso para verificar a origem em uma solicitação postMessage e quando a porta é uma porta padrão (80 ou 443), ela não é anexada ao caminho. I condicionalmente verificado para que ao criar o meu URL: var locationHost = (location.port !== '80' && location.port !== '443') ? location.host : location.hostname; var locationOrigin = location.protocol + '//' + locationHost;
rhoster
2
Fiz esse comentário em outro lugar sobre uma variante mais popular dessa solução, mas como essa era a minha solução favorita, queria repeti-la aqui. No IE11, ter um nome de usuário no href fará com que todas essas leituras de propriedades gerem erros de segurança. Exemplo: " example.com " funcionará perfeitamente. Mas " nomedeusuá[email protected] " ou " nomedeusuário: [email protected] " fará qualquer tentativa de referenciar uma das outras propriedades do elemento âncora (exemplo: hash) para falhar e gerar um erro desagradável.
Clippy
17

js-uri (disponível no Google Code) pega um URL de string e resolve um objeto URI a partir dele:

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
fonte
obrigado!!! mas eu quero uri = new Local ( " example.com/aa/bb" ) typeof (window.location) == typeof (URI)
freddiefujiwara
Como window.location é uma string, eu realmente não vejo como isso seria possível ou útil. Por que os tipos precisam corresponder quando você pode converter facilmente de um para o outro?
Rex M
developer.mozilla.org/en/DOM/window.location é uma API muito boa !! por isso, espero Cordas convertido ao objeto window.location
freddiefujiwara
11
Definir window.location altera o navegador para que não aconteça.
epascarello
11
Hmm isso mesmo. window.location não é uma sequência, mas pode ser atribuída a partir de uma sequência. Não tenho certeza se isso pode ser imitado, tentei atribuir o protótipo de localização a um novo objeto de uri, mas isso não funcionou.
Rex M
12

E a expressão regular simples?

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
fonte
Tente analisar algo válido como //user:[email protected]/path/x?y=ze você verá por que a expressão regular simples não é suficiente. Agora jogue algo inválido e deve sair de maneira previsível também.
Mikko Rantalainen 02/10/19
Regex simples é para problemas simples :) Mas não me parece que uma URL como essa não possa ser analisada pelo regex, seria necessário apenas mais alguns ajustes. Mas eu provavelmente iria a uma biblioteca se precisar de algo mais complexo e mais complexo.
Svestka
12

hoje eu encontrei esse problema e encontrei: URL - MDN Web APIs

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

Este retorno:

{ 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: "" }

Esperando que minha primeira contribuição o ajude!

A. Moynet
fonte
Resposta duplicada
Martin van Driel
6
Sim, mas o cara no topo apenas atualizar seu awser em 2017, me eu postá-lo em 2016.
A. Moynet
Ah, meu mal, desculpe
Martin van Driel
9

Aqui está uma versão que copiei de https://gist.github.com/1847816 , mas reescrevi para facilitar a leitura e a depuração. O objetivo de copiar os dados da âncora para outra variável chamada "resultado" é porque os dados da âncora são bastante longos e, portanto, copiar um número limitado de valores para o resultado ajudará a simplificar o 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
fonte
6

A análise de URL entre navegadores , soluciona o problema de caminho relativo para o IE 6, 7, 8 e 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 ( demo JSFiddle aqui ):

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
fonte
5

Para quem procura uma solução moderna que funcione no IE, Firefox e Chrome:

Nenhuma dessas soluções que usam um elemento de hiperlink funcionará da mesma maneira no chrome. Se você passar um URL inválido (ou em branco) para o chrome, ele sempre retornará o host de onde o script é chamado. Portanto, no IE, você ficará em branco, enquanto no Chrome, você receberá o host local (ou o que for).

Se você está tentando olhar para o referenciador, isso é enganoso. Você quer ter certeza de que o host que você voltou estava no URL original para lidar com isso:

    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 : '';
    }
KingOfHypocrites
fonte
Isso é uma coisa muito importante a considerar!
2rs2ts
Isso quebra completamente os URLs relativos!
lakenen
4

A maneira do AngularJS - brinque aqui: 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
fonte
2
Será mais angular se você vai usar $documente $windowserviços
Cherniv
3

Solução simples e robusta usando o padrão do módulo. Isso inclui uma correção para o IE, na qual pathnamenem sempre há uma barra invertida ( /).

Eu criei um Gist junto com um JSFiddle que oferece um analisador mais dinâmico. Eu recomendo que você dê uma olhada e forneça feedback.

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);

Demo

Resultado

{
 "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
fonte
3

Por que não o usa?

        $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
fonte
3

Você também pode usar a parse_url()função do projeto Locutus (antigo 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
fonte
11
que url não funcionou para mim, mas eu achei aqui github.com/hirak/phpjs/blob/master/functions/url/parse_url.js
Stan Quinn
@ StanQuinn, isso ocorre porque o php.js mudou seu nome para Locutus. Atualizei minha resposta com o novo link.
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 com URLs absolutos e relativos

Nikolay
fonte
abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1
山茶树和葡萄树
Atualizei o código para lidar com o subcomponente userinfo corretamente. Obrigado por seu comentário, eu não notei esse problema antes
Nikolay
amo este regex
Kunal
2

Pare de reinventar a roda. 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
fonte
5
Porque toda vez que você deseja resolver um problema ... usa uma biblioteca? Ok ... (não)
jiminikiz
4
Nem sempre (na verdade quase nunca), mas os URLs são muito difíceis de analisar, existem muitos detalhes nos RFCs. Melhor usar uma biblioteca que foi usada e testada por milhares.
Hugo Sequeira
Que tal usar apenas o que está embutido, em vez de alguém mais reinventar a roda com uma biblioteca? Veja stackoverflow.com/a/24006120/747739
Phil
Não há suporte para IE11 para a função interna, portanto, esta biblioteca é excelente. Dizer para nunca usar uma biblioteca é como dizer que nunca devemos usar o jQuery e apenas escrever código nativo, o que é absolutamente ridículo. Todo desenvolvedor tem um caso de uso diferente, não há uma maneira 'melhor', às vezes o vanilla / native funciona melhor, às vezes não ... algo que 92% dos desenvolvedores ainda precisa aprender.
tno2007 4/03
1

Basta usar a biblioteca url.js (para web e 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...
Roubar
fonte
1

um hack simples com a primeira resposta

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

isso pode ser usado mesmo sem argumento para descobrir o nome do host atual getLocation (). nome do host fornecerá o nome do host atual

sooraj
fonte