React调用C# API上传FormData时fetch返回400错误的求助
问题分析与解决方案
1. 前端请求的Content-Type错误(400错误核心原因)
你的React代码里手动设置了错误的请求头:'multi-part/formdata',存在两个关键问题:
- 拼写错误:正确的格式是
multipart/form-data(连字符位置错误,且应为form-data而非formdata) - 手动设置该请求头会导致浏览器无法自动添加boundary分隔符,而multipart类型的表单请求必须依赖boundary才能让后端正确解析数据,这直接导致后端无法识别请求内容,返回400错误。
修复前端代码
直接移除手动设置的Content-Type请求头,浏览器会自动为FormData生成正确的multipart/form-data请求头,并附带合适的boundary:
const sendRequest = () => { var base = process.env.REACT_APP_API_URL; var data = new FormData(); data.append('cover', cover); data.append('file', file); data.append('title', title); data.append('metaData', meta); data.append('description', description); // 移除手动设置的Content-Type,由浏览器自动处理 fetch(base + "/Book", { method: 'POST', body: data }) .then(success => { console.log(success); console.log(data); window.location.reload() }) .catch(e => { console.log(e); console.log(data); }); }
2. 后端API的数据流复用问题(潜在功能bug)
你的后端代码中,将同一个MemoryStream同时传给了文档和封面的S3上传对象:在执行await file.CopyToAsync(stream)后,流的位置指针已经移到末尾,上传封面时这个流是空的,会导致封面文件上传失败。
修复后端代码
为文档和封面分别创建独立的MemoryStream,并重置流指针到开头:
[HttpPost] public async Task<IActionResult> CreateBook([FromForm]IFormFile cover, [FromForm]IFormFile file, [FromForm]string title, [FromForm]string metaData, [FromForm]string description, CancellationToken cancellationToken) { if (cover == null || file == null) { return BadRequest(); } // 处理文档文件流 await using var docStream = new MemoryStream(); await file.CopyToAsync(docStream); docStream.Position = 0; // 重置指针到流开头,确保上传时能读取完整内容 // 处理封面文件流 await using var coverStream = new MemoryStream(); await cover.CopyToAsync(coverStream); coverStream.Position = 0; var id = Guid.NewGuid(); var fileExt = Path.GetExtension(file.FileName); var coverExt = Path.GetExtension(cover.FileName); var docName = $"{id}{fileExt}"; var coverName = $"{id}{coverExt}"; var bookDoc = new S3Obj() { BucketName = Constants.bucketName, InputStream = docStream, Name = Constants.bookPath + docName }; var bookCover = new S3Obj() { BucketName = Constants.bucketName, InputStream = coverStream, Name = Constants.bookCoverPath + coverName // 修正:原代码误用了docName,改为coverName }; var cred = new Credentials() { AccessKey = Constants.accessKey, SecretKey = Constants.secretKey }; var docResponse = await StorageService.Singleton.UploadFileAsync(bookDoc, cred); var coverResponse = await StorageService.Singleton.UploadFileAsync(bookCover, cred); Console.WriteLine(docResponse.Message); Book book = new Book() { Id = id, Title = title, MetaData = metaData, Description = description, Cover = coverName, DocumentPath = docName }; return Ok(await Mediator.Send(new CreateBook.Command() { Book = book }, cancellationToken)); }
注:同时修正了封面路径的命名错误,原代码中封面路径使用了文档的文件名,会导致文件覆盖或路径错误。
3. 额外排查点
如果修复后仍有问题,可检查以下内容:
- 确认React端的
cover和file变量是有效的File对象(可在append前通过console.log(cover, file)验证) - 检查后端是否有请求大小限制,可在
Program.cs中配置调整:
builder.Services.Configure<FormOptions>(options => { options.MultipartBodyLengthLimit = 10 * 1024 * 1024; // 设置10MB的请求大小限制,可按需调整 });
内容的提问来源于stack exchange,提问作者Újfalusi Ábel
相关产品推荐
相关产品推荐

