提问人:Cody Barker 提问时间:9/26/2023 更新时间:10/24/2023 访问量:51
当页面刷新返回后端 rails 的路由时,如何处理客户端路由?
How do I handle client side routes, when a page refresh returns the backend rails' route instead?
问:
我已将 SPA 部署到 Render。它由一个带有 Redux 的 React 前端和一个 Rails 后端组成。当应用装载时,它会在浏览器中呈现正确的前端路由,并且在不刷新页面的情况下进行任何后续导航。但是,当我刷新页面或直接导航到 https://trakable.onrender.com/projects 等 URL 时,浏览器会从服务器返回 JSON 数据,而不是前端项目组件。
我以非常相似的方式设置了另一个部署的应用程序,没有这个问题。我有一个用于渲染索引.html文件的后备控制器,如下所示:
class FallbackController < ActionController::Base
def index
# React app index page
render file: 'public/index.html'
end
end
我在我的routes.rb文件中包含了一些路由逻辑,如下所示:
Rails.application.routes.draw do
post '/signup', to: 'users#create'
get '/me', to: 'users#show'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :teams
resources :users, only: [:index]
resources :tasks
resources :projects
# Routing logic: fallback requests for React Router.
# Leave this here to help deploy your app later!
get "*path", to: "fallback#index", constraints: ->(req) { !req.xhr? && req.format.html? }
end
我在 package.json 中为 http://localhost:3000 设置了一个代理,以便我的切片中的路由如下所示:
export const fetchUsers = createAsyncThunk("users/fetchUsers", () => {
return fetch("/users")
.then((r) =>r.json())
})
我的 database.yml 文件具有如下生产设置:
production:
<<: *default
url: <%= ENV['DATABASE_URL'] %>
我的应用组件如下所示:
import './styles/App.css';
import NavBar from './navbar/NavBar'
import Tasks from './tasks/Tasks'
import LoginPage from './users/LoginPage'
import Task from './tasks/Task'
import EditTask from './tasks/EditTask'
import Projects from './projects/Projects'
import Project from './projects/Project'
import Teams from './teams/Teams'
import Team from './teams/Team'
import { useEffect, useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { Routes, Route } from 'react-router-dom'
import { fetchCurrentUser, fetchUsers } from './users/usersSlice'
import { fetchProjects } from './projects/projectsSlice'
import { fetchTeams } from './teams/teamsSlice'
function App() {
const dispatch = useDispatch();
const [loading, setLoading] = useState(true);
const currentUser = useSelector((state) => state.users.currentUser);
console.log(currentUser);
// Initialize previousUserId to null
const [previousUserId, setPreviousUserId] = useState(null);
useEffect(() => {
dispatch(fetchCurrentUser())
.then(() => setLoading(false))
}, [dispatch]);
useEffect(() => {
// Check if the user has changed (new user login)
if (currentUser && currentUser.id !== previousUserId) {
// Dispatch fetch actions when a new user logs in
dispatch(fetchUsers());
dispatch(fetchProjects());
dispatch(fetchTeams());
// Update previousUserId to the current user's ID
setPreviousUserId(currentUser.id);
}
}, [dispatch, currentUser, previousUserId]);
if (loading) {
return <div></div>
}
if (!currentUser || currentUser.errors) {
return <LoginPage />
}
return (
<main>
<NavBar />
<Routes>
<Route
path="/"
element={<Tasks />}/>
<Route
path="/tasks/:id"
element={<Task />}
/>
<Route
path="/tasks/:id/edit"
element={<EditTask />}
/>
<Route
path="/projects"
element={<Projects />}
/>
<Route
path="/projects/:id"
element={<Project />}
/>
<Route
path="/teams"
element={<Teams />}
/>
<Route
path="/teams/:id"
element={<Team />}
/>
</Routes>
</main>
);
}
export default App;
我尝试在后端路由中添加命名空间和/或范围,但没有成功。它似乎阻止了浏览器返回 JSON 而不是前端组件,但它以一种破坏事物的方式改变了我的路由。例如,通过提交表单以在 https://trakable.onrender.com/tasks/3/edit 等页面上更新 Task,请求将发送到 https://trakable.onrender.com/tasks/3/api/tasks/3 之类的 URL,而不仅仅是 tasks/3。
答:
这个应用程序在 Rails 中运行 React,因此 Rails 将负责处理应用程序的所有路由逻辑。当不是 API 路由请求(即客户端路由)的请求时,我们需要有一个回退路由来呈现 index.html 文件。
确保您已创建fallback_controller:
class FallbackController < ActionController::Base
def index
# React app index page
render file: 'public/index.html'
end
end
然后,在 config/routes.rb 中,将此行作为第一个路由:
get "*path", to: "fallback#index", constraints: ->(req) { !req.xhr? && req.format.html? }
回退路由在 routes.rb 中排在第一位非常重要。否则,可以返回 API 路由而不是 index.html 文件。
评论