簡體   English   中英

反應路由器在 aws s3 存儲桶中不起作用

[英]react router doesn't work in aws s3 bucket

我將我的 React 網站build/文件夾部署到 AWS S3 存儲桶中。

如果我去www.mywebsite.com ,它可以工作,如果我點擊a標簽去 Project 和 About 頁面,它會引導我到正確的頁面。 但是,如果我復制並發送頁面 url 或直接訪問如下鏈接: www.mywebsite.com/projects ,它會返回 404。

這是我的App.js代碼:

const App = () => (
    <Router>
        <div>
            <NavBar/>
            <Switch>
                <Route exact path="/" component={Home}/>
                <Route exact path="/projects" component={Projects}/>
                <Route exact path="/about" component={About}/>
                <Route component={NoMatch}/>
            </Switch>
        </div>
    </Router>
);

我不確定你是否已經解決了。 我遇到過同樣的問題。

這是因為 AWS S3 正在尋找您沒有的文件夾(項目)來提供服務。

只需將錯誤文檔指向index.html

在此處輸入圖像描述

這對我有用。 您可以在 react-router 中處理錯誤頁面。

2020 年 5 月 1 日更新:

由於這篇文章非常活躍,我需要更新答案:

所以你有幾個選項來解決這個問題:

  1. 您可以將index.html放在錯誤文檔框中(如 Alan Friedman 建議的那樣)。
    • 轉到您的存儲桶(實際具有代碼的存儲桶 - 不是您用來重定向的存儲桶)->屬性->靜態網站托管
    • 這不是“hacky”,但它之所以有效,是因為react-router的工作方式:它處理來自前端的請求並將用戶路由到其他路由,但總的來說,React 應用程序是一個單頁應用程序。
    • 如果您想要服務器端 React,請考慮使用Next.js

在此處輸入圖像描述

  1. 你可以在你的 React 應用的public文件夾和Static website hosting : Error 文檔框中放置一個指定的錯誤文件error.html ,輸入: error.html 這也有效。 我已經測試過了。

  2. 使用AWS CloudFront > CloudFront Distributions > The distribution of your bucket > Error Pages並添加您想要的錯誤代碼。 如果您不使用react-router (如下例所示),存儲桶將響應錯誤 403 ,因此您可以使用error.html響應。

在此處輸入圖像描述

  1. 對於TypeScript ,目前react-router不支持它,所以你應該考慮選項 2 和 3 他們將根據這個線程在未來的版本6.*中更新庫。

如果此 S3 存儲桶由CloudFront 分配提供:

在 CloudFront 分配中創建一個自定義錯誤頁面 (AWS Docs) ,將 404 錯誤路由到 index.html 並返回 200 響應代碼。 這樣,您的應用程序將處理路由。

自定義錯誤頁面

在此處輸入圖像描述

將 4xx 重定向到 index.html 會起作用,但該解決方案會使您陷入許多其他問題,例如 google 將無法抓取這些頁面。 請檢查此鏈接,它通過使用 S3 存儲桶的重定向規則解決了此問題。

案例使用 Cloudfront 到 S3:

https://hackernoon.com/hosting-static-react-websites-on-aws-s3-cloudfront-with-ssl-924e5c134455

3b) AWS CloudFront — 錯誤頁面 創建 CloudFront 分配后,當其狀態為進行中時,進入錯誤頁面選項卡。 使用自定義錯誤響應處理響應代碼 404 和 403。

Google 建議緩存 1 周或 604800 秒。 我們在這里所做的是設置 CloudFront 以處理丟失的 html 頁面,這通常發生在用戶輸入無效路徑時,或者特別是當他們刷新根路徑以外的路徑時。

發生這種情況時:

CloudFront 將查找 S3 存儲桶中不存在的文件; 存儲桶中只有 1 個 html 文件,即 index.html 用於像此項目示例這樣的單頁應用程序的情況 將返回 404 響應,我們的自定義錯誤響應設置將劫持它。 我們將返回 200 響應代碼和 index.html 頁面。 將與 index.html 文件一起加載的 React 路由器將查看 url 並呈現正確的頁面而不是根路徑。 對於查詢路徑的所有請求,此頁面將在 TTL 期間緩存。 為什么我們還需要處理 403? 這是因為 Amazon S3 為不存在的資產返回此響應代碼,而不是 404。 例如, https ://yourdomain.com/somewhere 的 url 將尋找一個不存在的名為某處(無擴展名)的文件。

PS。 它曾經返回404,但現在似乎返回403; 無論哪種方式,最好同時處理兩個響應代碼)。

