You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Rails项目中PATCH请求触发路由错误,寻求解决方案

问题:React+Rails项目中PATCH请求更新预订数据出现路由错误

开发React前端搭配Rails后端的项目时,发送PATCH请求更新预订数据时触发ActionController::RoutingError,提示找不到匹配[PATCH] "/reservation/2"的路由。以下是相关代码及日志,附解决方法:

错误日志

Started PATCH "/reservation/2" for 127.0.0.1 at 2022-09-01 00:32:51 +0530
  
ActionController::RoutingError (No route matches [PATCH] "/reservation/2"):

Started GET "/me" for 127.0.0.1 at 2022-09-01 00:32:51 +0530
Processing by UsersController#show as */*
hello
4
  User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 4], ["LIMIT", 1]]
  ↳ app/controllers/application_controller.rb:9:in `authorize'
[active_model_serializers] Rendered UserSerializer with ActiveModelSerializers::Adapter::Attributes (0.17ms)
Completed 200 OK in 2ms (Views: 0.4ms | ActiveRecord: 0.3ms | Allocations: 832)


Started GET "/reservations" for 127.0.0.1 at 2022-09-01 00:32:51 +0530
Processing by ReservationsController#index as */*
hello
4
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [[ "id", 4], ["LIMIT", 1]]
  ↳ app/controllers/application_controller.rb:9:in `authorize'
  Reservation Load (0.2ms)  SELECT "reservations".* FROM "reservations" WHERE "reservations"."user_id" = $1  [[ "user_id", 4]]
  ↳ app/controllers/reservations_controller.rb:4:in `index'
[active_model_serializers]   Restaurant Load (0.1ms)  SELECT "restaurants".* FROM "restaurants" WHERE "restaurants"."id" = $1 LIMIT $2  [[ "id", 30], ["LIMIT", 1]]
[active_model_serializers]   ↳ app/controllers/reservations_controller.rb:4:in `index'
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModelSerializers::Adapter::Attributes (1.53ms)
Completed 200 OK in 4ms (Views: 2.3ms | ActiveRecord: 0.5ms | Allocations: 2873)

Rails路由配置(routes.rb)

Rails.application.routes.draw do
  
  resources :reservations,only: [:index,:show,:create,:update,:destroy]
  resources :reviews,only: [:index,:create,:destroy]
  resources :restaurants,only: [:index]
 
  post "/signup", to: "users#create"
  get "/me", to: "users#show"
  post "/login", to: "sessions#create"
  delete "/logout", to: "sessions#destroy"
  
end

Reservations控制器代码

def update
  reservation = Reservation.find_by(id: params[:id])
  reservation.update!(reservation_params)
  render json: reservation,status: :ok
end

def reservation_params
  params.permit(:name, :date, :time, :num, :contact, :occasion,:user_id,:restaurant_id)
end

前端相关代码

App.js

function App() {
  const [user, setUser] = useState(null);
  
  const[reviews,setReviews]=useState([]);
  const[current,setCurrent]=useState({});
  useEffect(() => {
    document.title = "Nyc";
  }, []);

  useEffect(() => {
    // auto-login
    fetch("/me", { credentials: "same-origin" }).then((r) => {
      if (r.ok) {
        r.json().then((user) => setUser(user));
      }
    });
  }, [setUser]);
  function handleAddReviews(newReview) {
    setReviews([...reviews, newReview]);
  }

  
  if (!user) return <Loggin error={'please login'} onLogin={setUser} />;
  return (
    <Cont.Provider value={ {current,setCurrent}}>
    <div className="App">
   <Navbar user={user} setUser={setUser} />
   <Routes>
    <Route exact path="/blogs" element={<Blogs />} />
    
    <Route exact path="/myreservations" element={<MyReservations user={user} />} />

    <Route exact path="/restaurants/:id" element= {<RestaurantInfo handleAddReviews={handleAddReviews} user={user} reviews={reviews} setReviews={setReviews}/>}  />

    <Route exact path="/restaurants" element={<Restaurants />} />

     <Route exact path="/about" element={<About user={user} />} />

   </Routes>
   
   
    </div>
    </Cont.Provider>
  );
}

MyReservations.js

import {useEffect, useState } from "react";
import ReservationCard from "./ReservationCard";


