Reputation: 11137
I'm using html5Mode=true with AngularJS routing. Works fine. When I access the site with IE, Angular routing falls back to Hashbang URI's like http://example.com/#!/route-name. That's all fine. Except in Express I need to know the route, because it tells me which html file to serve from Express. The # part of the url is not sent to the server.
My question is, how can I let my server know which AngularJS route is being requested with hashbang routing in IE? I was thinking of configuring Angular somehow to send the route as an http header that I can read in express, but I don't know how to do that. Any ideas?
Update: Based on feedback I got, let me tell you that the site has templates. One for the homepage and one for all the other pages. They are both pretty different. Based on the route, the server needs to know when to serve the file for the homepage and when to serve the file for the other pages.
Upvotes: 1
Views: 3250
Reputation: 5881
I had a similar problem where I needed the parameters of the URL before they were removed by the redirect. As the URL is redirected in angular from the original one the previous URL is in the referer header.
So I used this express 3.x middleware to rewrite the original parameters from the referer to the query on requests to the root 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);
}
});
You could do the same in your case for the path, that is in referer.pathname
.
Upvotes: 1
Reputation: 11137
There seems no client-side answer as per the answer of Sergiu Paraschiv. So I have investigated a server-side solution that requires only one thing on the client. Make sure you always links as you would do in html5Mode which is linking to /xxxxx and not /#!/xxxxx.
Then In html5Mode and IE9 and lower what happens is that AngularJS redirects everything /xxxxx to /#!/xxxxx. In Express this makes the url / and the referer /xxxxx. This is something that you can check for quite easily.
If you want to cater for IE8 and lower to, it's unfortunate that Angular uses window.location.href in this fall back scenario redirecting to /#!/xxxxx. See github / angular.js / src / ng / browser.js line 169. Using window.location.href causes IE8 and lower to not send the referer.
Here's a server-side (Express 3.x) solution that resolves the Angular hashbang route from the referer value or a previousUrl session variable for IE8 and lower.
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
});
I'm sure that there are scenario's where this fails, but it worked for me in my tests and if it fails, that will only fail for IE9- browsers (as per the regexp's).
Upvotes: 0
Reputation: 10153
Just to be clear: this won't send the hashbang on the main page request. Based on the HTTP specifications there is no way of doing that.
You can send it on subsequent AJAX requests, but this won't really help you with solving this problem. There is no solution other than not supporting browsers that don't support html5mode.
Browsers won't send the hashbang to the server automatically, so yes you will need to send it manually. Using a header is a good option.
Take a look at $httpProvider.interceptors
in the docs.
You can register a request interceptor for $http
(AngularJS uses it internally for any AJAX request and so should you) that attaches a custom header. You can then set that header value to be the actual hashbang using $location.path()
:
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);
});
});
Here's the fiddle. You can test it like so. Check out the sent headers in developer tools.
Notice that I'm not testing with a true hashbang (#!
) but just a hash because the fiddle does not allow it.
Upvotes: 1