我使用S3托管一个javascript应用程序,将使用HTML5 pushStates。问题是,如果用户书签了任何url,它将不会解析为任何东西。我需要的是能够接受所有url请求,并在我的S3桶中提供根index.html,而不仅仅是进行完全重定向。然后我的javascript应用程序可以解析URL并提供适当的页面。

有没有办法告诉S3为所有URL请求服务index.html,而不是做重定向?这类似于通过提供一个index.html来设置apache来处理所有传入的请求,如本例中的https://stackoverflow.com/a/10647521/1762614。我真的希望避免仅仅为了处理这些路由而运行web服务器。从S3执行所有操作非常有吸引力。


当前回答

这是一个非常简单的答案。如果您在S3上托管,则只需为路由器使用散列位置策略。

export const AppRoutingModule: ModuleWithProviders = RouterModule。forRoot(routes, {useHash: true, scrollPositionRestoration: 'enabled'});

其他回答

这是切题的,但这里有一个提示,对于那些使用Rackt的React Router库和(HTML5)浏览器历史,想要在S3上托管的人。

假设一个用户在s3托管的静态网站上访问/foo/bear。根据David之前的建议,重定向规则将把它们发送到/#/foo/bear。如果您的应用程序是使用浏览器历史记录构建的,那么这样做不会有太大的好处。然而,你的应用程序在这一点上加载,它现在可以操纵历史。

在我们的项目中包括Rackt历史(参见React Router项目中的使用自定义历史),你可以添加一个侦听器,它可以感知哈希历史路径,并根据需要替换路径,如下例所示:

import ReactDOM from 'react-dom';

/* Application-specific details. */
const route = {};

import { Router, useRouterHistory } from 'react-router';
import { createHistory } from 'history';

const history = useRouterHistory(createHistory)();