function MyReservations({user}){
  // const[editMode,setEditForm]=useState(false);
  const[reservations,setReservations]=useState([]);
  useEffect(()=>{
    fetch("/reservations")
    .then(res=>res.json())
    .then(reservationData=>{
      setReservations(reservationData)
    })
  
   },[])
  
      // console.log("reservations",reservations)
      function handleUpdateReservation(updatedReservation) {
        const updatedReservations = reservations.map((reservation) => {
          if (reservation.id === updatedReservation.id) {
            return updatedReservation;
          } else {
            return reservation;
          }
        });
        setReservations(updatedReservations);
      }
    

      function handleCancel(reservationtodelete){
        const newReservations=reservations.filter(r=>r.id !== reservationtodelete)
         setReservations(newReservations)
         
       }
//  const renderReservations=reservations.map((reservation)=>(
//   <ReservationCard key={reservation.id} reservation={reservation} handleCancel={handleCancel}  />
//  ))

const renderReservations=reservations.map((reservation)=>(
  <ReservationCard key={reservation.id} reservation={reservation} handleCancel={handleCancel}  onUpdateReservation={handleUpdateReservation}  />
  ))
  
    return(
        <> 
        {renderReservations}
        </>
    )
}
export default MyReservations;

ReservationCard.js

function ReservationCard({reservation,handleCancel,onUpdateReservation}){
 
 
  const{ name, date, time, num, contact, occasion}=reservation;
  const [isEditing, setIsEditing] = useState(false);
  const handleReservationUpdate = (updatedReservation) => {
    setIsEditing(false);
    onUpdateReservation(updatedReservation);
  };

  function handleDeleteClick() {
    fetch(`/reservations/${reservation.id}`, {
     method: "DELETE",
       })
     handleCancel(reservation.id)
  }

  
    return(
        <>
        {isEditing ?( <EditReservationForm reservation={reservation}  onUpdateReservation={handleReservationUpdate} />) :(
           <div>
           <h2>{name}</h2>
           <h2>{date}</h2>
           <h2>{time}</h2>
           <h2>{num}</h2>
           <h2>{contact}</h2>
           <h2>{occasion}</h2>
           <h3>For-{reservation.restaurant.name}</h3>
           <button onClick={() => setIsEditing((isEditing) => !isEditing)}>
              Edit
            </button>
            <button onClick={handleDeleteClick}>Cancel Booking</button>
           </div>

        )}
       
       
      </>
    )
}
export default ReservationCard;

EditReservationForm.js

function EditReservationForm({reservation,onUpdateReservation}){
    const{ name, date, time, num, contact, occasion}=reservation;
    const[updateName,setUpdatedName]=useState(name);
   const[updateDate,setUpdatedDate]=useState(date);
   const[updateTime,setUpdatedTime]=useState(time);
   const[updateNum,setUpdatedNum]=useState(num);
   const[updateContact,setUpdatedContact]=useState(contact);
   const[updateOccasion,setUpdatedOccasion]=useState(occasion);

   function handleEditClick() {
      
  
            fetch(`/reservation/${reservation.id}`, {
              method: "PATCH",
            headers: {
                "Content-Type": "application/json",
              },
            body: JSON.stringify({ name:updateName,date:updateDate, time:updateTime,num:updateNum,contact:updateContact,occasion:updateOccasion}),
             })
               .then((r) => r.json())
              .then((updatedReservation) => {
                 onUpdateReservation(updatedReservation);
              });
       }
    return(
        <>
       <h2>Modify Reservation</h2>
       <form onSubmit={handleEditClick}  >
        <div >
        <label htmlFor="name"  >Name</label>
         <input type="text" name="name"  value={updateName}    onChange={(e) => setUpdatedName(e.target.value)} placeholder="name" />
       </div>
       <div >
        <label htmlFor="date"  >Date</label>
         <input type="date" name="date"   value={updateDate}  onChange={(e) => setUpdatedDate(e.target.value)}  placeholder="date" />
       </div>
       <div >
        <label htmlFor="time"  >Time</label>
         <input type="time" name="time"  value={updateTime}  onChange={(e) => setUpdatedTime(e.target.value)} placeholder="time" />
       </div>
       <div >
        <label htmlFor="num"  >Num</label>
         <input type="number" name="num"  value={updateNum}  onChange={(e) => setUpdatedNum(e.target.value)}  placeholder="num" />
       </div>
       <div >
        <label htmlFor="date"  >Contact</label>
         <input type="tel" name="contact" value={updateContact}  onChange={(e) => setUpdatedContact(e.target.value)}  placeholder="contact" />
       </div>
       <div >
        <label htmlFor="occasion"  >Occasion</label>
         <input type="text" name="occasion"  value={updateOccasion}   onChange={(e) => setUpdatedOccasion(e.target.value)} placeholder="occasion" />
       </div>
       <button type="submit">Update Reservation</button>
       </form>
        
        </>
    )
}
export default EditReservationForm;

解决方案

问题根源是前端EditReservationForm组件中的PATCH请求路径错误:当前使用单数形式/reservation/${reservation.id},而Rails路由配置的是复数资源reservations,正确路径应为复数形式/reservations/${reservation.id}。修改该路径后,路由错误即可解决。

内容的提问来源于stack exchange,提问作者codertraveller

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 18:46:17