AngularJS html5Mode在IE中回退。 Express服务器需要知道路由

时间:2014-08-12 12:56:18

标签: angularjs internet-explorer angularjs-routing hashbang

我在AngularJS路由中使用html5Mode = true。工作良好。当我使用IE访问该站点时,Angular路由回退到Hashbang URI,如http://example.com/#!/route-name。那一切都很好。除了在Express中我需要知道路由,因为它告诉我从Express提供哪个html文件。 URL的#部分不会发送到服务器。

我的问题是,如何让我的服务器知道在IE中使用hashbang路由请求哪条AngularJS路由?我正在考虑配置Angular以某种方式将路由作为http标头发送,我可以在快递中阅读,但我不知道该怎么做。有什么想法吗?

更新:根据我收到的反馈,让我告诉您该网站有模板。一个用于主页,一个用于所有其他页面。它们都非常不同。根据路线,服务器需要知道何时为主页提供文件以及何时为其他页面提供文件。

3 个答案:

答案 0 :(得分:1)

只是要清楚:这不会在主页面请求上发送hashbang。基于HTTP specifications,无法做到这一点。

可以在后续的AJAX请求中发送它,但这并不能真正帮助你解决这个问题。除了不支持不支持html5mode的浏览器之外,没有其他解决方案。

浏览器不会自动将hashbang发送到服务器,所以是的,您需要手动发送。使用标题是一个不错的选择。

查看docs中的$httpProvider.interceptors

你可以为$http注册一个请求拦截器(AngularJS在内部使用它来处理任何AJAX请求,所以你应该这样做)附加一个自定义头。然后,您可以使用$location.path()将该标头值设置为实际的hashbang:

var app = angular.module('MyApp',[]);

app.factory('httpInterceptor', function($q, $location) {
    return {
        'request': function(config) {
            if(config.url.indexOf('product')) {
                config.headers.Hashbang = $location.path();
            }
            return config;
        }
    };
});

app.config(function($httpProvider) {
    $httpProvider.interceptors.push('httpInterceptor');
});

app.controller('Ctrl', function($scope, $http) {
    $http({method: 'GET', url: '/echo/json/'}).
    success(function(data, status, headers, config) {
        console.log(data);
    });
});

这是the fiddle。你可以test it like so。查看开发人员工具中发送的标题。

请注意,我没有使用真正的hashbang(#!)进行测试,而只是一个哈希,因为小提琴不允许它。

答案 1 :(得分:1)

我遇到了类似的问题,在重定向删除之前我需要URL的参数。由于URL是从原始URL重定向的,因此先前的URL位于referer标头中。

所以我使用这个快速3.x中间件来重写从引用者到查询对根URL的请求的原始参数:

var _ = require('lodash');
var url = require('url');
var qs = require('querystring');

app.use(function(req, res, next) {    
    if (req.url === '/' && req.headers && req.headers.referer) {
        var referer = url.parse(req.headers.referer);

        // rewrite original parameters
        _.forEach(qs.parse(referer.query), function(value, key) {
            req.query[key] = value;
        });

        // do something with pathname
        console.log(referer.pathname);
    }
});

您可以针对路径执行相同的操作,即referer.pathname

答案 2 :(得分:0)

根据Sergiu Paraschiv的回答,似乎没有客户端的答案。所以我调查了服务器端解决方案,客户端只需要一件事。确保你总是像在链接到/ xxxxx而不是/#!/ xxxxx的html5Mode中那样进行链接。

然后在html5Mode and IE9 and lower,会发生什么是AngularJS将所有/ xxxxx重定向到/#!/ xxxxx。在Express中,这会生成url /和referer / xxxxx。这是你可以很容易检查的东西。

如果你想迎合IE8及更低版本,那么很遗憾Angular在这个秋季回归场景中使用window.location.href重定向到/#!/ xxxxx。见github / angular.js / src / ng / browser.js第169行。 使用window.location.href会导致IE8降低到not send引用。

这是一个服务器端(Express 3.x)解决方案,它可以从引用值或IE8及更低版本的previousUrl会话变量中解析Angular hashbang路由。

app.use(function(req, res, next) {
  if (req.url.match(/\/api/))
    return next(); // /api/something should proceed to next in middleware
  console.log(req.url, req.headers)
  //IE does not support html5Mode so /xxxxx is redirected to /#!/xxxxx
  //effectively making the url / and the referer /xxxxx
  //I check for that here and if xxxxx is not home I present page.html

  if (req.headers['user-agent'] &&
    req.headers['user-agent'].match(/MSIE ([7-8]{1}[.0-9]*)/) &&
    req.url.match(/^\/[\w-]+$/)
  ) {
    //However IE8 does not report the referer when doing a redirect using window.locarion.href
    //so I store the initially requested url /xxxxx in session...
    req.session.previousUrl = req.url;
    //console.log('IE8 saving the url here', req.session.previousUrl);
  }
  if (req.headers['user-agent'] &&
    req.headers['user-agent'].match(/MSIE ([7-8]{1}[.0-9]*)/) &&
    !req.headers['referer'] &&
    req.url === '/' &&
    req.session.previousUrl &&
    req.session.previousUrl !== '/home'
  ) {
    //continuation on the IE* referer story (aka the next redirected request)...
    //... and checked for the url stored in session here ;)
    return res.sendfile('public\\page.html'); 
  }
  if (req.headers['user-agent'] &&
    req.headers['user-agent'].match(/MSIE ([7-9]{1}[.0-9]*)/) &&
    req.url === '/' &&
    req.headers['referer'] &&
    req.headers['referer'].match(/^https?:\/\/(?:www)?\.example\.com\/(?!home$)([\w-]+)$/)
  ) {
    return res.sendfile('public\\page.html'); 
  }
  if (req.url.match(/\/home|\/$/))
    return res.sendfile('public\\home.html'); //for the home pages we're going to use home.html

  res.sendfile('public\\page.html'); //for everything else we're going to use page.html
});

我确信有这种情况会失败,但它在我的测试中对我有用,如果它失败了,那只会失败的IE9浏览器(按照正则表达式)。