Como configurar o IIS para a reescrita de URL de um aplicativo AngularJS no modo HTML5?


140

Eu tenho o projeto de semente AngularJS e adicionei

$locationProvider.html5Mode(true).hashPrefix('!');

para o arquivo app.js. Quero configurar o IIS 7 para rotear todas as solicitações para

http://localhost/app/index.html

para que isso funcione para mim. Como eu faço isso?

Atualizar:

Acabei de descobrir, fazer o download e instalar o módulo IIS Rewrite URL , esperando que isso torne fácil e óbvio alcançar meu objetivo.

Atualização 2 :

Eu acho que isso resume o que estou tentando alcançar (extraído da documentação do AngularJS Developer ):

O uso desse modo requer a reescrita de URL no servidor, basicamente você precisa reescrever todos os seus links no ponto de entrada do seu aplicativo (por exemplo, index.html)

Atualização 3:

Ainda estou trabalhando nisso e sei que NÃO preciso redirecionar (ter regras que reescrevam) certos URLs, como

http://localhost/app/lib/angular/angular.js
http://localhost/app/partials/partial1.html

portanto, qualquer coisa que esteja nos diretórios css, js, lib ou parcials não é redirecionada. Todo o resto precisará ser redirecionado para app / index.html

Alguém sabe como conseguir isso facilmente, sem precisar adicionar uma regra para cada arquivo?

Atualização 4:

Eu tenho duas regras de entrada definidas no módulo IIS Rewrite URL. A primeira regra é:

Regra de entrada 1 de reconfiguração de URL do IIS

A segunda regra é:

Regra de entrada 2 de reconfiguração de URL do IIS

Agora, quando eu navego para localhost / app / view1, ele carrega a página, mas os arquivos de suporte (os diretórios css, js, lib e parcials) também estão sendo reescritos na página app / index.html - para que tudo esteja chegando de volta como a página index.html, independentemente do URL usado. Eu acho que isso significa que minha primeira regra, que deve impedir que esses URLs sejam processados ​​pela segunda regra, não está funcionando ... alguma idéia? ...qualquer um? ...Eu me sinto tão sozinho... :-(


Obrigado por isso! Muito útil!
Ash Clarke

a segunda regra é crítica: você precisa ter certeza de que está direcionando app/index.htmle não app/ acionar explicitamente a página que serve o AngularJS. eu perdi 2 horas da minha vida antes de descobrir. :-)
Dexter Legaspi

Outra maneira de usar o ASP.NET MVC e adicionar ao RouteConfig.cs: routes.MapRoute (nome: "Padrão", url: "{* qualquer coisa}", padrões: new {controller = "Home", action = "Index", }); e o índice HomeController retorna apenas File ("~ / index-anyhinghere.html", "text / html"); Em seguida, o aplicativo se torna IIS independente
Toolkit

Eu tive que instalar o "módulo de reescrita de URL do 'Web Installer'". Se o módulo de reescrita de url não estiver lá, durante o recarregamento, a reescrita não funcionará. INSTALAR o módulo de reescrita de URL. :)
Bimal Das

Respostas:


289

Escrevo uma regra no web.config depois de $locationProvider.html5Mode(true)configurada app.js.

Espero que ajude alguém.

  <system.webServer>
    <rewrite>
      <rules>
        <rule name="AngularJS Routes" stopProcessing="true">
          <match url=".*" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
            <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>

No meu index.html, adicionei isso ao <head>

<base href="/">

Não se esqueça de instalar o IIS URL Rewrite no servidor.

Além disso, se você usar a API da Web e o IIS, isso funcionará se sua API estiver www.yourdomain.com/apidevido à terceira entrada (terceira linha de condição).


2
Isso foi muito útil e achei o mais completo. Obrigado!
Mario

2
Fico feliz que ajude @Mario
Yagiz Ozturk

