$ location /在html5和hashbang模式之间切换/链接重写


179

我给人的印象是Angular会重写出现在临时模板中定位标记的href属性中的URL,以便无论在html5模式还是hashbang模式下它们都可以工作。定位服务文档似乎说HTML链接重写可以解决hashbang的问题。因此,我希望当不在HTML5模式下时,将插入哈希,而在HTML5模式下,则不会。

但是,似乎没有进行任何重写。以下示例不允许我仅更改模式。应用程序中的所有链接都需要手工重写(或在运行时从变量派生。我是否需要根据模式手动重写所有URL?

我没有看到在Angular 1.0.6、1.1.4或1.1.3中进行任何客户端url重写。似乎所有的href值都必须在#/之前添加为hashbang模式,在//之前添加为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="https://stackoverflow.com/about"/>about</a>'
                });
                $routeProvider.when('/about', {
                    template: 'this is about. go to <a href="https://stackoverflow.com/"/>home</a'
                });
            }
        ])
            .run();
    </script>
</body>

附录:在重读我的问题时,我发现我使用了“重写”一词,但对于谁和何时进行重写却没有足够的明确性。问题在于如何使Angular在呈现路径时重写URL,以及如何使Angular在两种模式下统一解释JS代码中的路径。这不是关于如何使Web服务器做HTML5兼容的要求重写。


1
下面是该解决方案角度1.6
Mistalis

Answers:


361

该文档对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文件(例如,

<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

感谢您详细的解释,@ jupiter。我将研究是否可以跳过爆炸,保持散列并欺骗Angular,使其根据模式而不要求两组URL!
laurelnaiad

1
我想我没有正确理解您的问题。为什么不只使用没有哈希的URL?它们将在支持历史记录API的浏览器和不支持历史记录API的浏览器中工作。当您在不支持历史API的浏览器中单击AngularJS时,会将#版本放入位置栏中,因为AngularJS会拦截对链接的点击。
木星

我正在一个应该同时支持两种模式的框架上工作。应用程序作者应该能够选择一个或另一个,而不必担心模板中是否存在哈希值和/或相对路径的解释是否发生变化。我希望您的技巧可以使“所做的只是改变模式”成为现实(即使实际的解决方案是“将模式设置为html5,然后对浏览器功能进行调整”)。
laurelnaiad 2013年

1
我得到的$provide是不确定的?
Petrus Theron

1
@pate-设置装饰器时,需要在配置函数中注入$ provide。
laurelnaiad 2013年

8

以后的读者,如果您使用的是Angular 1.6,则还需要更改hashPrefix

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

不要忘记在HTML中设置基础<head>

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

有关变更日志的更多信息here


3
谢谢@Mistalis。你的答案很好。但是在路由后刷新页面时出现问题,找不到页面错误。请帮助我..
Ashish Mehta

@AshishMehta你好Ashish。我建议您阅读此答案,它可能会解决您的问题。再见!:)
Mistalis

0

这花了我一段时间,所以这就是我的工作方式-Angular WebAPI ASP路由,没有用于SEO的#

  1. 添加到Index.html-基本href =“ /”>
  2. 添加$ locationProvider.html5Mode(true); 到app.config

  3. 我需要一个特定的控制器(位于家庭控制器中)来上载图像而被忽略,因此我将该规则添加到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模式和固定令牌访问我的应用程序,然后切换到hashbang方法(保留令牌以便用户可以刷新其页面)。

访问我的应用程序的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' })
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.