Перезагрузка страницы дает неверный запрос GET в режиме AngularJS HTML5


193

Я хочу включить режим HTML5 для моего приложения. Я поставил следующий код для конфигурации, как показано здесь :

return app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {

    $locationProvider.html5Mode(true);
    $locationProvider.hashPrefix = '!';

    $routeProvider.when('/', {
        templateUrl: '/views/index.html',
        controller: 'indexCtrl'
    });
    $routeProvider.when('/about',{
        templateUrl: '/views/about.html',
        controller: 'AboutCtrl'
    });

Как вы можете видеть, я использовал $locationProvider.html5modeи изменил все свои ссылки в, ng-hrefчтобы исключить /#/.

Эта проблема

На данный момент я могу перейти на localhost:9000/страницу индекса и перейти к другим страницам, как localhost:9000/about.

Однако проблема возникает, когда я обновляю localhost:9000/aboutстраницу. Я получаю следующий вывод:Cannot GET /about

Если я смотрю на сетевые звонки:

Request URL:localhost:9000/about
Request Method:GET

Хотя, если я сначала зайду, localhost:9000/а затем нажму на кнопку, которая перейдет к, /aboutя получу:

Request URL:http://localhost:9000/views/about.html

Который прекрасно отображает страницу.

Как я могу включить Angular для получения правильной страницы при обновлении?



1
Я решаю это для себя. Смотрите здесь stackoverflow.com/questions/22394014/…
Саша Б.

Ответы:


99

Из угловых документов

На стороне сервера Для
использования этого режима требуется перезапись URL на стороне сервера, в основном вам нужно переписать все ваши ссылки на точку входа вашего приложения (например, index.html).

Причина этого заключается в том, что при первом посещении страницы ( /about), например, после обновления, браузер не может узнать, что это не настоящий URL, поэтому он загружается и загружается. Однако, если вы сначала загрузили корневую страницу и весь код javascript, то при переходе на /aboutAngular можно попасть туда, прежде чем браузер попытается подключиться к серверу и обработать его соответствующим образом.


21
Когда это говорит «вы должны переписать все ваши ссылки на точку входа вашего приложения (например, index.html)», что это значит? Означает ли это, что я должен войти в свой $routeProviderи изменить пути каждого, templateUrlнапример, с templateUrl : '/views/about.html',на templateUrl : 'example.com/views/about.html',?

7
Нет, ваши правила в $ routeProvider должны оставаться такими, как они есть. Ответ относится к «серверной части». Это относится к изменению маршрутизации на сервере, который предоставляет ваши угловые html-страницы. Прежде чем каждый запрос попадает в ваше угловое приложение, он должен сначала пройти через серверную маршрутизацию, которая должна переписать все запросы, чтобы указывать на вашу угловую точку входа в приложение (например, «index.html» или «/», в зависимости от того, как ваш угловой маршруты работают).
Марни

В случае, если я подам свой минифицированный index.html через CDN, как будет работать этот механизм маршрутизации?
CrazyGeek


5
Хорошая статья для apache, есть пример htaccess: ngmilk.rocks/2015/03/09/…
Alcalyn

63

Есть несколько вещей, которые нужно настроить, чтобы ваша ссылка в браузере была похожа, http://yourdomain.com/pathи это ваша угловая конфигурация + сторона сервера

1) AngularJS

$routeProvider
  .when('/path', {
    templateUrl: 'path.html',
  });
$locationProvider
  .html5Mode(true);

2) на стороне сервера, просто поместите .htaccessв свою корневую папку и вставьте эту

RewriteEngine On 
Options FollowSymLinks

RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /#/$1 [L]

Более интересные материалы для чтения о режиме html5 в angularjs и конфигурации, требуемой для разных сред https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-configure-your-server -to-work-with-html5mode Также этот вопрос может помочь вам $ location / переключения между html5 и режимом hashbang / переписыванием ссылок


