当页面刷新返回后端 rails 的路由时,如何处理客户端路由?

How do I handle client side routes, when a page refresh returns the backend rails' route instead?

提问人:Cody Barker 提问时间:9/26/2023 更新时间:10/24/2023 访问量:51

问:

我已将 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。

JavaScript ReactJS Ruby-on-Rails 路由 部署

评论

0赞 Cody Barker 9/26/2023
好的,所以我认为这与我的一些后端路由和前端路由具有相同的路径有关,因此页面刷新会向该路径发出 GET 请求,这就是它返回 JSON 而不是组件的原因。
1赞 Cody Barker 9/26/2023
好吧,我想就是这样。将我的前端路由从 /projects 之类的更改为 /users/:id/projects 似乎可以解决这个问题。
0赞 Alex 9/27/2023
如果已解决问题,则应将解决方案作为答案发布

答:

0赞 Cody Barker 10/24/2023 #1

这个应用程序在 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 文件。