5
PERFEITO! Esta foi uma questão difícil para mim, passou horas trabalhando nisso. Obrigado! CONSELHOS PARA ALGUÉM MAIS: Adicionei index.html ao <action type = "Rewrite" url "/index.html" /> Em seguida, no meu index.html (arquivo de diretório raiz do Angular SPA, o <base href = "/ index .html "/> Quando copiei o código acima, ele não correspondia ao meu <base href /> e, portanto, não funcionava corretamente. Adereços enormes para o padrão" / (api) "também continuavam tropeçando nessa parte.
Brad Martin

e se eu hospedar em sites do Azure?
Toolkit

7
É uma piada que temos que pular esses aros para o roteamento Angular. Isso mata o roteamento angular para mim. Que bagunça.
user441521

38

As regras de entrada do IIS, conforme mostrado na pergunta, funcionam. Eu tive que limpar o cache do navegador e adicionar a seguinte linha na parte superior da minha <head>seção da página index.html:

<base href="/myApplication/app/" />

Isso ocorre porque eu tenho mais de um aplicativo no host local e, portanto, solicitações para outras parciais estavam sendo encaminhadas para, em localhost/app/view1vez delocalhost/myApplication/app/view1

Espero que isso ajude alguém!


1
Eu não precisava disso, mas as edições das perguntas foram ótimas. Obrigado!
Ash Clarke

2
Também isso pode ser útil. Funcionou para mim ao recarregar a página. Sem regras especiais para cada arquivo: coderwall.com/p/mycbiq
Por G

como @ ash-clarke disse, não é necessário, mas é uma boa prática de qualquer maneira, pois permite que os links na página sejam agnósticos sobre a qual "subdiretório" pertence.
Dexter Legaspi

@ PerG Corri para a solução acima trabalhando, mas não em recarregar, aqui está o meu código: stackoverflow.com/questions/25644287/iis-url-rewrite-rules Alguma idéia?
Amcdnl

Não sei ou tentei filtrar determinadas chamadas. Uma maneira de contornar seria ter API em outro domínio, etc. Mas talvez isso não seja uma solução para você. Mas você pode continuar perguntando no meu link acima. E talvez o autor possa lhe responder?
por g

11

No meu caso, continuei recebendo 403.14 depois de configurar as regras de reescrita corretas. Acontece que eu tinha um diretório com o mesmo nome de uma das minhas rotas de URL. Depois que removi a regra de reescrita do IsDirectory, minhas rotas funcionaram corretamente. Existe um caso em que a remoção da negação de diretório pode causar problemas? Não consigo pensar em nada no meu caso. O único caso em que consigo pensar é se você pode procurar um diretório com seu aplicativo.

<rule name="fixhtml5mode" stopProcessing="true">
  <match url=".*"/>
  <conditions logicalGrouping="MatchAll">
    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
  </conditions>
  <action type="Rewrite" url="/" />
</rule>

10

O problema de ter apenas essas duas condições:

  <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
  <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />

é que eles funcionam apenas enquanto {REQUEST_FILENAME} existir fisicamente no disco . Isso significa que pode haver cenários em que uma solicitação para uma exibição parcial nomeada incorretamente retorne a página raiz em vez de uma 404, o que causaria o carregamento angular duas vezes (e em certos cenários, pode causar um loop infinito desagradável).

Portanto, algumas regras seguras de "fallback" seriam recomendadas para evitar essas questões difíceis de solucionar:

  <add input="{REQUEST_FILENAME}" pattern="(.*?)\.html$" negate="true" />
  <add input="{REQUEST_FILENAME}" pattern="(.*?)\.js$" negate="true" />
  <add input="{REQUEST_FILENAME}" pattern="(.*?)\.css$" negate="true" />

ou uma condição que corresponda a qualquer arquivo que termina :

<conditions>
  <!-- ... -->
  <add input="{REQUEST_FILENAME}" pattern=".*\.[\d\w]+$" negate="true" />
</conditions>

1
Com tudo isso no lugar, meu site carrega muito bem, mas se estou no caminho ao longo do caminho, tenho problemas. Portanto, se o caminho no meu navegador for: 10.34.34.46/jbvdev/documents/BC498484 Inicialmente, ele aparece bem, mas se eu pressionar a atualização, tudo será interrompido porque ele começará a procurar os css e js em 10.34.34.46/jbvdev/documents / css ou 10.34.34.46/jbvdev/documents/js Alguém sabe como resolver esse problema?
Michael Mahony

1

A maneira mais fácil que encontrei é apenas redirecionar as solicitações que acionam 404 para o cliente. Isso é feito adicionando uma hashtag mesmo quando$locationProvider.html5Mode(true) está definido.

Esse truque funciona para ambientes com mais aplicativos da Web no mesmo site e exigindo restrições de integridade da URL (autenticação externa do EG). Aqui está passo a passo como fazer

index.html

Defina o <base>elemento corretamente

<base href="@(Request.ApplicationPath + "/")">

web.config

Primeiro redirecione 404 para uma página personalizada, por exemplo "Página inicial / erro"

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Home/Error" />
    </customErrors>
</system.web>

Controlador residencial

Implemente uma entrada simples ActionResultpara "traduzir" em uma rota do lado do cliente.

public ActionResult Error(string aspxerrorpath) {
    return this.Redirect("~/#/" + aspxerrorpath);
}

Esta é a maneira mais simples.


É possível (aconselhável?) Aprimorar a função Erro com alguma lógica aprimorada para redirecionar 404 para o cliente apenas quando a URL é válida e deixar o 404 disparar normalmente quando nada for encontrado no cliente. Digamos que você tenha essas rotas angulares

.when("/", {
    templateUrl: "Base/Home",
    controller: "controllerHome"
})
.when("/New", {
    templateUrl: "Base/New",
    controller: "controllerNew"
})
.when("/Show/:title", {
    templateUrl: "Base/Show",
    controller: "controllerShow"
})

Faz sentido redirecionar o URL para o cliente apenas quando ele começa com "/ New" ou "/ Show /"

public ActionResult Error(string aspxerrorpath) {
    // get clientside route path
    string clientPath = aspxerrorpath.Substring(Request.ApplicationPath.Length);

    // create a set of valid clientside path
    string[] validPaths = { "/New", "/Show/" };

    // check if clientPath is valid and redirect properly
    foreach (string validPath in validPaths) {
        if (clientPath.StartsWith(validPath)) {
            return this.Redirect("~/#/" + clientPath);
        }
    }

    return new HttpNotFoundResult();
}

Este é apenas um exemplo de lógica aprimorada, é claro que todo aplicativo da Web tem necessidades diferentes


1

Eu tenho tentado implantar um aplicativo Angular 7 simples, em um Azure Web App. Tudo funcionou bem, até o ponto em que você atualizou a página. Ao fazer isso, estava me apresentando um conteúdo movido por 500 erros. Eu li nos documentos do Angular e em alguns fóruns, que preciso adicionar um arquivo web.config à minha solução implantada e garantir que a regra de reescrita seja substituta no arquivo index.html. Após horas de frustração e testes de tentativa e erro, descobri que o erro era bastante simples: adicionar uma marca à minha marcação de arquivo.


1

Eu tive um problema semelhante com o Angular e o IIS lançando um código de status 404 na atualização manual e tentei a solução mais votada, mas isso não funcionou para mim. Também tentei várias outras soluções que precisavam lidar com o WebDAV e alterar manipuladores e nenhuma funcionou.

Felizmente eu encontrei esta solução e funcionou (tirei as peças que não precisava). Portanto, se nenhuma das opções acima funcionar para você ou mesmo antes de experimentá-las, tente isso e veja se isso corrige sua implantação angular no problema do iis.

Adicione o snippet ao seu webconfig no diretório raiz do seu site. Pelo que entendi, ele remove o código de status 404 de qualquer herança (applicationhost.config, machine.config), depois cria um código de status 404 no nível do site e redireciona de volta para a home page como uma página 404 personalizada.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <httpErrors errorMode="Custom">
      <remove statusCode="404"/>
      <error statusCode="404" path="/index.html" responseMode="ExecuteURL"/>
    </httpErrors>
  </system.webServer>
</configuration>
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.