Recarregar a página fornece uma solicitação GET incorreta com o modo AngularJS HTML5


193

Quero ativar o modo HTML5 para meu aplicativo. Eu coloquei o seguinte código para a configuração, como mostrado aqui :

return app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {

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

    $routeProvider.when('/', {
        templateUrl: '/views/index.html',
        controller: 'indexCtrl'
    });
    $routeProvider.when('/about',{
        templateUrl: '/views/about.html',
        controller: 'AboutCtrl'
    });

Como você pode ver, eu usei o $locationProvider.html5modee mudei todos os meus links no ng-hrefpara excluir o /#/.

O problema

No momento, eu posso ir localhost:9000/e ver a página de índice e navegar para as outras páginas como localhost:9000/about.

No entanto, o problema ocorre quando atualizo a localhost:9000/aboutpágina. Eu recebo a seguinte saída:Cannot GET /about

Se eu olhar para as chamadas de rede:

Request URL:localhost:9000/about
Request Method:GET

Enquanto, se eu for primeiro localhost:9000/e clicar em um botão que navega até /abouteu recebo:

Request URL:http://localhost:9000/views/about.html

O que renderiza a página perfeitamente.

Como posso ativar o angular para obter a página correta quando atualizo?



1
Eu resolvo isso para mim. Veja aqui stackoverflow.com/questions/22394014/…
Sasha B.

Respostas:


99

Dos documentos angulares

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

A razão para isso é que, quando você visita a página pela primeira vez ( /about), por exemplo, após uma atualização, o navegador não tem como saber que este não é um URL real, por isso segue em frente e o carrega. No entanto, se você carregou a página raiz primeiro e todo o código javascript, quando você navega para /aboutAngular pode chegar lá antes que o navegador tente acessar o servidor e manipulá-lo de acordo.


21
Quando diz "você precisa reescrever todos os seus links no ponto de entrada do seu aplicativo (por exemplo, index.html)", o que isso significa? Isso significa que eu tenho que entrar no meu $routeProvidere mudar os caminhos de cada um, templateUrlpor exemplo, de templateUrl : '/views/about.html',para templateUrl : 'example.com/views/about.html',?

7
Não, suas regras no $ routeProvider devem permanecer como estão. A resposta refere-se ao "lado do servidor". Refere-se à alteração do roteamento no servidor que fornece suas páginas html angulares. Antes de cada solicitação chegar ao seu aplicativo angular, ela deve passar primeiro pelo roteamento do servidor, que deve reescrever todas as solicitações para apontar para o ponto de entrada do aplicativo angular (por exemplo, 'index.html' ou '/', dependendo de como o seu angular rotas funcionam).
marni

Caso eu sirva meu index.html minificado por meio de uma CDN, como é que esse mecanismo de roteamento será executado?
CrazyGeek


5
Bom artigo para apache, há um exemple htaccess: ngmilk.rocks/2015/03/09/...
Alcalyn

63

Existem algumas coisas a serem configuradas para que o seu link no navegador pareça http://yourdomain.com/pathe estas são as configurações angulares do servidor + configuração +

1) AngularJS

$routeProvider
  .when('/path', {
    templateUrl: 'path.html',
  });
$locationProvider
  .html5Mode(true);

2) no lado do servidor, basta colocar .htaccessdentro da pasta raiz e colar

RewriteEngine On 
Options FollowSymLinks

RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /#/$1 [L]

Coisas mais interessantes para ler sobre o modo html5 em angularjs e a configuração necessária para diferentes ambientes https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-configure-your-server -to-work-with-html5mode Também esta pergunta pode ajudar você $ location / alternar entre html5 e modo de hashbang / reescrita de link


Oi. Eu tentei a solução acima, fiz alterações no meu arquivo de configuração. Mas ainda não funciona. A atualização do URL ainda gera o erro "404 NÃO ENCONTRADO".
KTn

Como redirecionar / reescrever no aplicativo implantado no Websphere?
Gary

Não vejo por que não!
lokers 27/03/19

36

Eu tive um problema semelhante e resolvi:

  • Usando <base href="https://stackoverflow.com/index.html">na página de índice

  • Usando um middleware catch all route no meu nó / servidor Express da seguinte maneira (coloque-o após o roteador):

app.use(function(req, res) {
    res.sendfile(__dirname + '/Public/index.html');
});

Eu acho que isso deve colocá-lo em funcionamento.

