URL react-router tidak berfungsi saat menyegarkan atau menulis secara manual


655

Saya menggunakan React-router dan berfungsi dengan baik saat saya mengklik tombol tautan, tetapi ketika saya menyegarkan halaman web saya tidak memuat apa yang saya inginkan.

Misalnya, saya masuk localhost/joblistdan semuanya baik-baik saja karena saya tiba di sini dengan menekan tautan. Tetapi jika saya me-refresh halaman web saya mendapatkan:

Cannot GET /joblist

Secara default, tidak berfungsi seperti ini. Awalnya saya punya URL saya localhost/#/dan localhost/#/joblistdan mereka bekerja dengan sangat baik. Tapi saya tidak suka URL semacam ini, jadi mencoba menghapusnya #, saya menulis:

Router.run(routes, Router.HistoryLocation, function (Handler) {
 React.render(<Handler/>, document.body);
});

Masalah ini tidak terjadi localhost/, yang ini selalu mengembalikan apa yang saya inginkan.

EDIT: Aplikasi ini satu halaman, jadi /joblisttidak perlu meminta apa pun ke server mana pun.

EDIT2: Seluruh router saya.

var routes = (
    <Route name="app" path="/" handler={App}>
        <Route name="joblist" path="/joblist" handler={JobList}/>
        <DefaultRoute handler={Dashboard}/>
        <NotFoundRoute handler={NotFound}/>
    </Route>
);

Router.run(routes, Router.HistoryLocation, function (Handler) {
  React.render(<Handler/>, document.body);
});

kecuali Anda menggunakan htaccess untuk memuat halaman tur utama Anda dan beri tahu router Anda untuk menggunakan location.pathname itu tidak akan berfungsi ..
Charles John Thompson III

Bagaimana Anda menghapus #simbol itu? Terima kasih!
SudoPlz

4
Jika Anda meng-hosting aplikasi reaksi di bucket S3, Anda dapat dengan mudah mengatur dokumen kesalahan index.html. Ini akan memastikan index.htmlmengenai apa pun yang terjadi.
Trevor Hutto

Dalam kasus saya, ini berfungsi dengan baik di windows tetapi tidak di linux
Ejaz Karim

Ini adalah referensi yang membantu menyelesaikan masalah saya: github.com/facebook/create-react-app/blob/master/packages/…
jimbotron

Jawaban:


1096

Melihat komentar pada jawaban yang diterima dan sifat umum dari pertanyaan ini ('tidak berfungsi'), saya pikir ini mungkin tempat yang bagus untuk beberapa penjelasan umum tentang masalah yang terlibat di sini. Jadi jawaban ini dimaksudkan sebagai informasi latar belakang / elaborasi pada kasus penggunaan khusus OP. Tolong bersamaku.

Sisi server vs sisi Klien

Hal besar pertama yang harus dipahami tentang hal ini adalah bahwa sekarang ada 2 tempat di mana URL ditafsirkan, sedangkan dulu hanya ada 1 di 'masa lalu'. Di masa lalu, ketika hidup itu sederhana, beberapa pengguna mengirim permintaanhttp://example.com/about ke server, yang memeriksa bagian jalur URL, menentukan pengguna meminta halaman tentang dan kemudian mengirim kembali halaman itu.

Dengan perutean sisi klien, yang disediakan oleh React-Router, segalanya menjadi kurang sederhana. Pada awalnya, klien belum memiliki kode JS yang dimuat. Jadi permintaan pertama akan selalu ke server. Itu kemudian akan mengembalikan halaman yang berisi tag skrip yang diperlukan untuk memuat Bereaksi dan Bereaksi Router dll. Hanya ketika skrip-skrip itu dimuat, fase 2 dimulai. Pada fase 2, ketika pengguna mengklik tautan navigasi 'Tentang kami' misalnya, URL diubah secara lokal saja menjadi http://example.com/about(dimungkinkan oleh API Sejarah ), tetapi tidak ada permintaan ke server dibuat. Alih-alih, React Router melakukan tugasnya di sisi klien, menentukan tampilan Bereaksi mana yang akan di-render dan dirender. Dengan asumsi halaman tentang Anda tidak perlu melakukan panggilan REST, itu sudah dilakukan. Anda telah beralih dari Rumah ke Tentang Kami tanpa ada permintaan server yang dipecat.

Jadi pada dasarnya ketika Anda mengklik sebuah tautan, beberapa Javascript berjalan yang memanipulasi URL di bilah alamat, tanpa menyebabkan penyegaran halaman , yang pada gilirannya menyebabkan React Router untuk melakukan transisi halaman di sisi klien .

Tetapi sekarang pertimbangkan apa yang terjadi jika Anda menyalin-tempel URL di bilah alamat dan mengirimkannya lewat email ke teman. Teman Anda belum memuat situs web Anda. Dengan kata lain, dia masih dalam fase 1 . Belum ada React Router yang berjalan di mesinnya. Jadi browsernya akan membuat permintaan server untuk http://example.com/about.

Dan di sinilah masalah Anda dimulai. Hingga saat ini, Anda bisa lolos hanya dengan meletakkan HTML statis di webroot server Anda. Tetapi itu akan memberikan 404kesalahan untuk semua URL lain ketika diminta dari server . URL yang sama berfungsi dengan baik di sisi klien , karena ada React Router melakukan perutean untuk Anda, tetapi mereka gagal di sisi server kecuali Anda membuat server Anda memahaminya.

Menggabungkan perutean server dan sisi klien

Jika Anda ingin http://example.com/aboutURL berfungsi di sisi server dan sisi klien, Anda perlu mengatur rute untuk itu di sisi server dan sisi klien. Masuk akal bukan?

Dan di sinilah pilihan Anda dimulai. Solusi berkisar dari mem-bypass masalah sama sekali, melalui rute catch-all yang mengembalikan bootstrap HTML, ke pendekatan isomorfik lengkap di mana server dan klien menjalankan kode JS yang sama.

.

