上传文件时,如何处理服务器端的验证错误



我有一个经过服务器端验证的表单,如果该表单包含无效项,则会将模型返回到View和JavaScript,然后突出显示无效字段。这是一个由多部分组成的表单,包含用于文件上载的输入类型文件。我使用的是Visual Studio 2017、ASP.Net Core 2.1和ASP.Net MVC设计模式。

问题:用户提交了表单并附加了一个文件,但由于一个或多个输入字段,表单无效。当在验证错误的情况下将模型传递回视图时,文件将不再附加,因为您无法从服务器填充输入类型文件(至少据我所知,我认为这是一个安全功能)。

所需行为:用户不必重新选择输入中的文件进行第二次上传。

当前解决方案:我在服务器上的Session中序列化并保存上载的文件,然后在Model上设置一个属性,"告诉"View文件保存在会话中,并删除要上载的输入类型文件,然后重新提交时,如果表单有效,则从Session中反序列化该文件,对该文件执行我需要执行的操作,并将其从Session中删除。

问题:有更好的处理方法吗?如果是,请提供示例文档或建议。

正如您所猜测的,出于安全原因,没有任何主流浏览器允许您访问用户选择的文件的实际路径。你的服务器代码不会收到真实的路径,甚至JavaScript也不允许访问它。相反,浏览器会生成一个假路径,其中唯一真正的值是文件名。

因此,在提交表格后,将文件保留在当前设计(即标准表格)中的唯一方法是按照您在问题中描述的方式进行。显然,这不是一个好方法,尤其是如果文件太大,那么每次往返服务器都需要很长时间。

要以不同的方式解决问题,您需要更改设计。您需要使浏览器保留真实的路径值。唯一的方法是不提交表单。因此,您需要在不提交表单的情况下提交表单值。我能想到的唯一方法是使用Ajax提交表单。这意味着服务器端验证的结果必须以Ajax响应的形式返回(通常是JSON,但如果您愿意,可以用其他方式序列化),然后JavaScript将接收该响应并根据需要在表单上显示错误。通过这种方式,每次文件仍然会重新提交到服务器,但至少服务器不需要像问题中描述的那样将其发送回客户端。这节省了一半的带宽,但需要更多的编程。如果文件可能很大,仍然不建议使用此选项。在这种情况下,最好让用户重新选择文件,而不是等待很长时间才能再次上传文件。

要将Ajax助手添加到.Net Core,您只需从项目GitHub下载JavaScript文件,并在HTML的标题中链接到它。然而,我更喜欢只在我需要的页面上链接到它,因为大多数页面都不需要它。我所做的是,首先为它创建一个局部视图:

<environment names="Development">
<script src="~/lib/jquery-ajax-unobtrusive/dist/jquery.unobtrusive-ajax.js"></script>
</environment>
<environment exclude="Development">
<script src="~/lib/jquery-ajax-unobtrusive/dist/jquery.unobtrusive-ajax.min.js"></script>
</environment>

如果您喜欢,可以使用CDN。我找不到CDN.jsdelivr.net以外的CDN,也找不到好的后备测试,所以我将asp-fallback-test留空:

<environment names="Development">
<script src="~/lib/jquery-ajax-unobtrusive/dist/jquery.unobtrusive-ajax.js"></script>
</environment>
<environment exclude="Development">
<script src="https://cdn.jsdelivr.net/npm/jquery-ajax-unobtrusive@3.2.6/dist/jquery.unobtrusive-ajax.min.js"
asp-fallback-src="~/lib/jquery-ajax-unobtrusive/dist/jquery.unobtrusive-ajax.min.js"
asp-fallback-test=""
crossorigin="anonymous"
integrity="sha256-PAC000yuHt78nszJ2RO0OiDMu/uLzPLRlYTk8J3AO10="></script>
</environment>

无论哪种方式,都可以随意调用此文件,比如_AjaxScriptsPartial.cshtml。现在,在您需要的视图中,添加如下:

@section Scripts {
@{ await Html.RenderPartialAsync("_AjaxScriptsPartial"); }
}

以下是关于如何使用它的一个很好的解释:在Razor Pages中使用Unobtrusive Ajax。

或者,您可以保留当前的设计,但不要序列化文件并将其发送回浏览器,而是将其保存在服务器上并将URL发送到浏览器。现在,在JavaScript中,将文件输入字段替换为一条简单的消息,如"file uploaded successfully"或任何其他方式,以向用户指示不需要重新选择文件。当表单重新提交并且所有服务器验证成功时,在服务器上获取物理文件。您的代码必须知道是上传文件还是从服务器获取文件(通过检查文件输入字段是否有文件)。它还需要知道在服务器上的何处获取它。这种方式适用于所有文件大小,因为文件只上传一次,但需要一些工作。

我不确定您正在进行的验证,但它可以移动到前端吗?通常,前端的验证更多的是为了用户体验。(例如,格式不正确,字段为空,字段中的数据类型不正确)。如果是这样的话,我会把它移到前端,如果不是,你可以把它留在后端。

当表单中的字段离开焦点时,您可以使用AJAX来验证它们。这可以保证他们提交表单时,表单中的字段始终是正确的。我发现前端和服务器端验证的健康组合效果最好,而不是所有东西都在后端。

下面的粗略示例在onblur方法触发时向控制器发送ajax请求以验证输入字段。如果通过验证,它会将边框设置为绿色或红色,如果有要显示的边框,则会显示一条消息。只要确保你的模型与你在请求中发送的内容相匹配:

JS:

<script>
function validate() {
var input = { input: $('#one').val() };
$.ajax({
url: 'ValidateTextBox/',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(input),
success: function (data) {
if (JSON.parse(data.Result)) {
$('#one').css('border', '5px solid green');
} else {
$('#one').css('border', '5px solid red');
}
$('#result').text(data.Message);
}
})
}
</script>
<input id="one" type="text" onblur="validate()"/>
<label id="result"></label>

Controller.cs

[HttpPost]
public ActionResult ValidateTextBox(Dummy data)
{
int result = 0;
if (!int.TryParse(data.input, out result)) {
return Json(new { Result = false, Message = "Please enter in numbers" });
}
return Json(new { Result = true, Message = "Success" });
}

型号.cs

public class Dummy
{
public string input { get; set; }
}

最新更新