Здравствуй. Я попробовал вышеуказанное решение, внес изменения в мой конфигурационный файл. Но все равно это не работает. Обновление URL по-прежнему дает ошибку «404 НЕ НАЙДЕНО».
2016 года

Как перенаправить / переписать в приложении, развернутом в Websphere?
Гэри

Я не понимаю, почему нет!
lokers

36

У меня была похожая проблема, и я решил ее:

  • Использование <base href="https://stackoverflow.com/index.html">на странице индекса

  • Используя промежуточное ПО перехвата всех маршрутов на моем узле / сервере Express следующим образом (поместите его после маршрутизатора):

app.use(function(req, res) {
    res.sendfile(__dirname + '/Public/index.html');
});

Я думаю, что это должно заставить вас работать.

Если вы используете сервер Apache, вы можете захотеть изменить ваши ссылки mod_rewrite. Это не сложно сделать. Всего несколько изменений в конфигурационных файлах.

Все, что предполагает, что html5mode включен на angularjs. Сейчас. обратите внимание, что в угловых 1.2 объявление базового URL больше не рекомендуется на самом деле.


1
Это решение работало для меня с настройкой: <base href="https://stackoverflow.com/">и добавлением предложенной вами экспресс-маршрутизации. Большое спасибо.
Гилад Пелег

Тахир Чад, вам все еще нужно было использовать переписывание URL-адреса на сервере после реализации <base href="https://stackoverflow.com/index.html">?
Коди

Да, вы можете видеть переписывание URL как способ установить базовый URL вашего приложения / веб-сайта в камне (включая домен). HTML-оператор 'base url' - это просто способ убедиться, что все относительные ссылки правильно получены из одной и той же базы. Базовый URL не является абсолютно необходимым, если ваши ссылки являются абсолютными.
Тай

1
Когда я использую это, я просто вижу HTML-код на странице, а не отображается как фактическая страница.
Ной,

2
Этот ответ заслуживает тройной звезды! Спасибо за такой простой ответ. Все остальные просто повторяют одну и ту же строку о «необходимости перенаправить сервер».
Ахмед Хак

27

Решение для BrowserSync и Gulp.

С https://github.com/BrowserSync/browser-sync/issues/204#issuecomment-102623643

Первая установка connect-history-api-fallback:

npm --save-dev install connect-history-api-fallback

Затем добавьте его в свой gulpfile.js:

var historyApiFallback = require('connect-history-api-fallback');

gulp.task('serve', function() {
  browserSync.init({
    server: {
      baseDir: "app",
      middleware: [ historyApiFallback() ]
    }
  });
});

О, хватит! Наконец облегчение от разработки Angular View! И в качестве бонуса, стандартные URL-адреса (без хэш-бэнг) тоже маршрут! Спасибо!
бит

1
Работает как шарм ... Большое спасибо!
Нишант Наир

1
Если вы используете определенный порт (т. Е. Пустой шаблон Ionic работает на порте 8100), просто добавьте дополнительное свойство после сервера, т. Е. server: { ... }, port: 8100Затем просто добавьте serveзадачу в заданную по умолчанию задачу gulp (т.е. gulp.task('default', ['sass', 'serve']);), а затем вы можете запустить приложение без Cannot GET ...ошибок браузера при обновлении страницы при запуске gulp. Обратите внимание, что при запуске сервера с ionic serveошибками все еще встречается.
Люк Шоен

Это хорошо работает, когда я использую разработку browsersync, в prod я запускаю приложение как экспресс-приложение и перенаправляю весь маршрут в угловое приложение как app.get ('*', function (req, res) {req.session.valid = true ; res.redirect ('/');}); Страница не загружается, когда дают путь напрямую?
г-н AJ

13

Вам нужно настроить свой сервер так, чтобы все переписывалось в index.html для загрузки приложения:

https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#wiki-how-to-configure-your-server-to-work-with-html5mode