Se você usa um servidor apache, convém modificar seus links. Não é difícil de fazer. Apenas algumas alterações nos arquivos de configuração.

Tudo isso assumindo que você tenha o html5mode ativado no angularjs. Agora. observe que no angular 1.2, declarar um URL base não é mais recomendado.


1
Esta solução funcionou para mim com a configuração: <base href="https://stackoverflow.com/">e adicionando o roteamento Express que você sugeriu. Mucho obrigado.
Gilad Peleg

Tahir Chad, você ainda precisava usar uma reescrita de URL no servidor depois de implementar o <base href="https://stackoverflow.com/index.html">?
Cody

Sim, você pode ver a reescrita de URL como uma maneira de definir o URL base do seu aplicativo / site em pedra (domínio incluído). A instrução html 'base url' é apenas uma maneira de garantir que todos os links relativos sejam derivados corretamente da mesma base. O URL base não é absolutamente necessário se seus links forem absolutos.
Taye

1
Quando eu uso isso, apenas vejo o código html na página, não processado como uma página real.
Noah

2
Esta resposta merece estrelas triplas! Obrigado por fornecer uma resposta tão direta. Todos os outros repetem a mesma linha sobre "a necessidade de redirecionar o servidor".
Ahmed Haque

27

Solução para BrowserSync e Gulp.

Em https://github.com/BrowserSync/browser-sync/issues/204#issuecomment-102623643

Primeira instalação connect-history-api-fallback:

npm --save-dev install connect-history-api-fallback

Em seguida, adicione-o ao seu gulpfile.js:

var historyApiFallback = require('connect-history-api-fallback');

gulp.task('serve', function() {
  browserSync.init({
    server: {
      baseDir: "app",
      middleware: [ historyApiFallback() ]
    }
  });
});

Oh snap! Finalmente, alívio do desenvolvimento da vista angular! E, como bônus, também os URLs padrão (sem hash-bang)! Obrigado!
bit-less

1
Funciona como um encanto ... Muito obrigado!
Nishanth Nair

1
Se você estiver usando uma porta específica (por exemplo, o modelo em branco Ionic é executado na porta 8100), basta adicionar uma propriedade adicional após o servidor server: { ... }, port: 8100, em seguida, basta adicionar a servetarefa à sua tarefa de gulp padrão (por exemplo gulp.task('default', ['sass', 'serve']);) e, em seguida, você pode executar o aplicativo sem os Cannot GET ...erros do navegador quando você atualiza a página executando gulp. Observe que a execução do servidor ionic serveainda encontra os erros.
Luke Schoen

Isso funciona bem quando eu uso o browsers de desenvolvimento, no prod estou executando o aplicativo como aplicativo expresso e redirecionando toda a rota para o aplicativo angular como app.get ('*', function (req, res) {req.session.valid = true ; res.redirect ('/');}); A página não carrega quando fornece o caminho diretamente?
AJ AJ

13

Você precisa configurar seu servidor para reescrever tudo em index.html para carregar o aplicativo:

https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#wiki-how-to-configure-your-server-to-work-with-html5mode


1
Perfeito, o problema para mim foi usar o Laravel Forge para provisionar o servidor, que tinha a linha nginx relevante como try_files $uri $uri/ /index.php?...alternativa ao necessário index.html. Obrigado pelo link!
CJ Thompson

Deve ser a melhor resposta, oferece todas as situações, independentemente do servidor. Funcionou perfeitamente com o NodeJS
Joe Lloyd

10

Eu escrevi um middleware de conexão simples para simular a reescrita de URL em projetos difíceis. https://gist.github.com/muratcorlu/5803655

Você pode usar assim:

module.exports = function(grunt) {
  var urlRewrite = require('grunt-connect-rewrite');

  // Project configuration.
  grunt.initConfig({
    connect: {
      server: {
        options: {
          port: 9001,
          base: 'build',
          middleware: function(connect, options) {
            // Return array of whatever middlewares you want
            return [
              // redirect all urls to index.html in build folder
              urlRewrite('build', 'index.html'),

              // Serve static files.
              connect.static(options.base),

              // Make empty directories browsable.
              connect.directory(options.base)
            ];
          }
        }
      }
    }
  })
};

Existe uma maneira manual de realizar a reescrita de URL no $routeProvider?

1
Recebo um urlRewriteaviso não definido ao tentar executá-lo e estou tendo dificuldades para encontrar a conexão correta com a reescrita que posso usar.

Mostra um erro "A criação do objeto não possui o método 'initConfig' Use --force para continuar."
Edonbajrami