這個問題已經有幾個很好的答案。 雖然這個問題現在很老了,但我今天遇到了這個問題,所以我覺得這個答案可能會有所幫助。

S3 有兩種端點,如果您遇到此錯誤,您可能在 CloudFront 分配的源域名字段中直接選擇了 S3 存儲桶作為端點。

這看起來像: bucket-name.s3.amazonaws.com並且是一個完全有效的端點。 事實上,AWS 似乎確實希望這是默認行為。 此條目將位於您創建 CloudFront 分配時的下拉列表中。

但是,這樣做然后設置錯誤頁面可能會也可能不會解決您的問題。

但是,S3 有一個專用的網站端點。 這可從您的 S3 存儲桶 > 屬性 > 靜態網站托管訪問。 (您將在頂部看到鏈接)。

您應該使用此鏈接而不是 CloudFront 中出現的原始自動填充鏈接。 您可以在創建分發時將其放入,或者在創建后,您可以從選項卡 Origins 和 Origins Groups 進行編輯,然后使緩存無效。

此鏈接將如下所示: bucket-name.s3-website.region-name.amazonaws.com

一旦傳播和改變,這應該可以解決你的問題。

tl;dr:不要在 CloudFront 中使用默認的 S3 端點。 請改用 S3 網站端點。 您的原始域應如下所示: my-application.s3-website.us-east-1.amazonaws.com而不是my-application.s3.amazonaws.com

有關網站終端節點的更多信息,請參閱此處的 AWS 文檔。

以下配置已解決在此處輸入圖像描述

更新了相關雲前端分發中的錯誤頁面,其中自定義錯誤響應從 http 狀態代碼 404-not-found 到響應頁面路徑處的代碼 200-OK 為 / 且生存時間接近於零

此線程中有一些很好的答案,主要是圍繞使用重定向規則配置您的 S3 存儲桶。 雖然這也適用於我的用例,它通常通過 CloudFront 使用 React Router 托管 React 單頁應用程序,並將 S3 配置為 Origin,但鑒於最近發布的一種新功能,我決定嘗試不同的方法, CloudFront 函數。 主要思想是在 URL 到達 CloudFront 緩存之前重寫 URL,以便在客戶端由 React Router 創建的 URL 被重寫以指向應用程序域根。

您可以在此處閱讀有關 CloudFront 函數的更多信息: https ://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html

主要思想是使用 CloudFront 函數為任何不針對靜態文件(即 .js、.CSS、.html 等)的請求重寫請求 Uri,並使其指向 /index.html 文件。 此函數將作為查看器請求觸發器的一部分運行,並在 CloudFront 檢查請求的對象是否在 CloudFront 緩存中之前運行。 這將導致客戶端發送的任何由 React 路由器生成的 Uri 在從源服務器請求內容之前被重寫,因此不需要在 S3 上進行任何進一步的重定向。 同時,請求但在源上不再存在的文件將按預期返回 404,而所有其他靜態內容將按請求提供。

這是我的 CloudFront 函數的樣子:

function handler(event) {
  var request = event.request;

  if (!request.uri.includes('.')) {
    request.uri = "/index.html";
    request.querystring = {}
  }

  return request;
}

您可以根據自己的喜好使 URL 重寫規則變得復雜,但令人驚訝的是,這小段代碼非常適合我的用例。 此外,與 Lambda@Edge 函數不同,CloudFront 函數更輕量級,允許您通過 AWS 控制台進行幾乎實時的實驗,方法是在其中更改函數代碼、對其進行測試並在幾秒鍾內部署它。 但是,它們具有某些語言限制,因此不要期望支持所有 JavaScript 語言功能。 此頁面記錄了可供您使用的內容:

https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-features.html

這個線程對我真的很有幫助。 我知道這很舊,但是,我想添加一個最終對我有用的aws-cdk代碼片段。 我的用例需要我使用aws-cdk ,因此,如果您期待基於aws-cdk的解決方案,我將在此處添加它。

代碼片段將index.html添加為 s3 和 cloudfront 中的錯誤頁面。 特別是對於 cloudfront,我將錯誤頁面添加為/index.html用於404403 ,響應為200


// imports...
// import * as cdk from '@aws-cdk/core';
// import * as s3 from '@aws-cdk/aws-s3';
// import * as cloudfront from '@aws-cdk/aws-cloudfront';

