$location / 在HTML5和Hashbang模式之间切换/链接重写

180

我原本认为Angular会重写出现在模板中锚标签的href属性中的URL,以使它们在html5模式或哈希模式下都能工作。然而,位置服务文档似乎表明HTML链接重写已经处理了哈希情况。因此,我期望在非HTML5模式下会插入哈希,在HTML5模式下则不会。

然而,似乎没有重写正在发生。下面的示例不允许我只更改模式。应用程序中的所有链接都需要手动重写(或者在运行时从变量派生)。我必须根据模式手动重新编写所有URL吗?

我在Angular 1.0.6、1.1.4或1.1.3中没有看到任何客户端URL重写。似乎所有href值都需要以#/开头,以供哈希模式使用,/用于html5模式。

是否需要某些配置才能进行重写?我是否误读了文档?还是做错了什么傻事?

这里有一个小例子:

<head>
    <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.3/angular.js"></script>
</head>

<body>
    <div ng-view></div>
    <script>
        angular.module('sample', [])
            .config(
        ['$routeProvider', '$locationProvider',
            function ($routeProvider, $locationProvider) {

                //commenting out this line (switching to hashbang mode) breaks the app
                //-- unless # is added to the templates
                $locationProvider.html5Mode(true);

                $routeProvider.when('/', {
                    template: 'this is home. go to <a href="/about"/>about</a>'
                });
                $routeProvider.when('/about', {
                    template: 'this is about. go to <a href="/"/>home</a'
                });
            }
        ])
            .run();
    </script>
</body>

附言:重新阅读我的问题后,我发现我在使用“重写”一词时并没有特别明确地指出谁何时需要进行重写。这个问题是关于如何让Angular在呈现路径时重写URL并如何使其统一解释JS代码中的路径,而不是关于如何使Web服务器对请求进行HTML5兼容的重写。


1
这里是Angular 1.6的解决方案。(链接) - Mistalis
4个回答

360

关于AngularJS路由,文档不是很清楚。它谈到了Hashbang和HTML5模式。实际上,AngularJS路由有三种模式:

  • Hashbang模式
  • HTML5模式
  • HTML5中的Hashbang模式

对于每种模式,都有相应的LocationUrl类(LocationHashbangUrl、LocationUrl和LocationHashbangInHTML5Url)。

为了模拟URL重写,您必须将html5mode设置为true,并将$sniffer类装饰如下:

$provide.decorator('$sniffer', function($delegate) {
  $delegate.history = false;
  return $delegate;
});

现在我将更详细地解释:

Hashbang 模式

配置:

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

当您需要在HTML文件中使用带有哈希标识的URL时,就会出现这种情况,例如:

<a href="index.html#!/path">link</a>

在浏览器中您必须使用以下链接:http://www.example.com/base/index.html#!/base/path

正如您所看到的,在纯Hashbang模式下,HTML文件中的所有链接都必须以基本路径开头,例如"index.html#!"。

HTML5模式

配置:

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

你应该在HTML文件中设置基本路径

<html>
  <head>
    <base href="/">
  </head>
</html>

在此模式下,您可以在HTML文件中使用不带#的链接

<a href="/path">link</a>

在浏览器中的链接:

http://www.example.com/base/path

HTML5模式下的Hashbang

当我们在不兼容的浏览器中使用HTML5模式时,将会激活此模式。我们可以通过装饰$sniffer服务并将历史记录设置为false来在兼容的浏览器中模拟此模式。

配置:

$provide.decorator('$sniffer', function($delegate) {
  $delegate.history = false;
  return $delegate;
});
$routeProvider
  .when('/path', {
    templateUrl: 'path.html',
  });
$locationProvider
  .html5Mode(true)
  .hashPrefix('!');

在HTML文件中设置基础:

<html>
  <head>
    <base href="/">
  </head>
</html>
在这种情况下,链接也可以在HTML文件中不带井号编写。
<a href="/path">link</a>

浏览器中的链接:

http://www.example.com/index.html#!/base/path

