使用启用 Html5 模式的 Angular Ui 路由器时页面重新加载失败

Posted

技术标签:

【中文标题】使用启用 Html5 模式的 Angular Ui 路由器时页面重新加载失败【英文标题】:Page reload fails when using Angular Ui Router with Html5 mode enabled 【发布时间】:2015-07-08 07:05:50 【问题描述】:

我在我的 Angular 应用程序中使用 Angular UI 路由器,并且我已启用 html5 模式以通过在配置中使用 $locationProvider 来删除 URL 中的 #。

var app = angular.module('openIDC', ['ui.router']);
app.config(function($urlRouterProvider, $stateProvider, $locationProvider) 

    $locationProvider.html5Mode(true);

    $urlRouterProvider.otherwise('/');

    $stateProvider
    .state('home', 
        url: '/',
        templateUrl: 'views/home.html',
        controller: 'HomeController'
    )
    .state('login', 
        url: '/login', 
        templateUrl: 'views/login.html',
        controller: 'LoginController'
    )
);

我还在 index.html 文件中设置了<base href="/" /> 标签。路由工作正常,我可以导航到页面并删除 # 但是当我使用浏览器上的重新加载按钮刷新页面时,会出现 404 错误响应。

为什么会发生这种情况,我该如何解决它并启用 HTML5 模式以获得正确的 URL

【问题讨论】:

【参考方案1】:

Kasun,发生这种情况的原因是因为您试图从您的子路由之一刷新页面(与 ui-router 无关)。

基本上,如果您请求www.yourdomain.com/,您的服务器设置可能会返回index.html,它会引导您的角度应用程序。应用加载后,任何进一步的 url 更改都会考虑html5Mode 并通过 ui-router 更新您的页面。

当您重新加载页面时,角度应用程序不再有效,因为它尚未加载,因此如果您尝试加载子路由(例如:www.yourdomain.com/someotherpage),那么您的服务器不知道如何处理使用 /someotherpage 并可能返回 404 或其他错误。

您需要做的是配置您的服务器,以便为 所有 路由返回您的 Angular 应用程序。我主要使用 node/express,所以我做了类似的事情:

app.get('*', function(req, res, next) 
    // call all routes and return the index.html file here

注意:我通常使用这样的东西作为最终的全部,但是我还包括其他路由,例如请求静态文件、网络爬虫和任何其他需要处理的特定路由。

【讨论】:

这基本上是解决方案。我想补充一件事:如果您的后端也提供动态数据,例如一个 API,你想把这个 API 和这个重定向隔离开,所以只有当一个匹配请求的文件不存在并且没有其他 API 路由也匹配时,你才需要添加这个规则。 好点。更新了我的答案。不过,对于 API,我更喜欢从单独的 url/域运行它们。 这里有一个很好的 NodeJS 服务 SPA 示例:***.com/questions/20396900/…【参考方案2】:

你需要在服务器端设置url重写

对于 apache,它会是这样的:

RewriteEngine On
RewriteBase /
RewriteCond %REQUEST_FILENAME !-f
RewriteCond %REQUEST_FILENAME !-d
RewriteRule . index.html

.htaccess 文件中(确保mod_rewrite apache 模块已启用)

然后像这样为 nginx 配置:

location / 
    ....
    try_files $uri $uri/ /index.html =404;

【讨论】:

你如何在 IIS 中做同样的事情? 不确定,从来没有在 IIS 中这样做过。但这个答案看起来是对的:***.com/questions/12614072/…【参考方案3】:

注意到一条关于 IIS 的评论——这里还没有看到——但这就是我的工作方式。确保安装了 URL Rewrite 2.0。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<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>
</configuration>`

【讨论】:

【参考方案4】:

首先制作 .htaccess 文件,然后复制这些代码

<ifModule mod_rewrite.c>
     RewriteEngine on

    # Don't rewrite files or directories
    RewriteCond %REQUEST_FILENAME -f [OR]
    RewriteCond %REQUEST_FILENAME -d
    RewriteRule ^ - [L]


    RewriteRule ^ your root folder/index.html [L]
      </ifModule>


<base url="/"></base> in index.html file

【讨论】:

【参考方案5】:

无法详细解释为什么会发生这种情况,但我可以向您描述我解决此问题的方法。所以我将 UrlRewriterFilter 用于我的 SpringMvc 后端。 Angular 通用模块配置:

var app = angular.module('ilonaChatGeneralModule', 
    [
        'ui.router'
    ]);

app.config(function($locationProvider)
    $locationProvider.html5Mode(true);
);

app.config(function($stateProvider, $urlRouterProvider) 

    $urlRouterProvider.otherwise('/');

    var home = ['/', 
        url: '/',
        templateUrl: 'views/userlist.html'
    ];

    var login = ['login', 
        url: '/login',
        templateUrl: 'views/login.html'
    ];

    var privateRoom = ['privateroom', 
        url: '/privateroom',
        templateUrl: 'views/privateroom.html'
    ];

    $stateProvider
        .state(home)
        .state(login)
        .state(privateRoom)
        ;
);

我的主页,index.html:

<html ng-app="ilonaChatGeneralModule"  ng-controller="ilonaChatGeneralCtrl">
<head>
    <base href="/">
    ...

</head>
<body>
<div>
    <div ui-view=""></div>
</div>
</body>
</html>

这是前端。对于我使用 UrlRewriteFilter 的后端,您可以通过以下 maven 依赖项获取它:

  <dependency>
        <groupId>org.tuckey</groupId>
        <artifactId>urlrewritefilter</artifactId>
        <version>4.0.3</version>
    </dependency>

我已经将它添加到我的 SpringMVC WebAppInitializer 中:

    package com.sbk.config;

    import org.springframework.context.ApplicationContext;
    import org.springframework.web.context.ContextLoaderListener;
    import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
    import org.springframework.web.filter.CharacterEncodingFilter;
    import org.springframework.web.servlet.DispatcherServlet;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    import org.tuckey.web.filters.urlrewrite.UrlRewriteFilter;

    import javax.servlet.*;
    import java.nio.charset.StandardCharsets;
    import java.util.EnumSet;

    public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer    
        private static final String URL_REWRITE_FILTER_NAME = "urlRewrite";
        private static final String URL_REWRITE_FILTER_MAPPING = "/*";

...
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException 
            super.onStartup(servletContext);

            FilterRegistration.Dynamic urlReWrite = servletContext.addFilter(URL_REWRITE_FILTER_NAME, new UrlRewriteFilter());
            EnumSet<DispatcherType> urlReWriteDispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);
            urlReWrite.addMappingForUrlPatterns(urlReWriteDispatcherTypes, true, URL_REWRITE_FILTER_MAPPING);
        
    

此过滤器需要您的 WEB-INF 目录下的 urlrewrite.xml 文件(似乎在可配置但默认位置 - 此处)。该文件的内容:

<!DOCTYPE urlrewrite
        PUBLIC "-//tuckey.org//DTD UrlRewrite 2.6//EN"
        "http://www.tuckey.org/res/dtds/urlrewrite2.6.dtd">

<urlrewrite>

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

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

</urlrewrite>

我没有仔细阅读手册,但我怀疑这个想法是当您使用 http://yourhost:xxxx/privateroom 刷新您的浏览器时,您的应用程序会尝试查找物理现有视图 http://yourhost:xxxx/privateroom。但它的缺席。当您将其重定向到您的基本页面时,角度将使用其状态定义建立到物理文件的正确链接。理论上我可能会出错,但它在实践中有效

【讨论】:

【参考方案6】:

如果您无法配置您的服务器,请使用以下内容配置您的 web.xml,以在用户位于 html5 推送路径上时处理刷新。

<error-page>
    <!-- Mapping a 404 when user refreshes with an html5 push route-->
    <error-code>404</error-code>
    <location>/index.html</location>
</error-page>

【讨论】:

【参考方案7】:

对我来说最简单的方法是:

在 index.html 中使用&lt;base href="/admin/"&gt;&lt;/base&gt;

我正在为我的管理页面这样做

【讨论】:

OP的问题与base url无关。接受的答案已经很好地解决了这个问题。 但是它是如何解决我的问题的,如果你能解决我的问题,谢谢 “我的问题”是什么意思?

以上是关于使用启用 Html5 模式的 Angular Ui 路由器时页面重新加载失败的主要内容,如果未能解决你的问题,请参考以下文章

AngularJS:无法使用 ui-route $state 获取 html5 模式网址

angular2 学习笔记 ( Router 路由 )

带有 Html5Mode 的 Angular-UI-Router 刷新页面问题

两个带有 angular-ui/ui-sortable 的连接列表,在一个中禁用占位符,同时在另一个中启用它

使用 AngularJS 和外部 NodeJS 服务器启用 html5 模式

在页面刷新时使用 Angular JS ui.router html5Mode(true) 配置 Amazon S3 静态站点