1
Отлично, проблема для меня заключалась в том, чтобы использовать Laravel Forge для предоставления сервера, на котором try_files $uri $uri/ /index.php?...вместо нужной строки была указана соответствующая строка nginx index.html. Спасибо за ссылку!
CJ Томпсон

Должен быть лучший ответ, дает любую ситуацию, независимо от вашего сервера. Отлично работал с NodeJS
Джо Ллойд

10

Я написал простое промежуточное ПО для подключения, имитирующее переписывание URL-адресов в проектах grunt. https://gist.github.com/muratcorlu/5803655

Вы можете использовать так:

module.exports = function(grunt) {
  var urlRewrite = require('grunt-connect-rewrite');

  // Project configuration.
  grunt.initConfig({
    connect: {
      server: {
        options: {
          port: 9001,
          base: 'build',
          middleware: function(connect, options) {
            // Return array of whatever middlewares you want
            return [
              // redirect all urls to index.html in build folder
              urlRewrite('build', 'index.html'),

              // Serve static files.
              connect.static(options.base),

              // Make empty directories browsable.
              connect.directory(options.base)
            ];
          }
        }
      }
    }
  })
};

Существует ли ручной способ перезаписи URL в $routeProvider?

1
Я получаю urlRewriteне определенное предупреждение при попытке запустить его, и мне трудно найти правильное соединение с перезаписью, которое я могу использовать.

Показывает мне ошибку «У построения объекта нет метода initConfig. Используйте --force для продолжения».
edonbajrami

8

Если вы находитесь в стеке .NET с MVC с AngularJS, это то, что вам нужно сделать, чтобы удалить '#' из URL:

  1. Настройте ваш базовый href на странице _Layout: <head> <base href="https://stackoverflow.com/"> </head>

  2. Затем добавьте следующее в конфигурацию вашего углового приложения: $locationProvider.html5Mode(true)

  3. Выше удалит «#» из URL, но обновление страницы не будет работать, например, если вы находитесь на странице «yoursite.com/about», обновление даст вам 404. Это потому, что MVC не знает об угловой маршрутизации и по шаблону MVC это будет искать страницу MVC для 'about', которой нет в пути маршрутизации MVC. Обходной путь для этого состоит в том, чтобы отправить все запросы страницы MVC к одному представлению MVC, и вы можете сделать это, добавив маршрут, который перехватывает все URL


routes.MapRoute(
        name: "App",
        url: "{*url}",
        defaults: new { controller = "Home", action = "Index" }
    );

8

Правило перезаписи URL IIS для предотвращения ошибки 404 после обновления страницы в html5mode

Для углового запуска под IIS в Windows

<rewrite>
  <rules>
    <rule name="AngularJS" stopProcessing="true">
      <match url=".*" />
      <conditions logicalGrouping="MatchAll">
        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
      </conditions>
      <action type="Rewrite" url="/" />
    </rule>
  </rules>
</rewrite>

NodeJS / ExpressJS Маршруты для предотвращения ошибки 404 после обновления страницы в html5mode

Для углового бега под Node / Express

var express = require('express');
var path = require('path');
var router = express.Router();

// serve angular front end files from root path
router.use('/', express.static('app', { redirect: false }));

// rewrite virtual urls to angular app to enable refreshing of internal pages
router.get('*', function (req, res, next) {
    res.sendFile(path.resolve('app/index.html'));
});

module.exports = router;

Дополнительная информация: AngularJS - включить обновление страницы в режиме HTML5 без ошибок 404 в NodeJS и IIS


Спасибо, правила IIS помогли мне с приложением angular2, развернутым в Azure, которое не направлялось на дочерний маршрут.
отпечаток

Я не могу отблагодарить вас за решение для IIS (localhost в VS 2013). Ударяя голову достаточно долго от разочарования, наконец нашел этот ответ. Работал как шарм.
Флорида Г.

