Volver a cargar la página da una solicitud GET incorrecta con el modo AngularJS HTML5


193

Quiero habilitar el modo HTML5 para mi aplicación. He puesto el siguiente código para la configuración, como se muestra aquí :

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 puede ver, usé el $locationProvider.html5modey cambié todos mis enlaces en el ng-hrefpara excluir el /#/.

El problema

Por el momento, puedo ir localhost:9000/y ver la página de índice y navegar a las otras páginas como localhost:9000/about.

Sin embargo, el problema ocurre cuando actualizo la localhost:9000/aboutpágina. Me sale el siguiente resultado:Cannot GET /about

Si miro las llamadas de red:

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

Mientras que si primero voy localhost:9000/y luego hago clic en un botón que navega /about, obtengo:

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

Lo que hace la página perfectamente.

¿Cómo puedo habilitar angular para obtener la página correcta cuando actualizo?



1
Lo resuelvo por mi. Vea aquí stackoverflow.com/questions/22394014/…
Sasha B.

Respuestas:


99

De los documentos angulares

Lado del servidor El
uso de este modo requiere la reescritura de URL en el lado del servidor, básicamente debe reescribir todos sus enlaces al punto de entrada de su aplicación (por ejemplo, index.html)

La razón de esto es que cuando visita por primera vez la página ( /about), por ejemplo, después de una actualización, el navegador no tiene forma de saber que esta no es una URL real, por lo que continúa y la carga. Sin embargo, si ha cargado primero la página raíz y todo el código de JavaScript, cuando navegue a /aboutAngular puede ingresar allí antes de que el navegador intente acceder al servidor y manejarlo en consecuencia


21
Cuando dice "tienes que reescribir todos tus enlaces al punto de entrada de tu aplicación (por ejemplo, index.html)" ¿qué significa esto? ¿Significa que tengo que entrar en mi $routeProvidery cambiar los caminos de cada uno, templateUrlpor ejemplo, de templateUrl : '/views/about.html',a templateUrl : 'example.com/views/about.html',?

77
No, sus reglas en $ routeProvider deben permanecer como están. La respuesta se refiere al "lado del servidor". Se refiere a cambiar el enrutamiento en el servidor que proporciona sus páginas html angulares. Antes de que cada solicitud llegue a su aplicación angular, debe pasar primero por el enrutamiento del lado del servidor, que debería reescribir todas las solicitudes para que apunten al punto de entrada angular de su aplicación (por ejemplo, 'index.html' o '/', dependiendo de cómo rutas de trabajo).
marni

En caso de que sirva mi index.html minificado a través de CDN, ¿cómo se ejecutará este mecanismo de enrutamiento?
CrazyGeek


55
Buen artículo para apache, hay un ejemplo de htaccess: ngmilk.rocks/2015/03/09/…
Alcalyn

63

Hay algunas cosas que configurar para que se vea su enlace en el navegador http://yourdomain.com/pathy estos son su configuración angular + servidor

1) AngularJS

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

2) lado del servidor, solo ponlo .htaccessdentro de tu carpeta raíz y pega

RewriteEngine On 
Options FollowSymLinks

RewriteBase /

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

Más cosas interesantes para leer sobre el modo html5 en angularjs y la configuración requerida para diferentes entornos https://github.com/angular-ui/ui-router/wiki/Frequency-Asked-Questions#how-to-configure-your-server -to-trabajar-con-html5mode También esta pregunta podría ayudarlo a $ location / cambiar entre html5 y el modo hashbang / reescritura de enlaces


Hola. Probé la solución anterior, hice cambios en mi archivo de configuración. Pero aún así no funciona. Actualizar la URL aún da el error "404 NO ENCONTRADO".
kTn

¿Cómo redirijo / reescribo en la aplicación implementada en Websphere?
Gary

¡No veo por qué no!
Lokers

36

Tuve un problema similar y lo resolví de la siguiente manera:

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

  • Usando un middleware catch all route en mi nodo / servidor Express de la siguiente manera (póngalo después del enrutador):

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

Creo que eso debería ponerte en marcha.

Si usa un servidor apache, es posible que desee modificar_escribir sus enlaces. No es dificil de hacer. Solo unos pocos cambios en los archivos de configuración.

Todo lo que se supone es que tiene activado html5mode en angularjs. Ahora. tenga en cuenta que en angular 1.2, declarar una URL base ya no se recomienda en realidad.


1
Esta solución me funcionó con la configuración: <base href="https://stackoverflow.com/">y agregando el enrutamiento Express que sugirió. Mucho gracias.
Gilad Peleg

Tahir Chad, ¿todavía necesitabas usar una reescritura de URL del lado del servidor después de implementar el <base href="https://stackoverflow.com/index.html">?
Cody

Sí, puede ver la reescritura de URL como una forma de configurar la URL base de su aplicación / sitio web en piedra (dominio incluido). La declaración html 'base url' es solo una forma de garantizar que todos los enlaces relativos se derivan correctamente de la misma base. La URL base no es absolutamente necesaria si sus enlaces son absolutos.
Taye

