嵌套表单场景下Ajax文件上传500错误排查求助
问题
我的HTML页面包含多个标签页,全部嵌套在一个表单内,点击表单外的保存按钮时会序列化所有标签页的表单字段并提交到数据库。现在需要在其中一个标签页添加文件上传功能,要求上传后显示包含文件名的成功提示。
我创建了独立的上传表单(因为不能嵌套表单),通过jQuery的FormData发起Ajax请求,但运行时控制台报Failed to load resource 500错误,且控制器的SaveDoc方法未被命中。相关代码如下:
HTML标签页结构
<div id="TabBtns" class="col-12 p-0"> <button type="button" class="btn btn-secondary mt-3">Tab1 </button> <button type="button" class="btn btn-secondary mt-3">Tab2</button> <button type="button" class="btn btn-secondary mt-3">Tab3</button> ... </div>
上传表单代码
<div class="card mb-2"> <a class="collapsed card-link" data-toggle="collapse" id="uploadDocsLink" href="#Documents"> <div class="card-header"> Documents <em class="fa pull-right fa-chevron-up"></em> </div> </a> <div id="Documents" class="collapse"> <div class="card-body"> <input type="hidden" asp-for="@Model.UploadedBy" /> <input type="hidden" asp-for="@Model.Uri" /> <form id="docUploadForm"> <div class="col-12"> <div class="row"> <div class="col-sm-6 col-lg-4"> <label asp-for="@Model.documentViewModel.DocumentName">Document Name*</label> <input asp-for="@Model.documentViewModel.DocumentName" type="text" class="form-control" /> <span asp-validation-for="@Model.documentViewModel.DocumentName" class="text-danger field-validation-error"></span> </div> <div class="col-sm-6 col-lg-4"></div> <div class="col-sm-6 col-lg-4"> <label>Document</label> <div class="custom-file"> <input type="file" class="custom-file-input" asp-for="@Model.documentViewModel.FileName"> <span asp-validation-for="@Model.documentViewModel.FileName"></span> <label class="custom-file-label" for="FileName" id="lblFileName">Choose file</label> </div> </div> </div> </div> <div class="mt-2 mb-3"> <div class="col-12 text-right"> <button type="button" class="btn btn-primary float-right" id="UploadDoc" title="">Upload</button> <button type="button" class="btn btn-outline-primary mr-3" id="ClearDoc">Clear</button> </div> </div> <div class="mt-3"> <div class="col"> <div class="table-responsive"> <table id="tblfileDoc" class="table table-sm table-hover display" width="100%" cellspacing="0"> <thead> <tr> <th>Id</th> <th>Document Name</th> <th>Updated Date</th> <th class="text-center" width="100">Action</th> </tr> </thead> </table> </div> </div> </div> </form> </div> </div> </div>
jQuery代码
$('body').on('click', '#UploadDoc', function (e) { e.preventDefault(); var btnCaption = $(this).text(); var fileInput = $("#documentViewModel_FileName"); if (btnCaption == "Upload") { fileInput.rules("add", { required: true, messages: { required: "Please choose document to upload.", } }); } else { fileInput.rules("remove"); } var fileData = fileInput.prop('files')[0]; if (!fileData && btnCaption === "Upload") { toastr.error("Please choose a file first"); return; } var formData = new FormData(); if (fileData) { formData.append('file', fileData); } formData.append('DocumentName', $("#documentViewModel_DocumentName").val()); formData.append('DocumentType', $("#documentViewModel_DocumentType").val()); formData.append('UploadedBy', $("#UploadedBy").val()); $.ajax({ url: "/Controller/SaveDoc", // 替换为实际控制器名称,比如"/Home/SaveDoc" type: "POST", processData: false, contentType: false, // 新增核心配置 beforeSend: function () { $(".page-loader").css("display", "block"); }, data: formData, success: function (response) { $(".page-loader").css("display", "none"); if (response.Status === "OK") { // 显示带文件名的成功提示 toastr.success(`${response.Message}:${fileData.name}`); // 可在此添加表格刷新等后续逻辑 } else { toastr.error(response.Message); } }, error: function (jqXHR, textStatus, errorThrown) { $(".page-loader").css("display", "none"); if (jqXHR.status == 400) { LogoutForSessionExpiration(); } else { toastr.error("Error Occurred"); } } }); });
Controller代码
[HttpPost] public JsonResult SaveDoc([FromForm] DocumentViewModel documentViewModel) { JsonResponse jsonResponse; bool result; try { if (documentViewModel.DocumentId == Guid.Empty) { var file = Request.Form.Files[0]; var fileExtension = Path.GetExtension(file.FileName); documentViewModel.FileName = Path.GetFileNameWithoutExtension(file.FileName) + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + fileExtension; documentViewModel.DocumentByte = ConvertToBytes(file); var blob = _userService.UploadDocs(documentViewModel).Result; if (blob.Uri != null) { documentViewModel.Uri = blob.Uri.ToString(); result = _userService.AddDocs(documentViewModel); if (result) { jsonResponse = new JsonResponse(); jsonResponse.Status = ResponseStatus.OK.ToString(); jsonResponse.Message = "Document uploaded successfully"; return Json(jsonResponse); } else { jsonResponse = new JsonResponse(); jsonResponse.Status = ResponseStatus.ERROR.ToString(); jsonResponse.Message = "Error in uploading document"; return Json(jsonResponse); } } else { jsonResponse = new JsonResponse(); jsonResponse.Status = ResponseStatus.ERROR.ToString(); jsonResponse.Message = "Error in uploading document"; return Json(jsonResponse); } } else { if (Request.Form.Files.Count > 0) { var file = Request.Form.Files[0]; _userService.RemoveDocs(documentViewModel); var fileExtension = Path.GetExtension(file.FileName); documentViewModel.FileName = Path.GetFileNameWithoutExtension(file.FileName) + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + fileExtension; documentViewModel.DocumentByte = ConvertToBytes(file); var blob = _userService.UploadDocs(documentViewModel).Result; if (blob.Uri != null) { documentViewModel.Uri = blob.Uri.ToString(); result = _userService.UpdateDocs(documentViewModel, true); if (result) { jsonResponse = new JsonResponse(); jsonResponse.Status = ResponseStatus.OK.ToString(); jsonResponse.Message = "Document uploaded successfully"; return Json(jsonResponse); } else { jsonResponse = new JsonResponse(); jsonResponse.Status = ResponseStatus.ERROR.ToString(); jsonResponse.Message = "Error in uploading document"; return Json(jsonResponse); } } else { jsonResponse = new JsonResponse(); jsonResponse.Status = ResponseStatus.ERROR.ToString(); jsonResponse.Message = "Error in uploading document"; return Json(jsonResponse); } } else { result = _userService.UpdateDocs(documentViewModel, false); if (result) { jsonResponse = new JsonResponse(); jsonResponse.Status = ResponseStatus.OK.ToString(); jsonResponse.Message = "Signed doc. info updated successfully"; return Json(jsonResponse); } else { jsonResponse = new JsonResponse(); jsonResponse.Status = ResponseStatus.ERROR.ToString(); jsonResponse.Message = "Error in updating signed doc. info"; return Json(jsonResponse); } } } } catch (Exception ex) { _logger.LogError(ex.Message); jsonResponse = new JsonResponse(); jsonResponse.Status = ResponseStatus.ERROR.ToString(); jsonResponse.Message = "Error in uploading document"; return Json(jsonResponse); } } // 文件转字节数组的辅助方法 private byte[] ConvertToBytes(IFormFile file) { using (var memoryStream = new MemoryStream()) { file.CopyTo(memoryStream); return memoryStream.ToArray(); } }
ViewModel代码
using Microsoft.AspNetCore.Mvc.Rendering; using System; using System.Collections.Generic; using System.Text; namespace App { public class DocumentViewModel { public Guid DocumentId { get; set; } public string DocumentName { get; set; } public string DocumentType { get; set; } public string UploadedBy { get; set; } public string FileName { get; set; } public string Uri { get; set; } public byte[] DocumentByte { get; set; } } }
错误分析与解决方案
以下是导致500错误和控制器方法未命中的核心问题及修正:
- Ajax缺少核心配置:上传FormData必须添加
contentType: false,否则jQuery默认的内容类型会导致后端无法解析文件。 - 参数名不匹配:ASP.NET模型绑定区分大小写,且存在
UploadedBy取值的笔误,需修正FormData参数名与ViewModel属性一致,并修正取值来源。 - 缺少模型绑定特性:控制器方法参数需添加
[FromForm],明确告知从表单数据绑定模型。 - 重复ID问题:折叠链接与表单使用相同ID会导致DOM选择异常,需修改为唯一ID。
- 验证选择器错误:文件输入的实际ID是
documentViewModel_FileName,需修正验证规则的选择器。 - 变量名不一致:控制器中更新分支的变量名大小写不统一,需保持与方法参数一致。
内容的提问来源于stack exchange,提问作者shubhkr1
相关产品推荐
相关产品推荐