5

Как уже упоминали другие, вам нужно переписать маршруты на сервере и установить <base href="https://stackoverflow.com/"/>.

Для gulp-connect:

npm install connect-pushstate

var gulp = require('gulp'),
  connect = require('gulp-connect'),
  pushState = require('connect-pushstate/lib/pushstate').pushState;
...
connect.server({
  ...
  middleware: function (connect, options) {
    return [
      pushState()
    ];
  }
  ...
})
....

4

Я использую apache (xampp) в своей среде разработки и apache на производстве, добавьте:

errorDocument 404 /index.html

.htaccess решить для меня эту проблему.


4

Для Grunt и Browsersync используйте connect-modrewrite здесь

var modRewrite = require('connect-modrewrite');    


browserSync: {
            dev: {
                bsFiles: {

                    src: [
                        'app/assets/css/*.css',
                        'app/*.js',
                        'app/controllers/*.js',
                        '**/*.php',
                        '*.html',
                        'app/jade/includes/*.jade',
                        'app/views/*.html',
               ],
            },
        options: {
            watchTask: true,
            debugInfo: true,
            logConnections: true,
            server: {
                baseDir :'./',
                middleware: [
                       modRewrite(['!\.html|\.js|\.jpg|\.mp4|\.mp3|\.gif|\.svg\|.css|\.png$ /index.html [L]'])
                ]
            },

            ghostMode: {
                scroll: true,
                links: true,
                forms: true
                    }
                }
            }
        },

над ответом есть 15, которые были поясом времени. 1. npm install connect-modrewrite --save2. require in gruntfile3. скопируйте вышеуказанный серверный объект
Omar

Спасибо, это сработало для меня в gulp 4 с настройкой browserSync.
Абдеали Чанданвала

Рад, что это помогло @Abdeali Chandanwala!
MrThunder

@ Омар, я не согласен с вами, менее продвинутым пользователям, таким как я, полезно узнать, как добавить их в gulpfile. Лучше всего помнить, что у людей разные уровни знаний, и не нужно было бы просто писать требовать в gruntfile, так как я предполагаю, что они понимают, как добавить его в gulp.
MrThunder

3

Я решил

test: {
        options: {
          port: 9000,
          base: [
            '.tmp',
            'test',
            '<%= yeoman.app %>'
          ],
         middleware: function (connect) {
                  return [
                      modRewrite(['^[^\\.]*$ /index.html [L]']),
                      connect.static('.tmp'),
                      connect().use(
                          '/bower_components',
                          connect.static('./bower_components')
                      ),
                      connect.static('app')
                  ];
              }
        }
      },

3

Я отвечаю на этот вопрос из более крупного вопроса:

Когда я добавляю $ locationProvider.html5Mode (true), мой сайт не позволяет вставлять URL-адреса. Как настроить мой сервер для работы, когда html5Mode имеет значение true?

Когда у вас включен html5Mode, символ # больше не будет использоваться в ваших URL. Символ # полезен, поскольку не требует настройки на стороне сервера. Без #, URL выглядит намного лучше, но это также требует переписывания на стороне сервера. Вот некоторые примеры:

Для Express Rewrites с AngularJS вы можете решить эту проблему с помощью следующих обновлений:

app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname + '/public/app/views/index.html'));
});

и

<!-- FOR ANGULAR ROUTING -->
<base href="/">

и

app.use('/',express.static(__dirname + '/public'));

Спасибо, это решение. Для Nodejs добавьте app.js в app.use ('/ *', index);
Тигин

2

Я считаю, что ваша проблема в отношении сервера. В англоязычной документации по режиму HTML5 (по ссылке в вашем вопросе) говорится:

На стороне сервера Для использования этого режима требуется перезапись URL на стороне сервера, в основном вам нужно переписать все ваши ссылки на точку входа вашего приложения (например, index.html)

Я считаю, что вам нужно настроить переписать URL-адрес с / о /.