8

Se você estiver na pilha do .NET com o MVC com o AngularJS, é isso que você precisa fazer para remover o '#' do URL:

  1. Configure seu href base na sua página _Layout: <head> <base href="https://stackoverflow.com/"> </head>

  2. Em seguida, adicione o seguinte na sua configuração angular do aplicativo: $locationProvider.html5Mode(true)

  3. Acima, o '#' será removido do URL, mas a atualização da página não funcionará, por exemplo, se você estiver na atualização da página "yoursite.com/about", você receberá 404. Isso ocorre porque o MVC não conhece o roteamento angular e, por padrão, o MVC procurará uma página do MVC para 'about' que não existe no caminho de roteamento do MVC. A solução alternativa para isso é enviar todas as solicitações de página do MVC para uma única exibição do MVC e você pode fazer isso adicionando uma rota que captura todos os URLs


routes.MapRoute(
        name: "App",
        url: "{*url}",
        defaults: new { controller = "Home", action = "Index" }
    );

8

Regra de reconfiguração de URL do IIS para evitar erro 404 após a atualização da página em html5mode

Para execução angular no IIS no Windows

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

Rotas NodeJS / ExpressJS para evitar erro 404 após a atualização da página no html5mode

Para execução angular em Node / Express

var express = require('express');
var path = require('path');
var router = express.Router();

// serve angular front end files from root path
router.use('/', express.static('app', { redirect: false }));

// rewrite virtual urls to angular app to enable refreshing of internal pages
router.get('*', function (req, res, next) {
    res.sendFile(path.resolve('app/index.html'));
});

module.exports = router;

Mais informações em: AngularJS - Ativar atualização da página no modo HTML5 sem erros 404 no NodeJS e IIS


Obrigado, as regras do IIS me ajudaram com um aplicativo angular2 implantado em azul que falhou ao direcionar para uma rota filho.
Bitsprint #

Não posso agradecer o suficiente pela solução para o IIS (localhost no VS 2013). Bati minha cabeça por tempo suficiente em frustração, finalmente encontrei esta resposta. Funcionou como um encanto.
Florida G.

5

Como já mencionado, você precisa reescrever rotas no servidor e definir <base href="https://stackoverflow.com/"/>.

Para gulp-connect:

npm install connect-pushstate

var gulp = require('gulp'),
  connect = require('gulp-connect'),
  pushState = require('connect-pushstate/lib/pushstate').pushState;
...
connect.server({
  ...
  middleware: function (connect, options) {
    return [
      pushState()
    ];
  }
  ...
})
....

4

Estou usando o apache (xampp) no meu ambiente de desenvolvimento e o apache na produção, adicione:

errorDocument 404 /index.html

para o .htaccess resolva para mim esse problema.


4

Para Grunt e Browsersync, use connect-modrewrite aqui

var modRewrite = require('connect-modrewrite');    


browserSync: {
            dev: {
                bsFiles: {

                    src: [
                        'app/assets/css/*.css',
                        'app/*.js',
                        'app/controllers/*.js',
                        '**/*.php',
                        '*.html',
                        'app/jade/includes/*.jade',
                        'app/views/*.html',
               ],
            },
        options: {
            watchTask: true,
            debugInfo: true,
            logConnections: true,
            server: {
                baseDir :'./',
                middleware: [
                       modRewrite(['!\.html|\.js|\.jpg|\.mp4|\.mp3|\.gif|\.svg\|.css|\.png$ /index.html [L]'])
                ]
            },

            ghostMode: {
                scroll: true,
                links: true,
                forms: true
                    }
                }
            }
        },

há 15 acima dessa resposta que foram uma cintura de tempo. 1. npm install connect-modrewrite --save2. require in gruntfile3. copie o objeto de servidor acima
Omar

Graças isso funcionou para mim no gulp 4 com a configuração do browserSync.
Abdeali Chandanwala

Fico feliz que ajudou @Abdeali Chandanwala!
MrThunder 02/07/19

@Omar Não concordo com você, usuários menos avançados, como eu, se beneficiam ao ver como adicioná-lo ao gulpfile. É melhor lembrar que as pessoas têm diferentes níveis de conhecimento e que apenas escrever em gruntfile não seria útil, pois eu estaria assumindo que elas entendem como adicioná-lo ao gole.
MrThunder 02/07/19

3

Eu resolvi