1
Cuando uso esto, solo veo el código html en la página, no representado como una página real.
Noah

2
¡Esta respuesta merece estrellas triples! Gracias por proporcionar una respuesta tan directa. Todos los demás simplemente repiten la misma línea sobre "la necesidad de hacer la redirección del servidor".
Ahmed Haque

27

Solución para BrowserSync y Gulp.

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

Primera instalación connect-history-api-fallback:

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

Luego agréguelo a su archivo gulpfile.js:

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

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

Oh chasquido! ¡Finalmente alivio del desarrollo de la vista angular! Y como beneficio adicional, las URL estándar (sin un hash-bang) también se enrutan. ¡Gracias!
bit-bit el

1
Funciona como un encanto ... ¡Muchas gracias!
Nishanth Nair

1
Si está utilizando un puerto específico (es decir, la plantilla en blanco de Ionic se ejecuta en el puerto 8100), simplemente agregue una propiedad adicional después del servidor server: { ... }, port: 8100, es decir , simplemente agregue la servetarea a su tarea de trago predeterminada (es decir gulp.task('default', ['sass', 'serve']);), y luego puede ejecutar la aplicación sin los Cannot GET ...errores del navegador cuando actualiza la página ejecutando gulp. Tenga en cuenta que ejecutar el servidor con ionic servetodavía encuentra los errores.
Luke Schoen el

Esto funciona bien cuando uso el navegador de sincronización de desarrollo, en prod estoy ejecutando la aplicación como aplicación express y redirigiendo toda la ruta a la aplicación angular como app.get ('*', function (req, res) {req.session.valid = true ; res.redirect ('/');}); ¿La página no se carga cuando da la ruta directamente?
Sr. AJ

13

Debe configurar su servidor para reescribir todo en index.html para cargar la aplicación:

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


1
Perfecto, el problema para mí fue usar Laravel Forge para aprovisionar el servidor, que tenía la línea nginx relevante en try_files $uri $uri/ /index.php?...lugar de la necesaria index.html. Gracias por el enlace!
CJ Thompson

Debe ser la mejor respuesta, da cada situación sin importar su servidor. Funcionó perfectamente con NodeJS
Joe Lloyd

10

Escribí un middleware de conexión simple para simular la reescritura de url en proyectos grunt. https://gist.github.com/muratcorlu/5803655

Puedes usar así:

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

¿Hay alguna forma manual de lograr la reescritura de URL en el $routeProvider?

1
Obtengo urlRewriteuna advertencia no definida cuando intento ejecutarlo, y me cuesta encontrar la conexión correcta con reescritura que pueda usar.

Me muestra un error "La compilación de objetos no tiene el método 'initConfig' Use --force para continuar".
edonbajrami

8

Si está en .NET stack con MVC con AngularJS, esto es lo que debe hacer para eliminar el '#' de la URL:

  1. Configure su href base en su página _Diseño: <head> <base href="https://stackoverflow.com/"> </head>

  2. Luego, agregue lo siguiente en la configuración angular de su aplicación: $locationProvider.html5Mode(true)

  3. Arriba eliminará '#' de la URL, pero la actualización de la página no funcionará, por ejemplo, si está en "yoursite.com/about", la actualización de la página le dará un 404. Esto se debe a que MVC no sabe sobre el enrutamiento angular y por el patrón MVC buscará una página MVC para "acerca de" que no existe en la ruta de enrutamiento MVC. La solución para esto es enviar todas las solicitudes de página MVC a una sola vista MVC y puede hacerlo agregando una ruta que capture todas las URL


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

8

Regla de reescritura de URL de IIS para evitar errores 404 después de actualizar la página en html5mode

Para la ejecución angular bajo IIS en 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>

Rutas NodeJS / ExpressJS para evitar el error 404 después de la actualización de la página en html5mode

Para funcionamiento angular bajo 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;

Más información en: AngularJS: habilite la actualización de la página en modo HTML5 sin errores 404 en NodeJS e IIS


Gracias, las reglas de IIS me ayudaron con una aplicación angular2 implementada en azul que no podía dirigir a una ruta secundaria.
bitsprint

No puedo agradecerles lo suficiente por la solución para IIS (localhost en VS 2013). Golpeé mi cabeza lo suficiente por la frustración, finalmente encontré esta respuesta. Trabajado como un encanto.
Florida G.

5

Como otros han mencionado, debe reescribir las rutas en el servidor y configurarlas <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

Estoy usando apache (xampp) en mi entorno de desarrollo y apache en la producción, agregue:

errorDocument 404 /index.html

al .htaccess resuelve para mí este problema.


4

Para Grunt y Browsersync, use connect-modrewrite aquí

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

Hay 15 por encima de esta respuesta que fueron una cintura de tiempo. 1. npm install connect-modrewrite --save2. require in gruntfile3. copie el obj del servidor anterior
Omar

Gracias, esto funcionó para mí en Gulp 4 con la configuración de browserSync.
Abdeali Chandanwala

¡Me alegra que haya ayudado a @Abdeali Chandanwala!
MrThunder