1
我认为我没有正确理解你的问题。为什么不使用没有哈希的URL呢?它们将在支持历史记录API和不支持历史记录API的浏览器中工作。当您在不支持历史记录API的浏览器中单击它们时,AngularJS会将#版本放入位置栏,因为AngularJS拦截链接上的点击。 - jupiter
1
我得到了“$provide未定义”? - Petrus Theron
@user271996,我认为他之所以说index.html#!/path是因为如果你不加它而转到联系页面,它会重新路由为contact#!/contact而不是index.html#!/contact,这至少看起来像你从未离开过索引页面,而另一个则显得令人困惑。 - Zuriel
1
@pate -- 当你设置装饰器时,需要在配置函数中注入 $provide。 - laurelnaiad
你好,我已经在我的应用程序中实现了HTML5模式下的Hashbang,问题是当我尝试直接访问像这样的URL时: http://app.localhost/catalog/pvc $routeProvider.when('/catalog/:category', {templateUrl: 'views/catalog.html', controller: 'CatalogController' }); 请帮帮我。 其他路由都很好,但参数不起作用。 谢谢。 - schwertfisch
显示剩余10条评论

8

对于未来的读者,如果您正在使用Angular 1.6,您还需要更改hashPrefix

appModule.config(['$locationProvider', function($locationProvider) {
    $locationProvider.html5Mode(true);
    $locationProvider.hashPrefix('');
}]);

不要忘记在你的HTML <head> 中设置基础路径:
<head>
    <base href="/">
    ...
</head>

关于更新日志的更多信息,请点击这里


3
谢谢,@Mistalis。您的答案很好用。但是,在路由后刷新页面时出现了“页面未找到”的错误。请帮我解决。 - Ashish Mehta
@AshishMehta 你好,Ashish。我建议你阅读这个答案,它可能会解决你的问题。再见! :) - Mistalis

0

我花了一些时间才弄明白,这是我让它正常工作的方法 - Angular WebAPI ASP路由不带#以便于SEO。

  1. 添加到Index.html - base href="/">
  2. 在app.config中添加$locationProvider.html5Mode(true);

  3. 我需要忽略某个控制器(它在home控制器中),以便不上传图像,因此我将该规则添加到RouteConfig

         routes.MapRoute(
            name: "Default2",
            url: "Home/{*.}",
            defaults: new { controller = "Home", action = "SaveImage" }
        );
    
  4. 在Global.asax中添加以下内容 - 确保忽略api和图像上传路径,让它们正常运行,否则重定向其他所有内容。

     private const string ROOT_DOCUMENT = "/Index.html";
    
    protected void Application_BeginRequest(Object sender, EventArgs e)
    {
        var path = Request.Url.AbsolutePath;
        var isApi = path.StartsWith("/api", StringComparison.InvariantCultureIgnoreCase);
        var isImageUpload = path.StartsWith("/home", StringComparison.InvariantCultureIgnoreCase);
    
        if (isApi || isImageUpload)
            return;
    
        string url = Request.Url.LocalPath;
        if (!System.IO.File.Exists(Context.Server.MapPath(url)))
            Context.RewritePath(ROOT_DOCUMENT);
    }
    
  5. 确保使用$location.url('/XXX')而不是window.location...进行重定向

  6. 使用绝对路径引用CSS文件

而不是

<link href="app/content/bootstrapwc.css" rel="stylesheet" />

最后的注释 - 这样做让我完全掌控,而且我不需要对Web配置文件进行任何操作。

希望这可以帮助到你,因为我花了一段时间才弄清楚。


0

我想要通过HTML5模式和固定令牌访问我的应用程序,然后切换到哈希方法(以保持令牌,使用户可以刷新页面)。

访问我的应用程序的URL:

http://myapp.com/amazing_url?token=super_token

然后当用户加载页面时:

http://myapp.com/amazing_url?token=super_token#/amazing_url

当用户导航时:

http://myapp.com/amazing_url?token=super_token#/another_url

通过这种方式,我将令牌保留在URL中,并在用户浏览时保持状态。我失去了一些URL的可见性,但没有完美的方式可以解决。

因此,请不要启用HTML5模式,然后添加此控制器:

.config ($stateProvider)->
    $stateProvider.state('home-loading', {
         url: '/',
         controller: 'homeController'
    })
.controller 'homeController', ($state, $location)->
    if window.location.pathname != '/'
        $location.url(window.location.pathname+window.location.search).replace()
    else
        $state.go('home', {}, { location: 'replace' })

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接