Memotong masalah sama sekali: Sejarah Hash

Dengan Hash History alih-alih Riwayat Browser , URL Anda untuk halaman tentang akan terlihat seperti ini: http://example.com/#/about Bagian setelah #simbol hash ( ) tidak dikirim ke server. Jadi server hanya melihat http://example.com/dan mengirim halaman indeks seperti yang diharapkan. React-Router akan mengambil #/aboutbagian itu dan menampilkan halaman yang benar.

Kerugian :

  • URL 'jelek'
  • Render sisi server tidak dimungkinkan dengan pendekatan ini. Sejauh menyangkut Search Engine Optimization (SEO), situs web Anda terdiri dari satu halaman dengan hampir tidak ada konten di dalamnya.

.

Tangkap semua

Dengan pendekatan ini, Anda menggunakan Riwayat Peramban, tetapi hanya mengatur catch-all pada server yang mengirim /*keindex.html , secara efektif memberi Anda banyak situasi yang sama seperti dengan Hash History. Namun Anda memiliki URL bersih dan Anda dapat memperbaiki skema ini nanti tanpa harus membatalkan semua favorit pengguna Anda.

Kerugian :

  • Lebih rumit untuk diatur
  • Masih belum bagus SEO

.

Hibrida

Dalam pendekatan hibrida, Anda memperluas skenario catch-all dengan menambahkan skrip khusus untuk rute tertentu. Anda dapat membuat beberapa skrip PHP sederhana untuk mengembalikan halaman terpenting situs Anda dengan konten yang disertakan, sehingga Googlebot setidaknya dapat melihat apa yang ada di halaman Anda.

Kerugian :

  • Bahkan lebih rumit untuk diatur
  • Hanya SEO yang bagus untuk rute yang Anda berikan perlakuan khusus
  • Kode duplikat untuk merender konten di server dan klien

.

Isomorfis

Bagaimana jika kita menggunakan Node JS sebagai server kita sehingga kita dapat menjalankan kode JS yang sama di kedua ujungnya? Sekarang, kita memiliki semua rute yang kita tentukan dalam satu konfigurasi router reaksi dan kita tidak perlu menduplikasi kode rendering kita. Jadi ini adalah 'cawan suci'. Server mengirimkan markup yang sama persis seperti yang kita akan berakhir jika transisi halaman telah terjadi pada klien. Solusi ini optimal dalam hal SEO.

Kerugian :

  • Server harus (dapat) menjalankan JS. Saya sudah bereksperimen dengan Java icw Nashorn tetapi tidak berhasil untuk saya. Dalam praktiknya itu sebagian besar berarti Anda harus menggunakan server berbasis Node JS.
  • Banyak masalah lingkungan yang rumit (menggunakan windowsisi server dll)
  • Curam kurva pembelajaran

.

Yang mana yang harus saya gunakan?

Pilih salah satu yang bisa Anda hindari. Secara pribadi saya pikir hasil tangkapan-semua cukup sederhana untuk diatur, jadi itu adalah minimum saya. Pengaturan ini memungkinkan Anda untuk memperbaiki berbagai hal dari waktu ke waktu. Jika Anda sudah menggunakan Node JS sebagai platform server Anda, saya pasti akan menyelidiki melakukan aplikasi isomorfik. Ya itu sulit pada awalnya, tetapi begitu Anda memahami itu sebenarnya solusi yang sangat elegan untuk masalah tersebut.

Jadi pada dasarnya, bagi saya, itu akan menjadi faktor penentu. Jika server saya berjalan pada Node JS, saya akan menggunakan isomorfis; kalau tidak saya akan mencari solusi Catch-all dan hanya mengembangkannya (solusi Hybrid) seiring berjalannya waktu dan persyaratan SEO menuntutnya.

Jika Anda ingin mempelajari lebih lanjut tentang rendering isomorfis (juga disebut 'universal') dengan React, ada beberapa tutorial bagus tentang subjek ini:

Juga, untuk memulai, saya sarankan untuk melihat beberapa starter kit. Pilih satu yang cocok dengan pilihan Anda untuk tumpukan teknologi (ingat, Bereaksi hanyalah V di MVC, Anda perlu lebih banyak barang untuk membangun aplikasi lengkap). Mulailah dengan melihat yang diterbitkan oleh Facebook sendiri:

Atau pilih salah satu dari banyak oleh komunitas. Ada situs bagus sekarang yang mencoba mengindeks semuanya:

Saya mulai dengan ini:

Saat ini saya menggunakan versi render universal buatan sendiri yang terinspirasi oleh dua starter kit di atas, tetapi sekarang sudah ketinggalan zaman.

Semoga berhasil dengan pencarian Anda!


2
Pos hebat Stijn! Apakah Anda akan merekomendasikan menggunakan starter kit untuk aplikasi reaksi Isomorphic? Jika demikian, dapatkah Anda memberikan contoh yang Anda inginkan?
Chris

1
@ Paulos3000 Tergantung pada server apa yang Anda gunakan. Pada dasarnya Anda menentukan rute /*dan membuatnya merespons dengan halaman HTML Anda. Yang rumit di sini adalah memastikan bahwa Anda tidak memotong permintaan untuk file .js dan .css dengan rute ini.
Stijn de Witt

2
@ Paulos3000 Lihat di sini untuk beberapa pertanyaan terkait: untuk Apache / php , untuk Express / js , untuk J2E / Java .
Stijn de Witt

1
Hai, saya mencoba solusi hash, namun tidak berhasil. Mendapatkan createMemoryHistory is not a functionkesalahan Pikiran lihat? stackoverflow.com/questions/45002573/...
Leon Gaban

5
@LeonGaban Sepertinya sejak jawaban ini ditulis, React Router mengubah implementasinya. Mereka sekarang memiliki instance Router yang berbeda untuk sejarah yang berbeda dan mereka melakukan konfigurasi sejarah di latar belakang. Prinsip dasarnya masih sama.
Stijn de Witt

116

Jawaban di sini sangat membantu, apa yang berhasil bagi saya adalah mengonfigurasi server Webpack saya untuk mengharapkan rute.

devServer: {
   historyApiFallback: true,
   contentBase: './',
   hot: true
},

HistoryApiFallback adalah apa yang memperbaiki masalah ini untuk saya. Sekarang perutean bekerja dengan benar dan saya dapat menyegarkan halaman atau mengetik URL secara langsung. Tidak perlu khawatir tentang bekerja di server node Anda. Jawaban ini jelas hanya berfungsi jika Anda menggunakan webpack.

EDIT: lihat jawaban saya di sini untuk alasan yang lebih terperinci mengapa ini diperlukan: https://stackoverflow.com/a/37622953/5217568


16
Perhatikan bahwa tim Webpack merekomendasikan agar tidak menggunakan server dev dalam produksi.
Stijn de Witt

5
Untuk tujuan pengembangan generik, ini adalah solusi terbaik. historyApiFallbackCukup. Adapun semua opsi lain, itu juga dapat diatur dari CLI dengan bendera --history-api-fallback.
Marco Lazzeri

2
@ Kunok Tidak. Ini adalah perbaikan cepat untuk pengembangan tetapi Anda masih harus memikirkan sesuatu untuk produksi.
Stijn de Witt

contentBase: ': /' karena file aplikasi Anda dapat diakses dari url
Nezih

85

Anda dapat mengubah .htaccessfile Anda dan menyisipkan ini:

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

Saya menggunakan react: "^16.12.0"dan react-router: "^5.1.2" Metode ini adalah Catch-all dan mungkin cara termudah untuk membantu Anda memulai.


3
Ini bekerja dengan baik! dan yang termudah jika Anda tidak ingin merestrukturisasi aplikasi Anda
mhyassin

7
Jangan lupa RewriteEngine On sebagai baris pertama
Kai Qing

3
Perhatikan bahwa jawaban ini merujuk ke konfigurasi pada server Apache. Ini bukan bagian dari aplikasi Bereaksi.
Colton Hicks

Saya tidak mengerti jawaban di atas. Tidak ada guru yang mengatakan bahwa tautan saya tidak akan berfungsi secara langsung. Namun file htaccess sederhana ini berfungsi. Terima kasih
Thomas Williams

Ini berfungsi untuk ekspor statis next.js juga. Karena tomcat tidak mirip dengan node, kita memerlukan konfigurasi tambahan ini untuk ekspor statis next.js.
giri-jeedigunta

62

Untuk Pengguna React Router V4 :

Jika Anda mencoba menyelesaikan masalah ini dengan teknik Hash History yang disebutkan dalam jawaban lain, perhatikan itu

<Router history={hashHistory} >

tidak bekerja di V4, silakan gunakan HashRoutersebagai gantinya:

import { HashRouter } from 'react-router-dom'

<HashRouter>
  <App/>
</HashRouter>

Referensi: HashRouter


Bisakah Anda memberikan detail mengapa HashRouter memperbaiki masalah ini? Tautan yang Anda berikan tidak menjelaskannya untuk saya. Juga, apakah ada cara untuk menyembunyikan hash di jalan? Saya menggunakan BrowserRouter tetapi mengalami masalah 404 dengannya ..
Ian Smith

29

Router dapat dipanggil dengan dua cara berbeda, tergantung pada apakah navigasi terjadi pada klien atau pada server. Anda mengonfigurasinya untuk operasi sisi klien. Parameter kunci adalah yang kedua untuk menjalankan metode , lokasi.

Ketika Anda menggunakan komponen React Router Link, itu memblokir navigasi browser dan memanggil transisi untuk melakukan navigasi sisi klien. Anda menggunakan HistoryLocation, sehingga ia menggunakan API riwayat HTML5 untuk menyelesaikan ilusi navigasi dengan mensimulasikan URL baru di bilah alamat. Jika Anda menggunakan browser lama, ini tidak akan berfungsi. Anda harus menggunakan komponen HashLocation.

Ketika Anda menekan refresh, Anda mem-bypass semua React dan React Router code. Server mendapatkan permintaan /joblistdan ia harus mengembalikan sesuatu. Di server Anda harus melewati jalur yang diminta ke runmetode agar dapat membuat tampilan yang benar. Anda dapat menggunakan peta rute yang sama, tetapi Anda mungkin perlu panggilan berbedaRouter.run . Seperti yang ditunjukkan Charles, Anda dapat menggunakan penulisan ulang URL untuk menangani hal ini. Opsi lain adalah menggunakan server node.js untuk menangani semua permintaan dan meneruskan nilai jalur sebagai argumen lokasi.

Dalam express, misalnya, mungkin terlihat seperti ini:

var app = express();

app.get('*', function (req, res) { // This wildcard method handles all requests

    Router.run(routes, req.path, function (Handler, state) {
        var element = React.createElement(Handler);
        var html = React.renderToString(element);
        res.render('main', { content: html });
    });
});

Perhatikan bahwa jalur permintaan sedang diteruskan ke run. Untuk melakukan ini, Anda harus memiliki mesin tampilan sisi-server tempat Anda dapat meneruskan HTML yang diberikan. Ada sejumlah pertimbangan lain yang menggunakan renderToStringdan dalam menjalankan Bereaksi di server. Setelah halaman diberikan di server, ketika aplikasi Anda memuat di klien, itu akan merender lagi, memperbarui HTML yang diberikan sisi-server sesuai kebutuhan.


2
permisi Permisi, bisakah Anda menjelaskan pernyataan berikutnya "ketika Anda menekan refresh, Anda mem-bypass semua React dan React Router code"? Mengapa itu terjadi?
VB_

React router biasanya digunakan untuk menangani berbagai 'jalur' di dalam browser saja. Ada dua cara khas melakukan ini: jalur hash gaya lama dan API riwayat yang lebih baru. Pembaruan browser akan membuat permintaan server, yang akan mem-bypass kode router reaksi sisi klien Anda. Anda harus menangani lintasan di server (tetapi hanya jika Anda menggunakan API riwayat). Anda dapat menggunakan router reaksi untuk ini, tetapi Anda harus melakukan sesuatu yang mirip dengan apa yang saya jelaskan di atas.
Todd

oke. Tetapi bagaimana jika server menggunakan bahasa non-JS (katakanlah Java)?
VB_

2
maaf ... maksudmu Anda memerlukan server ekspres untuk membuat rute yang "non root"? Saya pertama kali kagum bahwa definisi isomorfisme tidak termasuk data yang diambil dalam konsepnya (hal-hal yang sangat kompleks jika Anda ingin membuat tampilan DENGAN data server di samping, meskipun itu akan menjadi kebutuhan SEO yang jelas - dan isomorfisme mengklaimnya). Sekarang saya seperti "WTF bereaksi", serius ... Benar saya jika saya salah, apakah bara / sudut / tulang punggung memerlukan server untuk membuat rute? Saya benar-benar tidak mengerti persyaratan kembung ini untuk menggunakan rute
Ben

1
Apakah itu berarti refresh di router reaksi tidak berfungsi jika aplikasi reaksi saya bukan isomorfik?
Matt

28

Di index.html Anda head, tambahkan berikut ini:

<base href="/">
<!-- This must come before the css and javascripts -->

Kemudian ketika menjalankan dengan server dev webpack gunakan perintah ini.

webpack-dev-server --mode development --hot --inline --content-base=dist --history-api-fallback

--history-api-fallback adalah bagian yang penting


Ini bekerja dengan baik! Adakah tautan untuk membaca lebih lanjut tentang bagaimana Anda mendapatkan / menemukan solusi itu?
justDan

1
Maaf kawan Menemukannya melalui teknik coba-coba yang benar dan benar.
Efe Ariaroo

Perlu disebutkan bahwa jika Anda menggunakan basis href yang tidak /maka HashRoutertidak akan berfungsi (jika Anda menggunakan perutean hash)
Robbie Averill

Tag <base href = "/"> melakukannya untuk saya. Terima kasih untuk ini. Server Webpack dev terus mencoba mengambil bundel dari path / <bundle> dan gagal.
Malisbad

27

Saya menggunakan create-react-app untuk membuat situs web sekarang dan memiliki masalah yang sama disajikan di sini. Saya menggunakan BrowserRoutingdari react-router-dompaket. Saya menjalankan pada server Nginx dan apa yang dipecahkan untuk saya adalah menambahkan berikut ini/etc/nginx/yourconfig.conf

location / {
  if (!-e $request_filename){
    rewrite ^(.*)$ /index.html break;
  }
}

Yang sesuai dengan menambahkan berikut ini .htaccessjika Anda menjalankan Appache

Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]

Ini juga tampaknya menjadi solusi yang disarankan oleh Facebook sendiri dan dapat ditemukan di sini


1
Wow, solusi sederhana untuk nginx; bekerja seperti pesona, sebagai pengguna nginx baru. Cukup salin-tempel dan siapkan. +1 untuk menautkan ke dokumentasi fb resmi. Jalan untuk pergi.
user3773048

Sebagai catatan: Saya menggunakan AWS Instance dengan nginx dan App Angular. Ini bekerja.
Diego Sarmiento

kamu menyelamatkan aku. Saya mencoba untuk memperbaiki masalah saya dari kemarin saya lelah beberapa solusi tetapi saya gagal. akhirnya Anda membantu saya menyelesaikan masalah router ini. ah terima kasih banyak saudara
Sharifur Robin

Apakah diperlukan sesuatu yang berbeda untuk "react": "^ 16.8.6", "react-router": "^ 5.0.1"? Saya mencoba solusi ini (baik nginx dan apache) dan mereka tidak berfungsi.
Mark A. Tagliaferro

Karena Facebook belum mengubah dokumentasinya mengenai ini, saya hanya bisa berasumsi bahwa prosedurnya sama. Saya belum mencobanya dalam beberapa saat.
Aidin

17

Ini bisa menyelesaikan masalah Anda

Saya juga menghadapi masalah yang sama dalam aplikasi ReactJS dalam mode Produksi. Inilah 2 solusi untuk masalah ini.

1. Ubah riwayat perutean menjadi "hashHistory" sebagai ganti browserHistory sebagai ganti

<Router history={hashHistory} >
   <Route path="/home" component={Home} />
   <Route path="/aboutus" component={AboutUs} />
</Router>

Sekarang bangun aplikasi menggunakan perintah

sudo npm run build

Kemudian letakkan folder build di folder var / www / Anda, Sekarang aplikasi berfungsi dengan baik dengan menambahkan # tag di setiap url. Suka

localhost / # / home localhost / # / aboutus

Solusi 2: Tanpa tag # menggunakan browserHistory,

Tetapkan riwayat Anda = {browserHistory} di Router Anda, Sekarang buat dengan sudo npm run build.

Anda perlu membuat file "conf" untuk menyelesaikan halaman 404 tidak ditemukan, file conf harus seperti ini.

buka terminal Anda ketik perintah di bawah ini

cd / etc / apache2 / sites-available ls nano sample.conf Tambahkan konten di bawah ini di dalamnya.

<VirtualHost *:80>
    ServerAdmin admin@0.0.0.0
    ServerName 0.0.0.0
    ServerAlias 0.0.0.0
    DocumentRoot /var/www/html/

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    <Directory "/var/www/html/">
            Options Indexes FollowSymLinks
            AllowOverride all
            Require all granted
    </Directory>
</VirtualHost>

Sekarang Anda perlu mengaktifkan file sample.conf dengan menggunakan perintah berikut

cd /etc/apache2/sites-available
sudo a2ensite sample.conf

maka ia akan meminta Anda untuk memuat ulang server apache, menggunakan layanan sudo apache2 memuat ulang atau memulai kembali

kemudian buka folder localhost / build Anda dan tambahkan file .htaccess dengan konten di bawah ini.

   RewriteEngine On
   RewriteBase /
   RewriteCond %{REQUEST_FILENAME} !-f
   RewriteCond %{REQUEST_FILENAME} !-d
   RewriteCond %{REQUEST_FILENAME} !-l
   RewriteRule ^.*$ / [L,QSA]

Sekarang aplikasi berfungsi normal.

Catatan: ubah 0,0.0.0 ip ke alamat ip lokal Anda.

Jika ada keraguan tentang hal ini, jangan ragu untuk mengajukan komentar.

Saya harap ini membantu orang lain.


Apakah solusi pertama akan berfungsi "react-router-dom": "^5.1.2"?, Saya menggunakan<BrowserRouter>
151291

16

Jika Anda meng-hosting aplikasi reaksi melalui AWS Static S3 Hosting & CloudFront

Masalah ini muncul dengan sendirinya oleh CloudFront merespons dengan pesan 403 Akses Ditolak karena diharapkan ada / beberapa / lainnya / path ada di folder S3 saya, tetapi jalur itu hanya ada secara internal di React's routing dengan react-router.

Solusinya adalah menyiapkan aturan Halaman Kesalahan distribusi. Buka pengaturan CloudFront dan pilih distribusi Anda. Selanjutnya buka tab "Halaman Kesalahan". Klik "Buat Respons Kesalahan Kustom" dan tambahkan entri untuk 403 karena itulah kode status kesalahan yang kami dapatkan. Atur Path Halaman Respons ke /index.html dan kode status ke 200. Hasil akhirnya membuat saya takjub dengan kesederhanaannya. Halaman indeks dilayani, tetapi URL dipertahankan di browser, jadi setelah aplikasi bereaksi dimuat, ia akan mendeteksi jalur URL dan menavigasi ke rute yang diinginkan.

Halaman Kesalahan 403 Aturan


1
Inilah yang saya butuhkan. Terima kasih.
Jonathan

Jadi .. semua url Anda berfungsi, tetapi kembalikan kode status 403? Itu tidak benar. Kode status yang diharapkan harus dalam kisaran 2xx.
Stijn de Witt

@StijndeWitt Saya tidak yakin Anda mengerti dengan benar. CloudFront akan menangani semuanya - klien tidak akan melihat kode status 403. Perutean ulang terjadi di sisi server, merender halaman indeks, mempertahankan url, dan memberikan rute yang benar sebagai hasilnya.
th3morg

@ th3morg Ah ya saya mengerti sekarang. Saya melewatkan bahwa itu juga memungkinkan Anda untuk mengisi kode status respons sebagai 200. Jadi pada dasarnya Anda memetakan status 403 ke status 200 ... Terlihat baik-baik saja ... kecuali bahwa mungkin jika Anda mendapatkan 403 sebagai akibat karena beberapa alasan lain Anda tidak akan dapat melihatnya karena ini.
Stijn de Witt

1
terima kasih untuk ini @ th3morg. Apakah pengaturan ini juga berfungsi untuk jalur multi-level? Ini berfungsi baik bagi saya jika ada jalur di tingkat root, seperti domain / pendaftaran, domain / login, domain / <documentId>, tetapi tidak berfungsi untuk jalur multi-level, seperti domain / dokumen / <documentId>.
Pargles

16

Server Webpack Dev memiliki opsi untuk mengaktifkan ini. Buka package.jsondan tambahkan --history-api-fallback. Solusi ini bekerja untuk saya.

Reaksi-router-tutorial


3
Tidak apa-apa dengan webpack-dev-servertetapi bagaimana cara memperbaikinya untuk membangun produksi?
Hussain

12

Jika Anda meng-hosting aplikasi reaksi di IIS, cukup tambahkan file web.config yang berisi:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404" subStatusCode="-1" />
        <error statusCode="404" path="/" responseMode="ExecuteURL" />
    </httpErrors>
  </system.webServer>
</configuration>

Ini akan memberitahu server IIS untuk mengembalikan halaman utama ke klien alih-alih kesalahan 404 dan tidak perlu menggunakan riwayat hash.


terima kasih kawan !!!! ini bekerja!
Thanh Bao

12

Tambahkan ini ke webpack.config.js:

devServer: {
    historyApiFallback: true
}

Itu bagus untuk dev, tetapi tidak membantu membangun produksi.
KFunk

11

Tumpukan produksi: React, React Router v4, BrowswerRouter, Express, Nginx

1) Pengguna BrowserRouter untuk url cantik

// app.js

import { BrowserRouter as Router } from 'react-router-dom'

const App = () {
  render() {
    return (
        <Router>
           // your routes here
        </Router>
    )
  }
}

2) Tambahkan index.html ke semua permintaan yang tidak diketahui dengan menggunakan /*

// server.js

app.get('/*', function(req, res) {   
  res.sendFile(path.join(__dirname, 'path/to/your/index.html'), function(err) {
    if (err) {
      res.status(500).send(err)
    }
  })
})

3) bundel webpack dengan webpack -p

4) jalankan nodemon server.jsataunode server.js

EDIT: Anda mungkin ingin membiarkan nginx menangani ini di blok server dan mengabaikan langkah 2:

location / {
    try_files $uri /index.html;
}

mendapatkan jalan yang tidak terdefinisi
Goutham

@Goutham Di bagian atas file server.js Anda tambahkan import path from 'pathatauconst path = require('path')
Isaac Pak

langkah 2 (tangkap semua dengan /*) baru saja menyelamatkan hari saya. Terima kasih! :)
Atlas7

Ini berfungsi, untuk orang yang menggunakan redux dan peduli dengan router yang terhubung lihat contoh ini: github.com/supasate/connected-react-router/tree/master/examples/…
cjjenkinson

10

Jika Anda menggunakan Create React App:

Ada banyak jalan keluar dari masalah ini dengan solusi untuk banyak platform hosting utama yang dapat Anda temukan DI SINI di halaman Create React App. Sebagai contoh, saya menggunakan React Router v4 dan Netlify untuk kode frontend saya. Yang diperlukan hanyalah menambahkan 1 file ke folder publik saya ("_redirects") dan satu baris kode dalam file itu:

/*  /index.html  200

Sekarang situs web saya dengan benar membuat jalur seperti mysite.com/pricing ketika dimasukkan ke dalam browser atau ketika seseorang mengklik menyegarkan.


7

Coba tambahkan file ".htaccess" di dalam folder publik dengan kode di bawah ini.

RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]

RewriteRule ^ /index.html [L]  

Terima kasih, ini yang berhasil bagi saya di Fedora 28 dengan apache. Tidak ada aturan penulisan ulang di atas maupun yang ada di halaman CRA yang berfungsi untuk saya. Saya menambahkannya ke pengaturan host virtual alih-alih dalam file .htaccess terpisah.
Boerre

6

Jika Anda memiliki fallback ke index.html Anda, pastikan bahwa dalam file index.html Anda memilikinya:

<script>
  System.config({ baseURL: '/' });
</script>

Ini mungkin berbeda dari proyek ke proyek.


2
Tambahkan ini ke html Anda head: <base href = "/">
Efe Ariaroo

4

Jika Anda menggunakan firebase, Anda hanya perlu memastikan bahwa Anda memiliki properti penulisan ulang di file firebase.json di root aplikasi Anda (di bagian hosting).

Sebagai contoh:

{ 
  "hosting": {
    "rewrites": [{
      "source":"**",
      "destination": "/index.html"
    }]    
  }
}

Semoga ini bisa menyelamatkan orang lain dari frustrasi dan membuang waktu.

Selamat coding ...

Bacaan lebih lanjut tentang subjek:

https://firebase.google.com/docs/hosting/full-config#rewrites

Firebase CLI: "Konfigurasikan sebagai aplikasi satu halaman (tulis ulang semua url ke /index.html)"


Anda seorang legenda
Perniferous


3

Saya belum menggunakan rendering sisi server tetapi saya mengalami masalah yang sama dengan OP di mana Link tampaknya berfungsi dengan baik sebagian besar waktu tetapi gagal ketika saya memiliki parameter. Saya akan mendokumentasikan solusi saya di sini untuk melihat apakah itu membantu siapa pun.

JSX utama saya berisi ini:

<Route onEnter={requireLogin} path="detail/:id" component={ModelDetail} />

Ini berfungsi dengan baik untuk tautan yang cocok pertama tetapi ketika: id berubah <Link> ekspresi bersarang pada halaman detail model itu, url berubah di bilah browser tetapi konten halaman awalnya tidak berubah untuk mencerminkan model yang ditautkan.

Masalahnya adalah saya telah menggunakan props.params.iduntuk mengatur model componentDidMount. Komponen hanya dipasang sekali sehingga ini berarti bahwa model pertama adalah yang menempel pada halaman dan Tautan berikutnya mengubah alat peraga tetapi membiarkan halaman tampak tidak berubah.

Menyetel model dalam status komponen di kedua componentDidMountdan di componentWillReceiveProps( di mana didasarkan pada alat peraga berikutnya) memecahkan masalah dan konten halaman berubah untuk mencerminkan model yang diinginkan.


1
Mungkin lebih baik menggunakan konstruktor komponen (yang juga memiliki akses ke props) iso componentDidMountjika Anda ingin mencoba rendering sisi server. Karena componentDidMounthanya dipanggil di browser. Tujuannya adalah untuk melakukan hal-hal dengan DOM, seperti melampirkan pendengar acara ke bodydll yang tidak dapat Anda lakukan render.
Stijn de Witt

3

Topik ini agak lama dan sudah terpecahkan, tetapi saya ingin menyarankan Anda solusi yang sederhana, jelas, dan lebih baik. Ini berfungsi jika Anda menggunakan server web.

Setiap server web memiliki kemampuan untuk mengarahkan pengguna ke halaman kesalahan dalam hal http 404. Untuk mengatasi masalah ini, Anda perlu mengarahkan pengguna ke halaman indeks.

Jika Anda menggunakan server berbasis Java (kucing jantan atau server aplikasi java apa pun) solusinya bisa sebagai berikut:

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- WELCOME FILE LIST -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!-- ERROR PAGES DEFINITION -->
    <error-page>
        <error-code>404</error-code>
        <location>/index.jsp</location>
    </error-page>

</web-app>

Contoh:

  • DAPATKAN http://example.com/about
  • Server web melempar http 404 karena halaman ini tidak ada di sisi server
  • konfigurasi halaman kesalahan memberitahu server yang mengirim halaman index.jsp kembali ke pengguna
  • kemudian JS akan melakukan sisa pekerjaan di sisi klien karena url di sisi klien masih http://example.com/about .

Itu dia, tidak perlu lagi sihir :)


Ini luar biasa! Saya menggunakan server Wildfly 10.1 dan membuat pembaruan ini ke file web.xml saya, kecuali lokasi saya disetel ke '/' saja. Ini karena dalam kode reaksi saya, saya menggunakan riwayat browser seperti ini:const browserHistory = useRouterHistory(createHistory)({ basename: '/<appname>' });
Chuck L

1
Apakah Anda yakin ini tidak memengaruhi SEO? Anda memberikan status 404 ke halaman yang benar-benar ada. Pengguna mungkin tidak pernah menyadari hal ini, tetapi bot membayar banyak perhatian, bahkan sangat banyak, sehingga mereka tidak akan mengikis halaman Anda.
subharb

3

Jika Anda menggunakan Express atau kerangka kerja lain di backend, Anda dapat menambahkan konfigurasi yang sama seperti di bawah ini dan memeriksa jalur publik Webpack di konfigurasi, itu akan berfungsi dengan baik bahkan saat memuat ulang jika Anda menggunakan BrowserRouter

expressApp.get('/*', (request, response) => {
    response.sendFile(path.join(__dirname, '../public/index.html'));
});

ini adalah solusi paling sederhana. perhatikan rute ini harus pergi setelah rute lain karena ini adalah tangkapan semua
dcsan

3

Memperbaiki kesalahan "tidak bisa DAPATKAN / URL" saat menyegarkan atau saat memanggil URL secara langsung.

Konfigurasikan webpack.config.js Anda untuk mengharapkan tautan yang diberikan dengan rute seperti ini.

module.exports = {
  entry: './app/index.js',
  output: {
       path: path.join(__dirname, '/bundle'),
       filename: 'index_bundle.js',
       publicPath: '/'
  },

2

Saat saya menggunakan .Net Core MVC sesuatu seperti ini membantu saya:

    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            var url = Request.Path + Request.QueryString;
            return App(url);
        }

        [Route("App")]
        public IActionResult App(string url)
        {
            return View("/wwwroot/app/build/index.html");
        }
   }

Pada dasarnya di sisi MVC, semua rute yang tidak cocok akan jatuh ke Home/Indexseperti yang ditentukan startup.cs. Di Indexdalamnya dimungkinkan untuk mendapatkan url permintaan asli dan meneruskannya ke mana pun dibutuhkan.

startup.cs

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");

            routes.MapSpaFallbackRoute(
                name: "spa-fallback",
                defaults: new { controller = "Home", action = "Index" });
        });

Jika api web dipisahkan dari aplikasi, bagaimana Anda akan menangani ini?
dayanrr91

@ dayanrr91 jika proyek Anda memiliki web api, Anda tidak akan memerlukan routing sisi server. Dan reaksi-router Anda, saya percaya harus membaca url dan melakukan routing yang sesuai. Tidak ada yang harus memblokirnya
Elnoor

komentar Anda sangat dihargai, tapi kemudian saya punya pertanyaan, saya baru saja menambahkan HashRouter, tapi sekarang ketika saya memasukkan ke URL mana pun secara manual, itu akan mengarahkan ulang ke komponen rumah saya alih-alih mengarahkan ke rumah saya dan kemudian ke komponen yang saya coba untuk masuk, apakah ada solusi untuk itu?
dayanrr91

@ dayanrr91 benar-benar tidak tahu, belum pernah mencoba hashrouter tetapi saya pikir harus ada hubungannya dengan kode Anda. Cara kerja hashrouter harus serupa dengan browserrouter. Juga saya baru saja diuji di sini di kotak pasir ini, ia berfungsi dengan baik codesandbox.io/s/25okp1mny , menguji url 25okp1mny.codesandbox.io/#/roster/5
Elnoor

2

Jika Anda hosting di IIS; Menambahkan ini ke konfigurasi web saya memecahkan masalah saya

<httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
    <remove statusCode="500" subStatusCode="100" />
    <remove statusCode="500" subStatusCode="-1" />
    <remove statusCode="404" subStatusCode="-1" />
    <error statusCode="404" path="/" responseMode="ExecuteURL" />
    <error statusCode="500" prefixLanguageFilePath="" path="/error_500.asp" responseMode="ExecuteURL" />
    <error statusCode="500" subStatusCode="100" path="/error_500.asp" responseMode="ExecuteURL" />
</httpErrors>

Anda dapat membuat konfigurasi serupa untuk server lain


1

Dalam hal, siapa pun di sini mencari solusi pada Bereaksi JS SPA dengan Laravel. Jawaban yang diterima adalah penjelasan terbaik mengapa masalah seperti itu terjadi. Seperti yang sudah dijelaskan Anda harus mengkonfigurasi sisi klien dan sisi server. Dalam templat blade Anda, sertakan file yang dipaket js, pastikan untuk menggunakan URL facadeseperti ini

<script src="{{ URL::to('js/user/spa.js') }}"></script>

Di rute Anda, pastikan menambahkan ini ke titik akhir utama di mana templat blade berada. Sebagai contoh,

Route::get('/setting-alerts', function () {
   return view('user.set-alerts');
});

Di atas adalah titik akhir utama untuk templat blade. Sekarang tambahkan juga rute opsional,

Route::get('/setting-alerts/{spa?}', function () {
  return view('user.set-alerts');
});

Masalah yang terjadi adalah bahwa pertama templat blade dimuat, lalu router reaksi. Jadi, ketika Anda memuat '/setting-alerts', ia memuat html dan js. Tetapi ketika Anda memuat '/setting-alerts/about', itu pertama memuat di sisi server. Karena di sisi server, tidak ada apa pun di lokasi ini, ia mengembalikan tidak ditemukan. Ketika Anda memiliki router opsional itu, ia memuat halaman yang sama dan router reaksi juga dimuat, kemudian bereaksi loader memutuskan komponen mana yang akan ditampilkan. Semoga ini membantu.


Apakah mungkin memuat ke satu URI yang tepat ke server kemudian server membuat pengalihan ke Bereaksi? Seperti contoh, ketika saya memuat /user/{userID}, saya hanya perlu mengembalikan tampilan /userHTML universal , membawa ID dan menyebutnya menggunakan AJAX atau aksioma. Apakah mungkin untuk melakukan itu? apakah mereka SEO friendly?
Ryuujo

1

Bagi mereka yang menggunakan IIS 10, inilah yang harus Anda lakukan untuk memperbaikinya. Pastikan Anda menggunakan browserHistory dengan ini. Adapun referensi saya akan memberikan kode untuk routing, tetapi ini bukan yang penting, yang penting adalah langkah selanjutnya setelah kode komponen di bawah ini:

class App extends Component {
    render() {
        return (
            <Router history={browserHistory}>
                <div>
                    <Root>
                        <Switch>
                            <Route exact path={"/"} component={Home} />    
                            <Route path={"/home"} component={Home} />
                            <Route path={"/createnewproject"} component={CreateNewProject} />
                            <Route path={"/projects"} component={Projects} />
                            <Route path="*" component={NotFoundRoute} />
                        </Switch>
                    </Root>
                </div>
            </Router>
        )
    }
}
render (<App />, window.document.getElementById("app"));

Karena masalahnya adalah IIS menerima permintaan dari browser klien, itu akan menafsirkan URL seolah-olah meminta halaman, kemudian mengembalikan halaman 404 karena tidak ada halaman yang tersedia. Lakukan hal berikut:

  1. Buka IIS
  2. Buka Server lalu buka Folder Situs
  3. Klik situs web / aplikasi
  4. Buka Halaman Kesalahan
  5. Buka item status kesalahan 404 dalam daftar
  6. Alih-alih opsi "Sisipkan konten dari file statis ke respons kesalahan", ubah menjadi "Eksekusi URL di situs ini" dan tambahkan "/" nilai garis miring ke URL.

Dan sekarang akan berfungsi dengan baik.

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Saya harap ini membantu. :-)


1

Saya menggunakan WebPack, saya memiliki masalah yang sama Solusi => Dalam file server.js Anda

const express = require('express');
const app = express();

app.use(express.static(path.resolve(__dirname, '../dist')));
  app.get('*', function (req, res) {
    res.sendFile(path.resolve(__dirname, '../dist/index.html'));
    // res.end();
  });

Mengapa aplikasi saya tidak membuat setelah menyegarkan?


Ini berhasil untukku! Awalnya saya punya res.sendFile (path.JOIN (publicPath, "index.html")); Saya mengubah "gabung" menjadi "diselesaikan" seperti pada contoh di atas menjadi: res.sendFile (path.resolve ("./ dist", "index.html")); Saya juga bermain-main dengan __dirname tetapi tidak bisa benar-benar memahaminya atau membuatnya bekerja sehingga saya menyalin secara manual di "./dist" karena ini adalah tempat index.html saya dilayani. Saya juga menyatakan seperti sebelumnya: app.use (express.static ("./ dist"));
logixplayer

1

Menggunakan HashRouterbekerja untuk saya dengan Redux juga, cukup ganti:

import {
  Router //replace Router
} from "react-router-dom";

ReactDOM.render(
    <LocaleProvider locale={enUS}>
    <Provider store={Store}>
        <Router history={history}> //replace here saying Router
            <Layout/>
        </Router>
    </Provider>
</LocaleProvider>, document.getElementById("app"));
registerServiceWorker();

untuk:

import {
  HashRouter //replaced with HashRouter
} from "react-router-dom";

ReactDOM.render(
    <LocaleProvider locale={enUS}>
    <Provider store={Store}>
        <HashRouter history={history}> //replaced with HashRouter
            <Layout/>
        </HashRouter>
    </Provider>
</LocaleProvider>, document.getElementById("app"));
registerServiceWorker();

0

Saya memiliki masalah yang sama dan solusi ini bekerja untuk kami ..

Latar Belakang:

Kami meng-hosting beberapa aplikasi di server yang sama. Ketika kami akan menyegarkan server tidak akan mengerti ke mana harus mencari indeks kami di folder dist untuk aplikasi tertentu itu. Tautan di atas akan membawa Anda ke apa yang berhasil bagi kami ... Semoga ini bisa membantu, karena kami telah menghabiskan banyak waktu untuk mencari solusi untuk kebutuhan kami.

Kami menggunakan:

package.json

"dependencies": {
"babel-polyfill": "^6.23.0",
"ejs": "^2.5.6",
"express": "^4.15.2",
"prop-types": "^15.5.6",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-redux": "^5.0.4",
"react-router": "^3.0.2",
"react-router-redux": "^4.0.8",
"redux": "^3.6.0",
"redux-persist": "^4.6.0",
"redux-thunk": "^2.2.0",
"webpack": "^2.4.1"
}

webpack.config.js saya

webpack.config.js

/* eslint-disable */
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const babelPolyfill = require('babel-polyfill');
const HTMLWebpackPluginConfig = new HtmlWebpackPlugin({
  template: __dirname + '/app/views/index.html',
  filename: 'index.html',
  inject: 'body'
});