Спасибо за ваш ответ. Поэтому, когда я нахожусь на странице about и я обновляюсь, сервер должен переписать URL в /? Проблема в том, что я работаю с бэкэндом Rails, который находится в другом домене. Все общение происходит через вызовы API. Как бы я переписал эти URL?
Дитер де Месмакер

Дело в том, что ваше приложение доступно по маршруту вашего сервера / поэтому, когда вы обновляете URL-адрес, который / о браузере будет запрашивать с вашего сервера любую страницу в / о (в этом случае у вас нет страниц, поэтому вам нужно перенаправить те вернулись).
lucuma

2

У нас был сервер перенаправления в Express:

app.get('*', function(req, res){
    res.render('index');
});

и у нас все еще возникали проблемы с обновлением страницы, даже после того, как мы добавили <base href="https://stackoverflow.com/" />.

Решение: убедитесь, что вы используете реальные ссылки на своей странице для навигации; не вводите маршрут в URL, иначе вы получите обновление страницы. (глупая ошибка, я знаю)

:-П


но в связи с этим каждый HTTP запрос будет иметь индексный файл, например , как: getJsonFromServer / - запрос HTTP , который возвращает данные , но из - за этой конфигурации он также будет возвращать индекс страницы
Виджай

1

Наконец, у меня появился способ решить эту проблему на стороне сервера, так как это больше похоже на проблему с самими AngularJ, я использую 1.5 Angularjs и у меня возникла такая же проблема при перезагрузке страницы. Но после добавления приведенного ниже кода в мой server.jsфайл это спасает мой день, но это не является правильным решением или не хорошим способом.

app.use(function(req, res, next){
  var d = res.status(404);
     if(d){
        res.sendfile('index.html');
     }
});

0

Я нашел даже лучший плагин Grunt, который работает, если у вас есть index.html и Gruntfile.js в одном каталоге;

https://npmjs.org/package/grunt-connect-pushstate

После этого в вашем Gruntfile:

 var pushState = require('grunt-connect-pushstate/lib/utils').pushState;


    connect: {
    server: {
      options: {
        port: 1337,
        base: '',
        logger: 'dev',
        hostname: '*',
        open: true,
        middleware: function (connect, options) {
          return [
            // Rewrite requests to root so they may be handled by router
            pushState(),
            // Serve static files
            connect.static(options.base)
          ];
        }
      },
    }
},

Модуль grunt-connect-pushstate устарел
Эван Плейс

0
I solved same problem using modRewrite.  
AngularJS is reload page when after # changes.  
But HTML5 mode remove # and invalid the reload.  
So we should reload manually.
# install connect-modrewrite
    $ sudo npm install connect-modrewrite --save

# gulp/build.js
    'use strict';
    var gulp = require('gulp');
    var paths = gulp.paths;
    var util = require('util');
    var browserSync = require('browser-sync');
    var modRewrite  = require('connect-modrewrite');
    function browserSyncInit(baseDir, files, browser) {
        browser = browser === undefined ? 'default' : browser;
        var routes = null;
        if(baseDir === paths.src || (util.isArray(baseDir) && baseDir.indexOf(paths.src) !== -1)) {
            routes = {
                '/bower_components': 'bower_components'
            };
        }

        browserSync.instance = browserSync.init(files, {
            startPath: '/',
            server: {
            baseDir: baseDir,
            middleware: [
                modRewrite([
                    '!\\.\\w+$ /index.html [L]'
                ])
            ],
            routes: routes
            },
            browser: browser
        });
    }

0

У меня была такая же проблема с приложением java + angular, созданным с помощью JHipster . Я решил это с помощью фильтра и списка всех угловых страниц в свойствах:

application.yml:

angular-pages:
  - login
  - settings
...

AngularPageReloadFilter.java

public class AngularPageReloadFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.getRequestDispatcher("index.html").forward(request, response);
    }
}

