我有一个多部分的表单,我正在使用formData发送一个'PUT'请求的数据.我得到了错误Cast to objectId failed for value 'undefined' (type string) at path '_id' for model 'Blogpost',当我在控制器上console.logreq.params.id时,它返回undefined. 我使用react-hook-form,redux-toolkit和multer-cloudinary来上传图片.

我的前端是:

const BlogpostEdit = () => {
  const { id } = useParams()

  const {
    data: blogpostData,
    isLoading,
    refetch,
    error
  } = useGetBlogpostDetailsQuery(id)

  const [updateBlogpost, { isLoading: loadingUpdate }] =
    useUpdateBlogpostMutation()

  useEffect(() => {
    if (blogpostData) {
      setValue('image', blogpostData.image)
      setValue('title', blogpostData.title)
      setValue('subtitle', blogpostData.subtitle)
      setValue('content', blogpostData.content)
      setValue('category', blogpostData.category)
    }
  }, [blogpostData])

  const {
    register,
    setValue,
    handleSubmit,
    formState: { errors }
  } = useForm()

  const onFormSubmit = async data => {
    if (data.image[0]) {
      const formData = new FormData()
      formData.append('_id', id)
      formData.append('image', data.image[0])
      data = { ...data, image: data.image[0].name }
      formData.append('image', data.image)
      formData.append('title', data.title)
      formData.append('subtitle', data.subtitle)
      formData.append('content', data.content)
      formData.append('category', data.category)
      try {
        const response = await updateBlogpost(formData).unwrap()
        toast.success('Blogpost has been updated')
        refetch()
      } catch (err) {
        toast.error(err?.data?.message || err.error)
      }
    } else {
      try {
        const response = await updateBlogpost({ ...data, _id: id }).unwrap()
        toast.success('Blogpost has been updated')
        refetch()
      } catch (err) {
        toast.error(err?.data?.message || err.error)
      }
    }
  }

  return (
    <FormContainer>
      {loadingUpdate && <Loader />}

      {isLoading ? (
        <Loader />
      ) : error ? (
        <p>{error.data.message}</p>
      ) : (
        <>
          <img src={blogpostData.image.url} style={{ width: '150px' }} />
          <form onSubmit={handleSubmit(onFormSubmit)}>
            <label htmlFor='image' name='image'>
              image
            </label>
            <input type='file' {...register('image')} />
            <p>{errors.image?.message}</p>
            <label htmlFor='title' name='title'>
              Title
            </label>
            <input type='text' {...register('title')} />
            <p>{errors.title?.message}</p>
            <label htmlFor='subtitle' name='subtitle'>
              Subtitle
            </label>
            <input type='text' {...register('subtitle')} />
            <p>{errors.subtitle?.message}</p>
            <label htmlFor='content' name='content'>
              Content
            </label>
            <textarea
              rows='10'
              cols='100'
              type='text'
              {...register('content')}
            />
            <p>{errors.content?.message}</p>
            <label htmlFor='category'>Choose a category:</label>
            <select name='category' {...register('category')}>
              <option value=''></option>
              <option value='game'>Game</option>
              <option value='tv'>TV</option>
              <option value='anime'>Anime</option>
              <option value='book'>Book</option>
            </select>
            <p>{errors.category?.message}</p>
            <button type='submit'>Submit</button>
          </form>
        </>
      )}
    </FormContainer>
  )
}

export default BlogpostEdit

切片/Mutations 是:

updateBlogpost: builder.mutation({
      query: (data) => ({
        url: `${BLOGPOSTS_URL}/${data._id}`,
        method: 'PUT',
        body: data,
      }),
      invalidatesTags: ['Blogposts'],
    }),

后端控制器:

const updateBlogpost = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const blogpost = await Blogpost.findByIdAndUpdate(id, { ...req.body });
  if (req.file) {
    blogpost.image.url = req.file.path;
    blogpost.image.filename = req.file.filename;
  }
  const updatedBlogpost = await blogpost.save();
  if (updatedBlogpost) {
    res.status(200).json(updatedBlogpost);
  } else {
    res.status(404);
    throw new Error('Resouce not found');
  }
});

路由:

router
  .route('/:id')
  .put(registered, admin, upload.single('image'), updateBlogpost);

提交表格的数据看起来像这样:

log from data

和formData:

enter image description here

这是控制台上的错误:

enter image description here

try/catch的错误:

enter image description here

在控制器上,我得到了req.body,图像成功上传,但req.params.id未定义.如果我不添加一个图像,只是编辑标题,例如,一切工作.当我想包含图像时,会发生这些错误.我在"POST"请求上使用了完全相同的代码,它工作得很好. 现在我知道我可以先上传文件使用不同的上传路径和不同的路径来更新模型,但我就是不知道为什么会发生这些错误.

推荐答案

您正在向mutation端点传递一个FormData对象,它是101,就像普通JavaScript对象一样,您可以简单地从它中解构属性.您应该使用访问器来访问特定的表单数据值.

const formData = new FormData();
formData.append("_id", "1234");

console.log(formData._id); // undefined
console.log(formData.get("_id")); // "1234"

更新端点以正确访问id表单数据.

updateBlogpost: builder.mutation({
  query: (data) => ({
    url: `${BLOGPOSTS_URL}/${data.get("_id")}`,
    method: 'PUT',
    body: data,
  }),
  invalidatesTags: ['Blogposts'],
}),

Javascript相关问答推荐

为什么在获取回调内设置状态(不会)会导致无限循环?

如何避免移动设备中出现虚假调整大小事件?

没有输出到带有chrome.Devtools扩展的控制台

模块与独立组件

如何解决CORS政策的问题

cypress中e2e测试上的Click()事件在Switch Element Plus组件上使用时不起作用

获取Uint8ClampedArray中像素数组的宽度/高度

用JS从平面文件构建树形 struct 的JSON

如何在不影响隐式类型的情况下将类型分配给对象?

当标题被点击时,如何使内容出现在另一个div上?

打字脚本中方括号符号属性访问和拾取实用程序的区别

JS Animate()方法未按预期工作

Phaser3 preFX addGlow不支持zoom

有没有办法更改Chart.js 3.x.x中主要刻度的字体?

AG-GRIDreact 显示布尔值而不是复选框

调用特定数组索引时,为什么类型脚本不判断未定义

递增/递减按钮React.js/Redux

Plotly.js栏为x轴栏添加辅助文本

找不到处于状态的联系人

使用onClick单击子元素时,使用交点观察器的关键帧动画意外运行