サンプル
管理者画面からPostgreSQLへ登録する方法を備忘録として記録しておきます。
画面(Next.js)の実装
登録する際に下記のエラーが出たので覚えとして対象方法を記載します。
「ActionController::ParameterMissing (param is missing or the value is empty: adminuser
Did you mean? adminUserName):
app/controllers/adminusers_controller.rb:49:in adminuser_params'
app/controllers/adminusers_controller.rb:18:in create'
Started POST "/adminusers/create" for ::1 at 2025-03-30 19:55:15 +0900
ActionController::RoutingError (No route matches [POST] "/adminusers/create"):
Started POST "/adminUsers" for ::1 at 2025-03-30 19:58:04 +0900
ActionController::RoutingError (No route matches [POST] "/adminUsers"):
Started POST "/adminusers" for ::1 at 2025-03-30 20:00:39 +0900
Processing by AdminusersController#create as HTML
Parameters: {"adminUserName"=>"Anderson", "adminUserEmail"=>"anderson@example.com", "adminUserPhone"=>"00111222", "adminUserPassword"=>"[FILTERED]", "adminUserCompany"=>"ABC company", "adminUserDepartment"=>"ABC Depart"}
Completed 400 Bad Request in 1ms (ActiveRecord: 0.0ms | Allocations: 302)
ActionController::ParameterMissing (param is missing or the value is empty: adminuser
Did you mean? adminUserName):
app/controllers/adminusers_controller.rb:49:in adminuser_params'
app/controllers/adminusers_controller.rb:18:in create'」
具体的には、エラーメッセージに「param is missing or the value is empty: adminuser」と書かれており、params.require(:adminuser) が必要なパラメータを見つけられないという状況です。
Railsでは、params に渡すデータの名前が一致している必要がありますが、Next.js側の FormData では、adminUserName などのキーを渡しているため、Rails側での adminuser_params が適切に動作しません。Rails側では、params.require(:adminuser) を使って adminuser というキーを期待しているため、リクエストが一致しません。
解決策
Next.jsから送るパラメータ名を修正する: Rails側で adminuser_params が :adminuser を期待しているので、Next.js側でもその形式に合わせる必要があります。具体的には、FormData ではなく、JSON形式でデータを送信し、キー名を adminuser にラップする方法です。
Next.js側の変更: 現在、Next.jsで送信しているデータは、FormData として送信していますが、RailsはJSONを期待しているので、JSON形式でデータを送るように変更します。
'use client'
import { useState,useEffect } from "react"
import { useRouter } from "next/navigation"
import axios from "axios"
import { useForm } from "react-hook-form"
export default function AdminUserRegistration(){
const router = useRouter();
const defaultValues = {
adminUserName:'',
adminUserEmail:'',
adminUserPhone:'',
adminUserPassword:'',
adminUserCompany:'',
adminUserDepartment:''
};
const {register,handleSubmit,formState:{errors}} = useForm({
defaultValues
});
const [adminUserName,setAdminUserName] = useState('');
const [adminUserEmail,setAdminUserEmail] = useState('');
const [adminUserPhone,setAdminUserPhone] = useState('');
const [adminUserPassword,setAdminUserPassword] = useState('');
const [adminUserCompany,setAdminUserCompany] = useState('');
const [adminUserDepartment,setAdminUserDepartment] = useState('');
const onSubmit = async (data: any) => {
const formData = new FormData();
formData.append('adminUserName',adminUserName);
formData.append('adminUserEmail',adminUserEmail);
formData.append('adminUserPhone',adminUserPhone);
formData.append('adminUserPassword',adminUserPassword);
formData.append('adminUserCompany',adminUserCompany);
formData.append('adminUserDepartment',adminUserDepartment);
try {
const response = await axios.post('http://localhost:3000/adminusers', {
adminuser: { // adminuserというラッパーを使う
adminUserName: data.adminUserName,
adminUserEmail: data.adminUserEmail,
adminUserPhone: data.adminUserPhone,
adminUserPassword: data.adminUserPassword,
adminUserCompany: data.adminUserCompany,
adminUserDepartment: data.adminUserDepartment
}
}, {
headers: {
'Content-Type': 'application/json' // 明示的にJSON形式で送信
}
});
if (response.status === 200) {
alert('Admin User Registered successfully');
console.log(response.data);
} else {
console.log(response.data);
}
} catch (error) {
console.error(error);
alert('Error occurred while registering the admin user');
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label>Name</label>
<input
type="text"
className="w-full border border-gray-500 rounded-md"
defaultValue={defaultValues.adminUserName}
{...register('adminUserName',{
required:'AdminUserNamwe must be required.'
})}
onChange={(e)=>setAdminUserName(e.target.value)}
/>
<div className="text-rose-500">{errors.adminUserName?.message}</div>
</div>
<div>
<label>Email</label>
<input
type="email"
className="w-full border border-gray-500 rounded-md"
defaultValue={defaultValues.adminUserEmail}
{...register('adminUserEmail',{
required:'AdminuserEmail must be required.'
})}
onChange={(e)=>setAdminUserEmail(e.target.value)}
/>
<div className="text-rose-500">{errors.adminUserEmail?.message}</div>
</div>
<div>
<label>Phone</label>
<input
type="text"
className="w-full border border-gray-500 rounded-md"
defaultValue={defaultValues.adminUserPhone}
{...register('adminUserPhone',{
required:'AdminUserPhone must be required.'
})}
onChange={(e)=>{setAdminUserPhone(e.target.value)}}
/>
<div className="text-rose-500">{errors.adminUserPhone?.message}</div>
</div>
<div>
<label>Password</label>
<input
type="password"
className="w-full border border-gray-500 rounded-md"
defaultValue={defaultValues.adminUserPassword}
{...register('adminUserPassword',{
required:'AdminUserPassword must be required.'
})}
onChange={(e)=>setAdminUserPassword(e.target.value)}
/>
<div className="text-rose-500">{errors.adminUserPassword ?.message}</div>
</div>
<label>Company</label>
<input
type="text"
className="w-full border border-gray-500 rounded-md"
defaultValue={defaultValues.adminUserCompany}
{...register('adminUserCompany',{
required:'Company must be required.'
})}
onChange={(e)=>setAdminUserCompany(e.target.value)}
/>
<div className="text-rose-500">{errors.adminUserCompany?.message}</div>
<div>
<label>Department</label>
<input
type="text"
className="w-full border border-gray-500 rounded-md"
defaultValue={defaultValues.adminUserDepartment}
{...register('adminUserDepartment',{
required:'Departmanet muset be required.'
})}
onChange={(e)=>{setAdminUserDepartment(e.target.value)}}
/>
<div className="text-rose-500">{errors.adminUserDepartment?.message}</div>
</div>
<div>
<button
type="submit"
className="bg bg-blue-500 text-white rounded-md px-4 py-4"
>
Register
</button>
<button
type="button"
className="bg bg-green-500 text-white rounded-md px-4 py-4"
>
Back to Top
</button>
</div>
</form>
)
}
サーバ側(Ruby on Rails)の処理
bcrypt Gemのインストール
まず、bcrypt ジェムをインストールします。これを使用すると、パスワードを安全にハッシュ化できます。
Gemfileに以下を追加します。
gem 'bcrypt', '~> 3.1.7'
そして、次のコマンドでインストールします。
bundle install
class AdminusersController < ApplicationController
before_action :set_adminuser, only: %i[ show update destroy ]
# GET /adminusers
def index
@adminusers = Adminuser.all
render json: @adminusers
end
# GET /adminusers/1
def show
render json: @adminuser
end
# POST /adminusers
def create
@adminuser = Adminuser.new(adminuser_params)
#Generate hash password
@adminuser.adminUserPassword = BCrypt::Password.create(@adminuser.adminUserPassword)
if @adminuser.save
render json: @adminuser, status: :created, location: @adminuser
else
render json: @adminuser.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /adminusers/1
def update
if @adminuser.update(adminuser_params)
render json: @adminuser
else
render json: @adminuser.errors, status: :unprocessable_entity
end
end
# DELETE /adminusers/1
def destroy
@adminuser.destroy!
end
private
# Use callbacks to share common setup or constraints between actions.
def set_adminuser
@adminuser = Adminuser.find(params[:id])
end
# Only allow a list of trusted parameters through.
def adminuser_params
params.require(:adminuser).permit(:adminUserName, :adminUserEmail, :adminUserPhone, :adminUserPassword, :adminUserCompany, :adminUserDepartment)
end
end