$location / html5 和 hashbang 模式切换 / 链接重写
Posted
技术标签:
【中文标题】$location / html5 和 hashbang 模式切换 / 链接重写【英文标题】:$location / switching between html5 and hashbang mode / link rewriting 【发布时间】:2013-05-16 15:56:41 【问题描述】:我的印象是 Angular 会重写出现在模板中锚标记的 href 属性中的 URL,这样它们就可以在 html5 模式或 hashbang 模式下工作。 documentation for the location service 似乎说 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="/about"/>about</a>'
);
$routeProvider.when('/about',
template: 'this is about. go to <a href="/"/>home</a'
);
])
.run();
</script>
</body>
附录:在重新阅读我的问题时,我发现我使用了“重写”这个词,但并没有很清楚地说明我想要重写的对象和时间。问题是关于如何让 Angular 在呈现路径时重写 URL,以及如何让它在两种模式下统一解释 JS 代码中的路径。 不是关于如何使 Web 服务器对请求进行 HTML5 兼容的重写。
【问题讨论】:
这里是 Angular 1.6 的 the solution。 【参考方案1】:文档对 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 服务并将 history 设置为 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,具体取决于模式! 我认为我没有正确理解您的问题。为什么不只使用没有哈希的 URL?它们将在支持历史 API 的浏览器和不支持历史 API 的浏览器中工作。当您在不支持历史 API 的浏览器中单击它们时,AngularJS 会将 # 版本放入位置栏中,因为 AngularJS 会拦截对链接的点击。 我正在开发一个应该支持这两种模式的框架。应用程序作者应该能够选择其中一个,而不用担心他们的模板中是否有散列和/或相对路径的解释是否有变化。我希望您的技巧将有助于实现“您所做的只是更改模式”(即使实际的解决方案是“将模式设置为 html5,然后对浏览器功能撒谎”)。 我收到$provide
未定义?
@pate -- 在设置装饰器时,您需要在配置函数中注入 $provide。【参考方案2】:
我希望能够使用 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' )
【讨论】:
【参考方案3】:我花了一段时间才弄明白,所以这就是我的工作方式 - Angular WebAPI ASP Routing without the # for SEO
-
添加到 Index.html - base href="/">
添加 $locationProvider.html5Mode(true);到 app.config
我需要某个控制器(在家庭控制器中)被忽略以上传图像,所以我将该规则添加到 RouteConfig
routes.MapRoute(
name: "Default2",
url: "Home/*.",
defaults: new controller = "Home", action = "SaveImage"
);
在 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);
确保使用 $location.url('/XXX') 而不是 window.location ... 来重定向
使用绝对路径引用 CSS 文件
而不是
<link href="app/content/bootstrapwc.css" rel="stylesheet" />
最后一点 - 这样做让我可以完全控制,我不需要对网络配置做任何事情。
希望这会有所帮助,因为我花了一段时间才弄清楚。
【讨论】:
【参考方案4】:未来的读者,如果您使用的是Angular 1.6,您还需要更改hashPrefix
:
appModule.config(['$locationProvider', function($locationProvider)
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix('');
]);
别忘了在你的 HTML 中设置基础<head>
:
<head>
<base href="/">
...
</head>
关于更新日志的更多信息 here。
【讨论】:
感谢@Mistalis。你的答案工作正常。但是当我在路由后刷新页面时出现问题,页面未找到错误。请帮帮我.. @AshishMehta 你好,Ashish。我建议你阅读this answer,它可能会解决你的问题。再见! :)以上是关于$location / html5 和 hashbang 模式切换 / 链接重写的主要内容,如果未能解决你的问题,请参考以下文章