// following code snippet has to be added in a 
// class which extends to a Construct or a Stack

    const bucket = new s3.Bucket(this, '<Specify a name>', {
      bucketName: '<add your bucket name>',
      websiteIndexDocument: 'index.html',
      websiteErrorDocument: 'index.html',
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      removalPolicy: cdk.RemovalPolicy.DESTROY, // edit it according to your use case, I'll change it though
      autoDeleteObjects: true,
    });

    const cloudFrontOAI = new cloudfront.OriginAccessIdentity(this, 'OAI');

    const distribution = new cloudfront.CloudFrontWebDistribution(this, props?.stackName + 'AbcWebsiteDistribution', {
      defaultRootObject: "index.html",
      errorConfigurations: [{
        errorCode: 404,
        responsePagePath: "/index.html",
        responseCode: 200
      }, {
        errorCode: 403,
        responsePagePath: "/index.html",
        responseCode: 200
      }],
      originConfigs: [
        {
          s3OriginSource: {
            s3BucketSource: bucket,
            originAccessIdentity: cloudFrontOAI,
            originPath: "/artifacts" // change this based on your use case
          },
          behaviors: [{ isDefaultBehavior: true }]
        }
      ]
    })

    bucket.grantRead(cloudFrontOAI.grantPrincipal);

從這個線程中真正幫助我的答案是:

https://stackoverflow.com/a/56655629/6211961

https://stackoverflow.com/a/58978355/6211961

https://stackoverflow.com/a/64201582/6211961

我將添加另一種方法,因為CloudFront 函數選項很棒,只是它返回 soft-404,這對您的 SEO 不利

此方法同時使用 CloudFront 函數和 CloudFront 分發自定義錯誤頁面來返回具有 200 狀態代碼的有效 URI 和具有 404 狀態代碼的無效 URI。

功能:

這個特殊的功能是為React Router v6 快速入門指南中指定的路由配置而設計的,其中/invoices/expenses嵌套在根路由/下,外加一個用於 404s *的包羅萬象的路由。 它將對包含在validPaths數組中的 URI 的請求重定向到/index.html ,從而允許其他請求按原樣進行。 您應該調整此函數以反映您的 SPA 的有效 URI,並且您可以選擇更改此函數檢查request.urivalidPaths的方式。

function handler(event) {
    var request = event.request;

    var validPaths = ['/','/invoices','/expenses'];

    if( validPaths.includes(request.uri) ){
        request.uri = "/index.html"
    } 
     
    return request;
}

任何有效的靜態資產請求仍將返回靜態資產,但對頁面或資產的無效請求將返回帶有 403 狀態代碼的 XML 錯誤。

錯誤處理程序:

如果我們將其與將 403s 轉換為 404s + /index.html 的自定義錯誤響應相結合,我們將有一個應用程序允許 React Router 正常工作,而不會返回軟 404s! 您的本地搜索引擎爬蟲會很高興。

CloudFront 分發自定義錯誤響應表單圖像鏈接,因為我沒有足夠的分數讓它內聯顯示。

其他想法:

這種方法的缺點是必須手動維護函數中的有效 URI 列表。 本文討論了與此類似的其他方法。

請注意,對於這種方法,您應該使用REST API 作為您的端點,訪問受 OAI 限制,而不是 S3 靜態網站端點。 這將確保您網站的所有流量都通過您的 CloudFront 分配, 保護私有內容(如果適用),同時確保您的 CloudFront 日志(如果您使用它們)是完整的。

為什么會發生

您的問題是您希望將責任傳遞給您的反應應用程序/javascript。 該鏈接將起作用,因為 react 可以偵聽鏈接單擊並簡單地更新瀏覽器 URL 欄中的路由。 但是,如果您去的位置沒有加載您的腳本(index.html 和 bundle.js 或您的應用程序代碼所在的任何位置),那么 JavaScript 將永遠不會加載並且沒有機會處理請求。 相反,無論您的服務器運行什么,都會處理該請求,查看該位置是否有資源,並返回 404 錯誤或發現的任何其他錯誤。

解決方案

正如評論中提到的,這就是為什么您需要將 404 錯誤重定向到您的應用所在的確切位置的原因。 這不是亞馬遜特有的,它是設置您的反應應用程序的一般要求。

要解決這個問題,您需要找出在您的服務器上處理路由的內容以及如何配置它。 例如,如果你有一個 Apache 服務器,一個 .htaccess 文件可以像這樣處理這個問題:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.html$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.html [L]
 </IfModule>

該文件允許服務器將未找到的錯誤重定向到 index.html。 請記住,它可能會影響您服務器上的其他路由規則,因此如果您的 react 應用程序有自己的位置,則此配置最簡單,不會干擾其他內容。

