React-Redux调用Rails API注册用户时遭遇404错误求助
问题:React-Redux调用Rails API注册时出现POST 404错误
尝试在React-Redux应用中通过Rails API注册用户,并将用户信息存入localStorage用于后续登录,但调用注册接口时返回POST 404错误。确认使用的API端点是POST /api/v1/register/:name/:email/:password(.:format),但无法定位错误原因。
React-Redux的registerSlice代码
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; import axios from 'axios'; const initialState = { user: null, logged_in: false, error: null, }; export const register = createAsyncThunk( 'user/register', ({ email, name, password, setLoading, }) => { const result = axios .post( `https://dreamcars2.onrender.com/api/v1/register/${name}/${email}/${password}`, ) .then((response) => { localStorage.setItem('user', JSON.stringify(response.data)); window.location.reload(); }) .catch((error) => { setLoading(false); return { user: null, logged_in: false, error: error.response.data.error, }; }); return result; }, ); export const login = createAsyncThunk( 'user/login', ({ email, password, setLoading }) => { const result = axios .get(`https://dreamcars2.onrender.com/api/v1/login/${email}/${password}`) .then((response) => { localStorage.setItem('user', JSON.stringify(response.data)); window.location.reload(); return { user: true, logged_in: true, }; }) .catch((error) => { setLoading(false); return { user: null, logged_in: false, error: error.response.data.error, }; }); return result; }, ); export const registerSlice = createSlice({ name: 'register', initialState, reducers: { checkUser: () => { const user = JSON.parse(localStorage.getItem('user')); if (user) { return user; } return { user, logged_in: false, error: null, }; }, }, extraReducers: { [register.fulfilled]: (state, action) => ({ ...state, user: action.payload.user, logged_in: action.payload.logged_in, error: action.payload.error, }), [register.rejected]: (state, action) => ({ ...state, error: action.payload, }), [login.fulfilled]: (state, action) => ({ ...state, user: action.payload.user, logged_in: action.payload.logged_in, error: action.payload.error, }), [login.rejected]: (state, action) => ({ ...state, error: action.payload, }), }, }); export default registerSlice.reducer; export const { checkUser } = registerSlice.actions;
Rails Api::V1::UsersController代码
class Api::V1::UsersController < ApplicationController # before_action :authorize_request, except: %i[index create] # before_action :find_user, except: %i[create index] ALLOWED_DATA = %(name email password).freeze # GET /users def index users = User.all render json: users, status: :ok end # GET /users/:id def show render json: @user, status: :ok end # POST /users def create data = json_payload.select { |allow| ALLOWED_DATA.include?(allow) && allow != 'role' } return render json: { error: 'Empty body. Could not create user.' }, status: :unprocessable_entity if data.blank? user = User.new(data) if user.save render json: user, status: :ok else render json: { error: 'Could not create user.' }, status: :unprocessable_entity end end # PUT /users/:id def update if current_user.is? :admin data = json_payload.select { |allow| ALLOWED_DATA.include?(allow) } return render json: { error: 'Empty body. Could not update user.' }, status: :unprocessable_entity if data.blank? if @user.update(data) render json: @user, status: :ok else render json: { errors: @user.errors.full_messages }, status: :unprocessable_entity end else render json: { error: 'Unauthorized.' }, status: :unauthorized end end # DELETE /users/:id def destroy if current_user.is? :admin @user.destroy render json: @user, status: :ok else render json: { error: 'Unauthorized.' }, status: :unauthorized end end def register if User.find_by(email: params[:email].downcase) render json: { error: 'Useremail already exists! please choose another one.' }, status: :not_acceptable else @user = User.new(user_params) @user.email = @user.email.downcase if @user.save render json: { user: @user, logged_in: true }, status: :created else render json: { error: 'There was an error, please try again!' }, status: :internal_server_error end end end def login @user = User.find_by(email: params[:email].downcase) if @user render json: { user: @user, logged_in: true }, status: :ok else render json: { error: 'email is invalid.' }, status: :not_acceptable end end private def find_user @user = User.find_by_id!(params[:id]) rescue ActiveRecord::RecordNotFound render json: { errors: 'User not found' }, status: :not_found end end
补充的用户控制器代码片段
def register if User.find_by(email: params[:email].downcase) render json: { error: 'UserEmail already exists! please choose another one.' }, status: :not_acceptable else @user = User.new(user_params) @user.email = @user.email.downcase if @user.save render json: { user: @user, logged_in: true }, status: :created else render json: { error: 'There was an error, please try again!' }, status: :internal_server_error end end end def login @user = User.find_by(email: params[:email].downcase) if @user render json: { user: @user, logged_in: true }, status: :ok else render json: { error: 'email is invalid.' }, status: :not_acceptable end end private def user_params params.permit(:name, :email) end end
问题分析与解决方案
1. 404错误的核心原因:Rails路由未配置对应端点
你在前端调用的POST /api/v1/register/:name/:email/:password端点,在Rails的路由文件(config/routes.rb)中大概率没有配置匹配规则。需要添加对应的路由:
# config/routes.rb namespace :api do namespace :v1 do post 'register/:name/:email/:password', to: 'users#register' get 'login/:email/:password', to: 'users#login' # 其他已有路由... end end
不过更规范的做法是不要将敏感参数(如password)放在URL路径中,改用请求体传递,避免泄露风险。
2. 优化参数传递方式(推荐)
前端修改register请求:
axios.post( `https://dreamcars2.onrender.com/api/v1/register`, { name, email, password } // 将参数放在请求体 )
Rails路由修改:
# config/routes.rb namespace :api do namespace :v1 do post 'register', to: 'users#register' post 'login', to: 'users#login' # 登录用POST更安全,不要用GET resources :users end end
Rails控制器修改register方法:
def register if User.find_by(email: params[:email].downcase) render json: { error: '邮箱已存在,请更换其他邮箱。' }, status: :not_acceptable else @user = User.new(user_params) @user.email = @user.email.downcase if @user.save render json: { user: @user, logged_in: true }, status: :created else render json: { error: '注册失败,请重试!', details: @user.errors.full_messages }, status: :internal_server_error end end end private def user_params params.permit(:name, :email, :password) # 必须包含password,否则无法创建用户 end
3. 登录逻辑的问题修复
当前登录方法只检查邮箱存在,不验证密码,这是严重的安全问题:
def login @user = User.find_by(email: params[:email].downcase) # 使用Rails的authenticate方法验证密码(前提是User模型已添加has_secure_password) if @user && @user.authenticate(params[:password]) render json: { user: @user, logged_in: true }, status: :ok else render json: { error: '邮箱或密码错误。' }, status: :unauthorized end end
同时前端登录请求改为POST:
axios.post( `https://dreamcars2.onrender.com/api/v1/login`, { email, password } )
4. React-Redux异步逻辑优化
createAsyncThunk中直接返回axios promise即可,RTK会自动处理fulfilled/rejected状态,无需手动处理then/catch的返回值:
export const register = createAsyncThunk( 'user/register', async ({ email, name, password, setLoading }, { rejectWithValue }) => { try { const response = await axios.post( `https://dreamcars2.onrender.com/api/v1/register`, { name, email, password } ); localStorage.setItem('user', JSON.stringify(response.data)); window.location.reload(); return response.data; } catch (error) { setLoading(false); return rejectWithValue(error.response.data.error); } } );
对应的extraReducers调整为RTK推荐的builder模式:
extraReducers: (builder) => { builder .addCase(register.fulfilled, (state, action) => { state.user = action.payload.user; state.logged_in = action.payload.logged_in; state.error = null; }) .addCase(register.rejected, (state, action) => { state.error = action.payload; }) .addCase(login.fulfilled, (state, action) => { state.user = action.payload.user; state.logged_in = action.payload.logged_in; state.error = null; }) .addCase(login.rejected, (state, action) => { state.error = action.payload; }); }
内容的提问来源于stack exchange,提问作者Artur Gouveia
相关产品推荐
相关产品推荐

