Резервный вариант AngularJS html5Mode в IE. Экспресс-сервер должен знать маршрут

Я использую html5Mode=true с маршрутизацией AngularJS. Работает отлично. Когда я захожу на сайт через IE, маршрутизация Angular возвращается к URI Hashbang, например http://example.com/#!/route-name. Все в порядке. За исключением Express, мне нужно знать маршрут, потому что он говорит мне, какой html-файл обслуживать из Express. Часть # URL-адреса не отправляется на сервер.

Мой вопрос: как я могу сообщить моему серверу, какой маршрут AngularJS запрашивается с маршрутизацией hashbang в IE? Я думал о том, чтобы каким-то образом настроить Angular для отправки маршрута в виде http-заголовка, который я могу прочитать в экспресс-режиме, но я не знаю, как это сделать. Любые идеи?

Обновление. Основываясь на полученных отзывах, позвольте мне сообщить вам, что на сайте есть шаблоны. Один для главной страницы и один для всех остальных страниц. Они оба довольно разные. Основываясь на маршруте, сервер должен знать, когда обслуживать файл для домашней страницы и когда обслуживать файл для других страниц.


person Christiaan Westerbeek    schedule 12.08.2014    source источник
comment
Разве вы не используете $routeProvider для обработки маршрутизации?   -  person Rahil Wazir    schedule 12.08.2014
comment
@RahilWazir Да, я.   -  person Christiaan Westerbeek    schedule 12.08.2014


Ответы (3)


Просто для ясности: это не приведет к отправке хэш-банга в запросе главной страницы. Согласно спецификации HTTP сделать это невозможно.

Вы можете отправить его в последующих запросах AJAX, но это не поможет вам решить эту проблему. Нет другого решения, кроме как не поддерживать браузеры, которые не поддерживают html5mode.

Браузеры не будут отправлять хэш-банг на сервер автоматически, поэтому да, вам нужно будет отправить его вручную. Использование заголовка — хороший вариант.

Взгляните на $httpProvider.interceptors в документах. .

Вы можете зарегистрировать перехватчик запросов для $http (AngularJS использует его внутри для любого запроса AJAX, и вы должны использовать его), который прикрепляет пользовательский заголовок. Затем вы можете установить это значение заголовка как фактический хэш-банг, используя $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);
    });
});

Вот скрипка. Вы можете протестировать это так. Проверьте отправленные заголовки в инструментах разработчика.

Обратите внимание, что я тестирую не настоящий хеш-банг (#!), а просто хеш, потому что скрипка этого не позволяет.

person Sergiu Paraschiv    schedule 12.08.2014
comment
Это выглядит довольно полезно. Это действительно добавляет заголовок к запросу AJAX только правильно? Мне нужно, чтобы заголовок был добавлен к фактическому запросу страницы. То, что я так написал, заставляет меня думать, что это даже невозможно. Другим решением было бы выполнить угловую маршрутизацию с запросом вместо хэша? Итак, example.com/?route=route-name - person Christiaan Westerbeek; 12.08.2014
comment
Нет, извините, вы не сможете отправить его в первоначальном запросе. Это запрос, который отправляет браузер, и спецификации HTTP говорят, что он не должен отправлять хэш. Я знаю, что мой ответ, вероятно, не поможет вам в этом отношении. Это одна из причин, по которой AngularJS не поддерживает IE7 и старше, а v1.3 также прекратит поддержку IE8. - person Sergiu Paraschiv; 12.08.2014
comment
есть ли другой способ маршрутизации с Angular, например, с запросом вместо хеша? Например: example.com/?route=route-name - person Christiaan Westerbeek; 12.08.2014
comment
Нет, это тоже не сработает. В любом браузере изменение URL-адреса (что перед хэш-бангом или что-то еще, если вы не используете хэш-банг) ​​вызывает запрос GET на сервер с новым URL-адресом. Режим HTML5 в основном позволяет вам изменить URL-адрес, не запуская автоматический GET. По этой причине режим html5 в AngularJS работает только в более новых браузерах, которые реализуют этот API HTML5. - person Sergiu Paraschiv; 12.08.2014
comment
Спасибо за помощь. Нет - это тоже ответ, и вы были очень тщательны. Все варианты на стороне клиента кажутся здесь исчерпанными. Я реализовал что-то на стороне сервера, используя тот факт, что когда html5Mode не работает в IE, Angular перенаправляет все /xxxxx в /#!/xxxxx. В экспрессе это делает URL-адрес / и реферер /xxxxx. Это то, что я могу проверить и с чем работать. - person Christiaan Westerbeek; 12.08.2014
comment
Ого, хорошая находка! Раньше не думал о заголовке referer. Попробуйте и напишите свое решение в ответе, я уверен, что есть люди, заинтересованные в этом. - person Sergiu Paraschiv; 13.08.2014

У меня была аналогичная проблема, когда мне нужны были параметры URL-адреса, прежде чем они были удалены перенаправлением. Поскольку URL-адрес перенаправляется в angular из исходного, предыдущий URL-адрес находится в заголовке реферера.

Поэтому я использовал это промежуточное программное обеспечение Express 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.

person crackmigg    schedule 10.03.2015

Похоже, что ответа на стороне клиента нет в соответствии с ответом Серджиу Параскива. Поэтому я исследовал решение на стороне сервера, которое требует от клиента только одну вещь. Убедитесь, что вы всегда ссылаетесь, как в html5Mode, который ссылается на /xxxxx, а не на /#!/xxxxx.

Затем в html5Mode и IE9 и ниже что случается так, что AngularJS перенаправляет все /xxxxx на /#!/xxxxx. В Express это делает URL-адрес / и реферер /xxxxx. Это то, что вы можете проверить довольно легко.

Если вы хотите обслуживать IE8 и более ранние версии, к сожалению, Angular использует window.location.href в этом резервном сценарии с перенаправлением на /#!/xxxxx. См. github/angular.js/src/ng/browser. .js, строка 169. Использование window.location.href приводит к тому, что IE8 и ниже переходят в нет отправить реферера.

Вот решение на стороне сервера (Express 3.x), которое разрешает маршрут Angular hashbang из значения реферера или переменной сеанса previousUrl для IE8 и ниже.

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 (согласно регулярному выражению).

person Christiaan Westerbeek    schedule 13.08.2014