test: {
        options: {
          port: 9000,
          base: [
            '.tmp',
            'test',
            '<%= yeoman.app %>'
          ],
         middleware: function (connect) {
                  return [
                      modRewrite(['^[^\\.]*$ /index.html [L]']),
                      connect.static('.tmp'),
                      connect().use(
                          '/bower_components',
                          connect.static('./bower_components')
                      ),
                      connect.static('app')
                  ];
              }
        }
      },

3

Estou respondendo a essa pergunta da pergunta maior:

Quando adiciono $ locationProvider.html5Mode (true), meu site não permite a colagem de URLs. Como faço para configurar meu servidor para funcionar quando html5Mode for verdadeiro?

Quando o html5Mode estiver ativado, o caractere # não será mais usado em seus URLs. O símbolo # é útil porque não requer configuração do lado do servidor. Sem #, o URL parece muito melhor, mas também requer reescritas no servidor. aqui estão alguns exemplos:

Para Express Rewrites with AngularJS, você pode resolver isso com as seguintes atualizações:

app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname + '/public/app/views/index.html'));
});

e

<!-- FOR ANGULAR ROUTING -->
<base href="/">

e

app.use('/',express.static(__dirname + '/public'));

Graças a sua solução. Para Nodejs, adicione app.js a app.use ('/ *', index);
Tigin

2

Acredito que seu problema esteja relacionado ao servidor. A documentação angular em relação ao modo HTML5 (no link da sua pergunta) afirma:

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

Acredito que você precisará configurar uma reescrita de URL de / para /.


Obrigado pela sua resposta. Portanto, quando estou na página sobre e atualizo, o servidor deve reescrever o URL para /? O problema é que eu trabalho com um back-end de trilhos que está em um domínio diferente. Toda a comunicação é feita por chamadas de API. Como eu faria essas reescritas de URL?
Dieter De Mesmaeker 15/05

O problema é que seu aplicativo está disponível na rota do servidor de / então, quando você atualizar um URL que seja / sobre o navegador, solicitará do servidor qualquer página que esteja em / sobre (nesse caso, você não tem páginas lá, é necessário redirecionar aqueles de volta).
Lucuma 15/05

2

Tivemos um redirecionamento de servidor no Express:

app.get('*', function(req, res){
    res.render('index');
});

e ainda estávamos recebendo problemas de atualização de página, mesmo depois de adicionarmos o <base href="https://stackoverflow.com/" />.

Solução: verifique se você está usando links reais na sua página para navegar; não digite a rota no URL ou você receberá uma atualização da página. (erro bobo, eu sei)

:-P


mas devido a isso todos os http pedido terá arquivo de índice, como por exemplo: / getJsonFromServer - uma solicitação HTTP que retorna dados, mas devido a esta configuração que também irá retornar página do índice
Vijay

1

Finalmente, eu tenho uma maneira de resolver esse problema pelo lado do servidor, pois é mais um problema com o próprio AngularJs. Estou usando o 1.5 Angularjs e tive o mesmo problema ao recarregar a página. Mas depois de adicionar o código abaixo no meu server.jsarquivo, ele salva meu dia, mas não é uma solução adequada ou não é uma boa maneira.

app.use(function(req, res, next){
  var d = res.status(404);
     if(d){
        res.sendfile('index.html');
     }
});

0

Eu achei o plugin Grunt ainda melhor, que funciona se você tiver o index.html e o Gruntfile.js no mesmo diretório;

https://npmjs.org/package/grunt-connect-pushstate

Depois disso no seu Gruntfile:

 var pushState = require('grunt-connect-pushstate/lib/utils').pushState;


    connect: {
    server: {
      options: {
        port: 1337,
        base: '',
        logger: 'dev',
        hostname: '*',
        open: true,
        middleware: function (connect, options) {
          return [
            // Rewrite requests to root so they may be handled by router
            pushState(),
            // Serve static files
            connect.static(options.base)
          ];
        }
      },
    }
},

O módulo grunhido-connect-pushState está obsoleto
Evan Solha

0
I solved same problem using modRewrite.  
AngularJS is reload page when after # changes.  
But HTML5 mode remove # and invalid the reload.  
So we should reload manually.
# install connect-modrewrite
    $ sudo npm install connect-modrewrite --save

# gulp/build.js
    'use strict';
    var gulp = require('gulp');
    var paths = gulp.paths;
    var util = require('util');
    var browserSync = require('browser-sync');
    var modRewrite  = require('connect-modrewrite');
    function browserSyncInit(baseDir, files, browser) {
        browser = browser === undefined ? 'default' : browser;
        var routes = null;
        if(baseDir === paths.src || (util.isArray(baseDir) && baseDir.indexOf(paths.src) !== -1)) {
            routes = {
                '/bower_components': 'bower_components'
            };
        }

        browserSync.instance = browserSync.init(files, {
            startPath: '/',
            server: {
            baseDir: baseDir,
            middleware: [
                modRewrite([
                    '!\\.\\w+$ /index.html [L]'
                ])
            ],
            routes: routes
            },
            browser: browser
        });
    }

0

Eu tive o mesmo problema com o aplicativo java + angular gerado com o JHipster . Eu o resolvi com o filtro e a lista de todas as páginas angulares nas propriedades:

application.yml:

angular-pages:
  - login
  - settings
...

AngularPageReloadFilter.java

public class AngularPageReloadFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.getRequestDispatcher("index.html").forward(request, response);
    }
}

WebConfigurer.java

private void initAngularNonRootRedirectFilter(ServletContext servletContext,
                                              EnumSet<DispatcherType> disps) {
    log.debug("Registering angular page reload Filter");
    FilterRegistration.Dynamic angularRedirectFilter =
            servletContext.addFilter("angularPageReloadFilter",
                    new AngularPageReloadFilter());
    int index = 0;
    while (env.getProperty("angular-pages[" + index + "]") != null) {
        angularRedirectFilter.addMappingForUrlPatterns(disps, true, "/" + env.getProperty("angular-pages[" + index + "]"));
        index++;
    }
    angularRedirectFilter.setAsyncSupported(true);
}

Espero que seja útil para alguém.


0

Gulp + browserSync:

Instale connect-history-api-fallback via npm, depois configure sua tarefa serve gulp

var historyApiFallback = require('connect-history-api-fallback');

gulp.task('serve', function() {
  browserSync.init({
    proxy: {
            target: 'localhost:' + port,
            middleware: [ historyApiFallback() ]
        }
  });
});

0

O código do lado do servidor é JAVA. Siga as etapas abaixo

Etapa 1: Faça o download do urlrewritefilter JAR Clique aqui e salve para criar o caminho WEB-INF / lib

etapa 2: ativar o modo HTML5 $ locationProvider.html5Mode (true);

Etapa 3: definir o URL base <base href="https://stackoverflow.com/example.com/"/>

etapa 4: copie e cole no seu WEB.XML

 <filter>
     <filter-name>UrlRewriteFilter</filter-name>
 <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>UrlRewriteFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

etapa 5: criar arquivo no WEN-INF / urlrewrite.xml

 <urlrewrite default-match-type="wildcard">


    <rule>
            <from>/</from>
            <to>/index.html</to>
        </rule>

    <!--Write every state dependent on your project url-->
    <rule>
            <from>/example</from>
            <to>/index.html</to>
        </rule>
    </urlrewrite>

Como adicionar regra para url dinâmico como - test.com/user/101 test.com/user/102
Krishna Kumar Chourasiya

0

Eu tenho essa solução simples que tenho usado e seus trabalhos.

Em App / Exceptions / Handler.php

Adicione isto na parte superior:

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

Em seguida, dentro do método de renderização

public function render($request, Exception $exception)
{
    .......

       if ($exception instanceof NotFoundHttpException){

        $segment = $request->segments();

        //eg. http://site.dev/member/profile
        //module => member
        // view => member.index
        //where member.index is the root of your angular app could be anything :)
        if(head($segment) != 'api' && $module = $segment[0]){
            return response(view("$module.index"), 404);
        }

        return response()->fail('not_found', $exception->getCode());

    }
    .......

     return parent::render($request, $exception);
}

0

Resolvi o problema adicionando o snippet de código abaixo ao arquivo node.js.

app.get("/*", function (request, response) {
    console.log('Unknown API called');
    response.redirect('/#' + request.url);
});

Nota: quando atualizamos a página, ela procurará a API em vez da página Angular (por causa de nenhuma tag # no URL.). Usando o código acima, estou redirecionando para o URL com #


-2

Basta escrever uma regra no web.config

<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 índice, adicione este

<base href="/">

funciona para mim, talvez você tenha esquecido de definir o app.config do Html5Mode

app.config(function ($stateProvider, $urlRouterProvider, $locationProvider){
    $locationProvider.html5Mode({ enabled: true, requireBase: true });
    $locationProvider.hashPrefix('!');
}
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.