使用AngularJS HTML5模式重新加载页面会产生错误的GET请求

时间:2013-05-15 16:00:23

标签: angularjs html5 angular-routing

我想为我的应用启用HTML5模式。我已为配置添加了以下代码,如here所示:

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

正如您所看到的,我使用了$locationProvider.html5mode并更改了ng-href处的所有链接,以排除/#/

问题

目前,我可以转到localhost:9000/并查看索引页面并导航到localhost:9000/about等其他页面。

但是,刷新localhost:9000/about页面时会出现问题。我得到以下输出:Cannot GET /about

如果我查看网络电话:

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

如果我先转到localhost:9000/,然后点击导航到/about的按钮,我会得到:

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

这使页面完美呈现。

如何在刷新时启用角度来获取正确的页面?

24 个答案:

答案 0 :(得分:99)

来自angular docs

  

服务器端
  使用此模式需要在服务器端重写URL,基本上您必须重写所有链接到应用程序的入口点(例如index.html)

原因是当您第一次访问该页面时/about),例如刷新后,浏览器无法知道这不是真正的URL,因此它继续并加载它。但是,如果您首先加载了根页面以及所有javascript代码,那么当您导航到/about时,Angular可以在浏览器尝试访问服务器并相应处理之前进入其中

答案 1 :(得分:62)

设置的内容很少,因此您在浏览器中的链接看起来像http://yourdomain.com/path,这些是您的角配置+服务器端

1)AngularJS

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

2)服务器端,只需将.htaccess放入根文件夹并粘贴

RewriteEngine On 
Options FollowSymLinks

RewriteBase /

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

有关angularjs中html5模式和每个不同环境所需配置的更多有趣内容https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-configure-your-server-to-work-with-html5mode 此问题也可能对您有所帮助$location / switching between html5 and hashbang mode / link rewriting

答案 2 :(得分:34)

我有类似的问题,我解决了它:

  • 在索引页面中使用<base href="/index.html">

  • 在我的node / Express服务器中使用catch all all route middleware,如下所示(把它放在路由器之后):

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

我认为这应该让你起步并运行。

如果您使用的是apache服务器,则可能需要mod_rewrite您的链接。这并不难。只需对配置文件进行一些更改。

所有这一切都假设你在angularjs上启用了html5mode。 现在。请注意,在角度1.2中,实际上不再推荐声明基本URL。

答案 3 :(得分:26)

BrowserSync和Gulp的解决方案。

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

首先安装connect-history-api-fallback

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

然后将其添加到您的gulpfile.js:

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

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

答案 4 :(得分:13)

您需要配置服务器以将所有内容重写为index.html以加载应用程序:

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

答案 5 :(得分:10)

我编写了一个简单的连接中间件,用于模拟grunt项目的url重写。 https://gist.github.com/muratcorlu/5803655

您可以这样使用:

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

答案 6 :(得分:8)

如果您使用AngularJS与MVC进行.NET堆栈,则必须执行此操作才能删除&#39;#&#39;来自网址:

  1. 在_Layout页面中设置基本href:<head> <base href="/"> </head>

  2. 然后,在角度应用配置中添加以下内容:$locationProvider.html5Mode(true)

  3. 上面会移除&#39;#&#39;来自网址,但页面刷新不会工作,例如如果你在&#34; yoursite.com/about"页面刷新将给你一个404.这是因为MVC不知道角度路由,并且通过MVC模式,它将寻找关于&#39;关于&#39;在MVC路由路径中不存在。解决方法是将所有MVC页面请求发送到单个MVC视图,您可以通过添加捕获所有URL的路径来执行此操作

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

答案 7 :(得分:8)

IIS网址重写规则以防止在html5mode中页面刷新后出现404错误

对于Windows上的IIS下的角度运行

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

在html5mode中页面刷新后阻止404错误的NodeJS / ExpressJS路由

对于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;

更多信息:AngularJS - Enable HTML5 Mode Page Refresh Without 404 Errors in NodeJS and IIS

答案 8 :(得分:5)

正如其他人所提到的,您需要在服务器上重写路由并设置<base href="/"/>

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

答案 9 :(得分:4)

我在开发环境中使用apache(xampp),并在生产中使用apache, 添加:

errorDocument 404 /index.html

到.htaccess解决了这个问题。

答案 10 :(得分:3)

我解决了

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

答案 11 :(得分:3)

我从更大的问题回答这个问题:

  

当我添加$ locationProvider.html5Mode(true)时,我的网站将不允许粘贴网址。当html5Mode为true时,如何配置我的服务器?

启用html5Mode后,您的网址中将不再使用#字符。 #符号很有用,因为它不需要服务器端配置。没有#,url看起来更好,但它也需要服务器端重写。以下是一些例子:

对于使用AngularJS的Express重写,您可以使用以下更新来解决此问题:

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

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

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

答案 12 :(得分:3)

对于Grunt和Browsersync,请使用connect-modrewrite here

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

答案 13 :(得分:2)

我们在Express中有一个服务器重定向:

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

即使我们添加了<base href="/" />

,我们仍然会遇到页面刷新问题

解决方案:确保您使用页面中的真实链接进行导航;不要在URL中输入路线,否则您将获得页面刷新。 (愚蠢的错误,我知道)

: - P

答案 14 :(得分:2)

我相信您的问题与服务器有关。有关HTML5模式的角度文档(在您的问题中的链接)指出:

服务器端 使用此模式需要在服务器端重写URL,基本上您必须重写所有链接到应用程序的入口点(例如index.html)

我相信你需要设置一个url重写/关于/.

答案 15 :(得分:1)

最后我找到了一种方法来解决服务器端的问题,因为它更像是AngularJs本身的问题我使用的是1.5 Angularjs,我在重新加载页面时遇到了同样的问题。 但是在我的server.js文件中添加以下代码后,我将保存 ,但这不是一个正确的解决方案,也不是一个好方法。

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

答案 16 :(得分:0)

我已通过在node.js文件中添加以下代码段来解决此问题。

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

注意:当我们刷新页面时,它会查找API而不是Angular页面(因为URL中没有#tag)。使用上面的代码,我用#

重定向到url

答案 17 :(得分:0)

我找到了更好的Grunt插件,如果你将index.html和Gruntfile.js放在同一个目录中,那就有用了;

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

之后在你的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)
          ];
        }
      },
    }
},

答案 18 :(得分:0)

我有这个我一直在使用的简单解决方案及其作品。

在App / Exceptions / Handler.php

在顶部添加:

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

然后在渲染方法

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

答案 19 :(得分:0)

您的服务器端代码是JAVA,然后按照以下步骤

执行

第1步:下载urlrewritefilter JAR Click Here    并保存到构建路径WEB-INF / lib

第2步:启用HTML5模式$ locationProvider.html5Mode(true);

第3步:设置基本网址<base href="/example.com/"/>

第4步:复制并粘贴到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>

步骤5:在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>

答案 20 :(得分:0)

Gulp + browserSync:

通过npm安装connect-history-api-fallback,稍后配置你的服务gulp任务

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

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

答案 21 :(得分:0)

使用 JHipster 生成的 java + angular app遇到了同样的问题。 我用过滤器和属性中所有角度页面的列表解决了它:

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

希望,这会对某人有所帮助。

答案 22 :(得分: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
        });
    }

答案 23 :(得分:-2)

只需在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>

在索引中添加此

<base href="/">

它对我有用,也许你已经忘记设置Html5Mode app.config

app.config(function ($stateProvider, $urlRouterProvider, $locationProvider){
    $locationProvider.html5Mode({ enabled: true, requireBase: true });
    $locationProvider.hashPrefix('!');
}