@Omar No estoy de acuerdo con usted, los usuarios menos avanzados, como yo, se benefician al ver cómo agregarlo al archivo. Es mejor recordar que las personas tienen diferentes niveles de conocimiento y que solo escribir requiere en gruntfile no sería útil, ya que estaría asumiendo que entienden cómo agregarlo al trago.
MrThunder

3

Resolví a

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

Estoy respondiendo esta pregunta de la pregunta más grande:

Cuando agrego $ locationProvider.html5Mode (verdadero), mi sitio no permitirá el pegado de URL. ¿Cómo configuro mi servidor para que funcione cuando html5Mode es verdadero?

Cuando tiene habilitado html5Mode, el carácter # ya no se utilizará en sus URL. El símbolo # es útil porque no requiere configuración del lado del servidor. Sin #, la url se ve mucho mejor, pero también requiere reescrituras del lado del servidor. Aquí hay unos ejemplos:

Para Express Rewrites con AngularJS, puede resolver esto con las siguientes actualizaciones:

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

y

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

y

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

Gracias es la solución. Para Nodejs, agregue a app.js a app.use ('/ *', index);
Tigin

2

Creo que su problema es con respecto al servidor. La documentación angular con respecto al modo HTML5 (en el enlace de su pregunta) dice:

Lado del servidor El uso de este modo requiere la reescritura de URL en el lado del servidor, básicamente debe reescribir todos sus enlaces al punto de entrada de su aplicación (por ejemplo, index.html)

Creo que tendrá que configurar una reescritura de url de / about a /.


Gracias por su respuesta. Entonces, cuando estoy en la página Acerca de y actualizo, el servidor debe reescribir la url a /? El problema es que trabajo con un backend de rieles que está en un dominio diferente. Toda la comunicación es por llamadas API. ¿Cómo haría esas reescrituras de URL?
Dieter De Mesmaeker

El hecho es que su aplicación está disponible en la ruta de su servidor de / entonces cuando actualiza una url que es / sobre el navegador solicitará a su servidor cualquier página que esté en / sobre (en este caso no tiene páginas allí, por lo que debe redirigir los de vuelta).
lúcuma

2

Tuvimos una redirección de servidor en Express:

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

y seguíamos teniendo problemas de actualización de página, incluso después de agregar el <base href="https://stackoverflow.com/" />.

Solución: asegúrese de utilizar enlaces reales en su página para navegar; no escriba la ruta en la URL o obtendrá una actualización de la página. (error tonto, lo sé)

:-PAGS


pero debido a que CADA solicitud http tendrá un archivo de índice, como por ejemplo: / getJsonFromServer - una solicitud http que devuelve datos pero debido a esta configuración también devolverá la página de índice
vijay

1

Finalmente, obtuve una manera de resolver este problema por parte del servidor, ya que es más como un problema con AngularJs en sí, estoy usando 1.5 Angularjs y obtuve el mismo problema al volver a cargar la página. Pero después de agregar el siguiente código en mi server.jsarchivo, me salva el día, pero no es una solución adecuada o no es una buena manera.

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

0

He encontrado aún mejor el complemento Grunt, que funciona si tienes tu index.html y Gruntfile.js en el mismo directorio;

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

Después de eso en tu 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)
          ];
        }
      },
    }
},

El módulo grunt-connect-pushstate está en desuso
Evan Plaice

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

Tuve el mismo problema con la aplicación Java + angular generada con JHipster . Lo resolví con Filter y la lista de todas las páginas angulares en propiedades:

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 sea útil para alguien.


0

Gulp + browserSync:

Instale connect-history-api-fallback a través de npm, luego configure su tarea de servicio gulp

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

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

0

Su código del lado del servidor es JAVA, luego siga los siguientes pasos

Paso 1: Descargar urlrewritefilter JAR Haz clic aquí y guárdelo para crear la ruta WEB-INF / lib

paso 2: Habilita el modo HTML5 $ locationProvider.html5Mode (verdadero);

Paso 3: establecer la URL base <base href="https://stackoverflow.com/example.com/"/>

Paso 4: copia y pega en tu 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>

Paso 5: crea el archivo en 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>

Cómo agregar una regla para url dinámico como - test.com/user/101 test.com/user/102
Krishna Kumar Chourasiya

0

Tengo esta solución simple que he estado usando y funciona.

En App / Exceptions / Handler.php

Agregue esto en la parte superior:

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

Luego dentro del método de renderizado

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

He resuelto el problema agregando el fragmento de código a continuación en el archivo node.js.

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

Nota: cuando refresquemos la página, buscará la API en lugar de la página angular (debido a que no hay una etiqueta # en la URL). Usando el código anterior, estoy redirigiendo a la url con #


-2

Simplemente escriba una regla en 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>

En el índice agregue esto

<base href="/">

funciona para mí tal vez te hayas olvidado de configurar Html5Mode app.config

app.config(function ($stateProvider, $urlRouterProvider, $locationProvider){
    $locationProvider.html5Mode({ enabled: true, requireBase: true });
    $locationProvider.hashPrefix('!');
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.