module.exports = {
  entry: [
    'babel-polyfill', './app/index.js'
  ],
  output: {
    path: __dirname + '/dist/your_app_name_here',
    filename: 'index_bundle.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      loader: 'babel-loader',
      query : {
          presets : ["env", "react", "stage-1"]
      },
      exclude: /node_modules/
    }]
  },
  plugins: [HTMLWebpackPluginConfig]
}

index.js saya

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import Routes from './Routes'
import { Provider } from 'react-redux'
import { createHistory } from 'history'
import { useRouterHistory } from 'react-router'
import configureStore from './store/configureStore'
import { syncHistoryWithStore } from 'react-router-redux'
import { persistStore } from 'redux-persist'

const store = configureStore();

const browserHistory = useRouterHistory(createHistory) ({
  basename: '/your_app_name_here'
})
const history = syncHistoryWithStore(browserHistory, store)

persistStore(store, {blacklist: ['routing']}, () => {
  console.log('rehydration complete')
})
// persistStore(store).purge()


ReactDOM.render(
    <Provider store={store}>
      <div>
        <Routes history={history} />
      </div>
    </Provider>,
  document.getElementById('mount')
)

app.js saya

var express = require('express');
var app = express();

app.use(express.static(__dirname + '/dist'));
// app.use(express.static(__dirname + '/app/assets'));
app.set('views', __dirname + '/dist/your_app_name_here');
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');

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

app.listen(8081, function () {
  console.log('MD listening on port 8081!');
});

0

Saya suka cara menanganinya. Coba tambahkan: yourSPAPageRoute / * di sisi server untuk menyingkirkan masalah ini.

Saya menggunakan pendekatan ini karena bahkan API Riwayat HTML5 asli tidak mendukung pengalihan yang benar pada refresh halaman (Sejauh yang saya tahu).

Catatan: Jawaban yang dipilih sudah membahas ini, tetapi saya mencoba untuk lebih spesifik.

Rute Ekspres

Tes - API Sejarah Diuji dan hanya ingin membagikan ini.

Semoga ini bisa membantu.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.