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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 16:27:02