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

Inertia React文章更新时上传文件触发必填字段验证错误

文章更新功能上传文件时必填字段验证失败问题解决

问题描述

开发文章更新功能时,仅修改标题或正文提交表单无异常,但上传缩略图并提交时,明明已填写标题和正文,却触发必填字段验证错误:

标题字段是必填的。
内容字段是必填的。

相关代码

Laravel ArticleController 代码

public function update(Request $request, Article $article)
{
    $request->validate([
        'title' => 'required',
        'body' => 'required',
        'thumbnail' => 'nullable|mimes:png,jpg,jpeg',
    ]);

    // 上传新缩略图时的处理
    if ($request->hasFile('thumbnail')) {
        $thumbnail = $request->file('thumbnail')->store('thumbnail');

        $article->update([
            'title' => $request->title,
            'body' => $request->body,
            'thumbnail' => $thumbnail
        ]);
    } else {
        // 无新缩略图时的处理
        $article->update([
            'title' => $request->title,
            'body' => $request->body,
        ]);
    }

    return redirect('/admin');
}

Inertia React 视图代码

import React, { useState } from "react";
import Navbar from "../components/Navbar";
import { Editor } from "@tinymce/tinymce-react";
import { useForm } from "@inertiajs/inertia-react";

export default function EditArticle({ article }) {
    const { data, setData, patch, errors, reset, processing } = useForm({
        title: article.title,
        body: article.body,
        thumbnail: null
    });

    const [isOpen, setIsOpen] = useState(false);

    function onSubmitEventHandler(e) {
        e.preventDefault();
        patch(`/articles/${article.id}/edit`, { onSuccess: () => reset() });
    }

    function openModalEventHandler() {
        setIsOpen(!isOpen);
    }

    function handleThumbnailChange(e) {
        setData('thumbnail', e.target.files[0]);
    }

    return (
        <>
            <Navbar />
            <form className="mt-24" onSubmit={onSubmitEventHandler} encType="multipart/form-data">
                {
                    isOpen &&
                    <>
                        <div className="fixed inset-0 flex items-center justify-center z-40">
                            <div className="p-4 rounded-lg flex flex-col bg-white text-black z-50 relative">
                                <div onClick={openModalEventHandler} className="hover:cursor-pointer absolute -top-2 -right-2 shadow-md p-2 bg-white  rounded-full">
                                    <img src="/icon/closeIcon.svg" className="w-4" alt="关闭图标" />
                                </div>
                                <h2 className="text-2xl font-semibold">编辑文章</h2>
                                <label htmlFor="title" className="mt-5 text-sm">标题:</label>
                                <input type="text" id="title" className="rounded-lg border py-2 px-3" value={data.title} onChange={(e) => setData('title', e.target.value)} />

                                <label htmlFor="thumbnail" className="mt-5 text-sm">缩略图:</label>
                                <input type="file" className="mt-2" id="thumbnail" onChange={handleThumbnailChange} />

                                <button type="submit" disabled={processing} className={`mt-4 py-2 px-4 bg-darkerBlue rounded-lg text-white ${processing && 'opacity-25'}`}>保存文章</button>
                                {errors.title &&
                                    <p className="mt-2 text-sm text-red-700">{errors.title}</p>
                                }
                                {errors.body &&
                                    <p className="mt-2 text-sm text-red-700">{errors.body}</p>
                                }
                                {errors.thumbnail &&
                                    <p className="mt-2 text-sm text-red-700">{errors.thumbnail}</p>
                                }
                            </div>
                            <div onClick={openModalEventHandler} className="fixed inset-0 bg-black opacity-35"></div>
                        </div>
                    </>
                }
                <Editor
                    apiKey='5d3gmavobfl1gplfh7eithg9chuj3shiru72msgp1kescjzi'
                    init={{
                        toolbar: 'undo redo | blocks fontfamily fontsize | bold italic underline strikethrough | table  | addcomment showcomments |  align lineheight | checklist numlist bullist indent outdent |  charmap | removeformat',
                        height: 520,
                    }}
                    value={data.body}
                    onEditorChange={(newValue, editor) => setData('body', newValue)}
                />

                <div onClick={openModalEventHandler} className="hover:bg-gray-200 hover:cursor-pointer bg-white transition fixed right-3 bottom-5 rounded-full p-4 card-shadow">
                    保存
                </div>
            </form>
        </>
    )
}

问题原因

Inertia的patch方法默认以JSON格式提交数据,当表单包含文件时,JSON格式无法正确传递文件,同时后端无法解析常规JSON中的表单字段,导致title和body字段未被正确接收,触发必填验证失败。

解决方案

修改React组件中的提交函数,添加multipart: true选项,让Inertia自动以FormData格式提交包含文件的表单数据:

function onSubmitEventHandler(e) {
    e.preventDefault();
    patch(`/articles/${article.id}/edit`, { 
        onSuccess: () => reset(),
        multipart: true // 启用多部分表单数据提交
    });
}

添加该选项后,Inertia会自动处理表单数据的格式转换,确保文件和其他字段都能被后端正确接收,从而避免必填字段的错误验证。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 03:13:12