WebConfigurer.java

private void initAngularNonRootRedirectFilter(ServletContext servletContext,
                                              EnumSet<DispatcherType> disps) {
    log.debug("Registering angular page reload Filter");
    FilterRegistration.Dynamic angularRedirectFilter =
            servletContext.addFilter("angularPageReloadFilter",
                    new AngularPageReloadFilter());
    int index = 0;
    while (env.getProperty("angular-pages[" + index + "]") != null) {
        angularRedirectFilter.addMappingForUrlPatterns(disps, true, "/" + env.getProperty("angular-pages[" + index + "]"));
        index++;
    }
    angularRedirectFilter.setAsyncSupported(true);
}

Надеюсь, это будет полезно для кого-то.


0

Gulp + browserSync:

Установите connect-history-api-fallback через npm, позже настройте задачу подачи сообщений

var historyApiFallback = require('connect-history-api-fallback');

gulp.task('serve', function() {
  browserSync.init({
    proxy: {
            target: 'localhost:' + port,
            middleware: [ historyApiFallback() ]
        }
  });
});

0

Ваш код на стороне сервера - JAVA, затем выполните следующие шаги

Шаг 1: Загрузите JAR urlrewritefilter. Нажмите здесь и сохраните, чтобы построить путь. WEB-INF / lib

шаг 2: включить режим HTML5 $ locationProvider.html5Mode (true);

шаг 3: установить базовый URL <base href="https://stackoverflow.com/example.com/"/>

шаг 4: скопируйте и вставьте в свой WEB.XML

 <filter>
     <filter-name>UrlRewriteFilter</filter-name>
 <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>UrlRewriteFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

шаг 5: создать файл в WEN-INF / urlrewrite.xml

 <urlrewrite default-match-type="wildcard">


    <rule>
            <from>/</from>
            <to>/index.html</to>
        </rule>

    <!--Write every state dependent on your project url-->
    <rule>
            <from>/example</from>
            <to>/index.html</to>
        </rule>
    </urlrewrite>

Как добавить правило для динамического URL, как - test.com/user/101 test.com/user/102
Кришна Кумар Chourasiya

0

У меня есть это простое решение, которое я использую, и оно работает.

В приложении / Exceptions / Handler.php

Добавьте это вверху:

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

Затем внутри метода рендера

public function render($request, Exception $exception)
{
    .......

       if ($exception instanceof NotFoundHttpException){

        $segment = $request->segments();

        //eg. http://site.dev/member/profile
        //module => member
        // view => member.index
        //where member.index is the root of your angular app could be anything :)
        if(head($segment) != 'api' && $module = $segment[0]){
            return response(view("$module.index"), 404);
        }

        return response()->fail('not_found', $exception->getCode());

    }
    .......

     return parent::render($request, $exception);
}

0

Я решил проблему, добавив приведенный ниже фрагмент кода в файл node.js.

app.get("/*", function (request, response) {
    console.log('Unknown API called');
    response.redirect('/#' + request.url);
});

Примечание: когда мы обновляем страницу, она будет искать API вместо страницы Angular (из-за отсутствия тега # в URL). Используя приведенный выше код, я перенаправляю на URL с #


-2

Просто напишите правило в web.config

<system.webServer>
  <rewrite>
    <rules>
    <rule name="AngularJS Routes" stopProcessing="true">
      <match url=".*" />
      <conditions logicalGrouping="MatchAll">
        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
        <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
      </conditions>
      <action type="Rewrite" url="/" />
    </rule>
       </rules>
    </rewrite>
</system.webServer>

В индекс добавить это

<base href="/">

это работает для меня, может быть, вы забыли установить Html5Mode app.config

app.config(function ($stateProvider, $urlRouterProvider, $locationProvider){
    $locationProvider.html5Mode({ enabled: true, requireBase: true });
    $locationProvider.hashPrefix('!');
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.