history.listen(function (location) {
  const path = (/#(\/.*)$/.exec(location.hash) || [])[1];
  if (path) history.replace(path);
});

ReactDOM.render(
  <Router history={history} routes={route}/>,
  document.body.appendChild(document.createElement('div'))
);

回顾一下:

David的S3重定向规则将/foo/bear定向到/#/foo/bear。 您的应用程序将加载。 历史侦听器将检测#/foo/bear历史符号。 用正确的路径替换历史记录。

链接标签将按预期工作,所有其他浏览器历史功能也是如此。我所注意到的唯一缺点是在初始请求时发生的间隙重定向。

这是受到AngularJS的一个解决方案的启发,我怀疑它可以很容易地适应任何应用程序。

既然问题仍然存在,我想我提出另一个解决方案。 我的情况是,我想在合并之前自动将所有拉请求部署到s3进行测试,使它们可以在[mydomain]/pull-requests/[pr number]/上访问 (例如www.example.com/pull-requests/822/)

据我所知,没有s3规则的场景允许在一个桶中使用html5路由有多个项目,所以上面大多数投票建议适用于根文件夹中的项目,不适用于自己子文件夹中的多个项目。

所以我把我的域指向我的服务器,在那里遵循nginx配置做的工作

location /pull-requests/ {
    try_files $uri @get_files;
}
location @get_files {
    rewrite ^\/pull-requests\/(.*) /$1 break;
    proxy_pass http://<your-amazon-bucket-url>;
    proxy_intercept_errors on;
    recursive_error_pages on;
    error_page 404 = @get_routes;
}

location @get_routes {
    rewrite ^\/(\w+)\/(.+) /$1/ break;
    proxy_pass http://<your-amazon-bucket-url>;
    proxy_intercept_errors on;
    recursive_error_pages on;
    error_page 404 = @not_found;
}

location @not_found {
    return 404;
}

它尝试获取文件,如果没有找到,则假设它是html5路由并尝试。如果你在angular的404页面中没有找到路由,你将永远不会到达@not_found,而会返回angular 404页面而不是not found files,这可以通过在@get_routes中添加If规则来修复。

我不得不说我在nginx配置和使用regex方面感到不太舒服,我得到了这个工作,经过了一些试验和错误,所以虽然这是有效的,但我相信有改进的空间,请分享你的想法。

注意:如果在s3配置中有重定向规则,请删除它们。

顺便说一下,在Safari中工作

也在寻找同样的问题。 我最终混合使用了上述建议的解决方案。

首先,我有一个s3桶,有多个文件夹,每个文件夹代表一个react/redux网站。 我还使用cloudfront进行缓存失效。

所以我必须使用路由规则来支持404,并将它们重定向到一个散列配置:

<RoutingRules>
    <RoutingRule>
        <Condition>
            <KeyPrefixEquals>website1/</KeyPrefixEquals>
            <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
        </Condition>
        <Redirect>
            <Protocol>https</Protocol>
            <HostName>my.host.com</HostName>
            <ReplaceKeyPrefixWith>website1#</ReplaceKeyPrefixWith>
        </Redirect>
    </RoutingRule>
    <RoutingRule>
        <Condition>
            <KeyPrefixEquals>website2/</KeyPrefixEquals>
            <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
        </Condition>
        <Redirect>
            <Protocol>https</Protocol>
            <HostName>my.host.com</HostName>
            <ReplaceKeyPrefixWith>website2#</ReplaceKeyPrefixWith>
        </Redirect>
    </RoutingRule>
    <RoutingRule>
        <Condition>
            <KeyPrefixEquals>website3/</KeyPrefixEquals>
            <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
        </Condition>
        <Redirect>
            <Protocol>https</Protocol>
            <HostName>my.host.com</HostName>
            <ReplaceKeyPrefixWith>website3#</ReplaceKeyPrefixWith>
        </Redirect>
    </RoutingRule>
</RoutingRules>

在我的js代码中,我需要用react-router的baseName配置来处理它。 首先,确保你的依赖是可互操作的,我已经安装了history==4.0.0,它与react-router==3.0.1不兼容。

我的依赖项是:

“历史”:“3.2.0”, “反应”:“15.4.1”, :“react-redux 4.4.6”, :“react-router 3.0.1”, :“react-router-redux 4.0.7”,

我创建了一个history.js文件来加载历史:

import {useRouterHistory} from 'react-router';
import createBrowserHistory from 'history/lib/createBrowserHistory';

export const browserHistory = useRouterHistory(createBrowserHistory)({
    basename: '/website1/',
});

browserHistory.listen((location) => {
    const path = (/#(.*)$/.exec(location.hash) || [])[1];
    if (path) {
        browserHistory.replace(path);
    }
});

export default browserHistory;

这段代码允许用散列处理服务器发送的404,并在载入路由的历史记录中替换它们。

你现在可以使用这个文件来配置你的存储和根文件。

import {routerMiddleware} from 'react-router-redux';
import {applyMiddleware, compose} from 'redux';

import rootSaga from '../sagas';
import rootReducer from '../reducers';

import {createInjectSagasStore, sagaMiddleware} from './redux-sagas-injector';

import {browserHistory} from '../history';

export default function configureStore(initialState) {
    const enhancers = [
        applyMiddleware(
            sagaMiddleware,
            routerMiddleware(browserHistory),
        )];

    return createInjectSagasStore(rootReducer, rootSaga, initialState, compose(...enhancers));
}
import React, {PropTypes} from 'react';
import {Provider} from 'react-redux';
import {Router} from 'react-router';
import {syncHistoryWithStore} from 'react-router-redux';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import variables from '!!sass-variable-loader!../../../css/variables/variables.prod.scss';
import routesFactory from '../routes';
import {browserHistory} from '../history';

const muiTheme = getMuiTheme({
    palette: {
        primary1Color: variables.baseColor,
    },
});

const Root = ({store}) => {
    const history = syncHistoryWithStore(browserHistory, store);
    const routes = routesFactory(store);

    return (
        <Provider {...{store}}>
            <MuiThemeProvider muiTheme={muiTheme}>
                <Router {...{history, routes}} />
            </MuiThemeProvider>
        </Provider>
    );
};

Root.propTypes = {
    store: PropTypes.shape({}).isRequired,
};

export default Root;

希望能有所帮助。 你会注意到,在这个配置中,我使用了redux注入器和一个自制的sagas注入器来通过路由异步加载javascript。 不要介意这些句子。

转到你的桶的静态网站托管设置 设置“Error document”为index.html。

对于这个问题,我有4种解决方案。前三个问题已经在答案中提到了,最后一个是我的贡献。

Set the error document to index.html. Problem: the response body will be correct, but the status code will be 404, which hurts SEO. Set the redirection rules. Problem: URL polluted with #! and page flashes when loaded. Configure CloudFront. Problem: all pages will return 404 from origin, so you need to chose if you won't cache anything (TTL 0 as suggested) or if you will cache and have issues when updating the site. Prerender all pages. Problem: additional work to prerender pages, specially when the pages changes frequently. For example, a news website.

我的建议是使用选项4。如果预先呈现所有页面,预期页面将不会出现404错误。页面将正常加载,框架将作为一个SPA进行控制和正常工作。还可以设置错误文档以显示通用的error.html页面和重定向规则,将404错误重定向到404.html页面(不带hashbang)。

关于403禁忌错误,我不让它们发生。在我的应用程序中,我认为主机桶中的所有文件都是公共的,我用具有读权限的everyone选项设置了这一点。如果您的站点有私有页面,让用户看到HTML布局应该不是问题。您需要保护的是数据,这是在后端完成的。

此外,如果你有私有资产,比如用户照片,你可以将它们保存在另一个bucket中。因为私有资产需要与数据同样的关注,并且不能与用于托管应用程序的资产文件进行比较。