盡管使用了接受的答案中描述的配置,但我遇到了這個問題,但是如果您仍然收到 403 錯誤,並且您正在使用 AWS Cloudflare Manager,您可以在分發設置的“錯誤頁面”選項卡中創建一個錯誤頁面,具有以下配置:

Error Code: 404,
Customize Error Response: Yes, 
Response Page Path: /index.html, 
HTTP Response Code: 200

將路由處理傳遞給瀏覽器路由器的錯誤頁面配置圖片

這對我有用,盡管我對適當的服務器管理員一無所知,希望有人會告訴我這是否是一個重大問題,如果是偶然的。

使用托管靜態更新 s3 的配置,默認使用 index.html在此處輸入圖像描述

添加帶有錯誤頁面的 CloudFront

404 error-> index.html->status 200

在此處輸入圖像描述

100% 工作和測試在 S3 Bucket 中托管像 React 這樣的 SPA 網站並修復 404 子頁面問題

  1. 登錄亞馬遜控制台
  2. 搜索雲鋒
  3. 選擇您的發行版
  4. 選擇錯誤頁面選項卡並單擊創建自定義錯誤響應在此處輸入圖像描述
  5. 在此處創建響應規則在此處輸入圖像描述
  6. 保存錯誤規則

你已經完成了!

這里有很多答案,所以我想我會對迄今為止的現有答案進行調查,並比較各種方法的優缺點。

方法 描述 優點 缺點 答案
CloudFront 函數 使用 AWS CloudFront 函數將請求寫入 SPA 的根文檔,例如index.html 沒有拋出錯誤,沒有重定向,顯然對 SEO 有好處。 AWS 在他們的一個文檔中建議了這種方法。 額外費用(盡管在撰寫本文時,免費層允許每月免費調用 2M 次,之后每百萬次調用只有 10c)。 需要部署/管理的額外基礎設施。 公斤
主機作為網站 將 S3 存儲桶配置為托管網站,而不是 REST API。 以這種方式配置時,S3 會自動處理路由。 無法使用 Orign Access Identity 相反,S3 端點通過 HTTP 公開,您需要使用自定義標頭來限制訪問,這更復雜。 FV
錯誤重定向(404 和 403) 絕大多數答案似乎都提供了一些這樣的味道。 這個想法是允許 404 或 403 錯誤發生,但隨后返回index.html (或任何 SPA 根文檔)作為錯誤頁面。 易於實施。 每次嘗試訪問非根 URL 時都會導致客戶端重定向。 顯然這可能對 SEO 不利。 AR , V , MG , PB , K , KS
URL 哈希 / Hashbang 一些解決方案涉及使用 URL 中的哈希路由子頁面。 無需重定向,無需額外的 AWS 資源。 這些方法似乎存在問題 - 請參見此處 URL 中的哈希值被一些人認為是丑陋的; 似乎確實有減輕這種情況的方法,但這涉及一些額外的復雜性。 甲乙_

注意:我的這個答案是基於我在其他答案中讀到的。 除了“CloudFront Function”方法之外,我還沒有完全測試過任何方法,我可以確認它有效。

我通過使用HashRouter而不是Router來修復它

import { Switch, Route, HashRouter } from 'react-router-dom';

const App = () => (
    <HashRouter>
        <div>
            <NavBar/>
            <Switch>
                <Route exact path="/" component={Home}/>
                <Route exact path="/projects" component={Projects}/>
                <Route exact path="/about" component={About}/>
                <Route component={NoMatch}/>
            </Switch>
        </div>
    </HashRouter>
);

該應用程序可通過https://your-domain.s3.amazonaws.com/index.html?someParam=foo&otherPram=bar/#/projects之類的方式訪問

在本地主機上,例如https://localhost:3000/?someParam=foo&otherPram=bar/#/projects

不需要對 S3 進行任何更改。

我正在使用 NextJS 並且遇到了同樣的問題。 我的解決方案是檢查路由信息,如果不合適就推送。

const router = useRouter();
useEffect(() => {
    if (!router.pathname.startsWith(router.asPath)) {
      router.push(router.asPath);
    }
  });

除了將錯誤頁面路由到 index.html 之外,無需在 S3 端進行任何修改

根據之前的答案,解決問題的最佳方法:重新定義后備策略。 如果您想了解更多關於客戶端路由與服務器端的信息,尤其是 React JS 中不同的路由方法,請查看這篇文章

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM