上传文件时如何处理服务器端的验证错误
How to handle validation error(s) Server Side when a file is uploaded
我有一个经过服务器端验证的表单,如果表单包含无效项目,模型将返回到视图,然后 JavaScript 突出显示无效字段。这是一个包含用于文件上传的输入类型文件的多部分表单。我正在使用 Visual Studio 2017、ASP.Net Core 2.1 和 ASP.Net MVC 设计模式。
问题:用户提交表单并附加了一个文件,但由于一个或多个输入字段,表单无效。当将模型传回带有验证错误的视图时,该文件不再附加,因为您无法从服务器填充输入类型文件(至少我不知道,认为这是一项安全功能)。
期望的行为:用户无需重新select输入中的文件进行第二次上传。
当前解决方案:我将上传的文件序列化并保存在服务器的会话中,然后在模型上设置一个属性,它告诉视图文件是保存在会话中并删除输入类型文件以进行上传,然后在重新提交时,如果表单有效,将文件从会话中反序列化,对文件执行我需要执行的操作,然后将其从会话中删除。
问题:有更好的处理方法吗?如果是这样,请提供示例文档或建议。
我不确定你正在做的验证,但它可以移动到前端吗?通常在前端进行验证更多是为了用户体验。 (例如,格式不正确、字段留空、字段中的数据类型不正确)。如果是这样我会把它移到前端,如果不是你可以把它留在后端。
您可以在离开焦点时使用 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" });
}
Model.cs
public class Dummy
{
public string input { get; set; }
}
如您所料,出于安全原因,没有主流浏览器允许您访问用户选择的文件的实际路径。您的服务器代码将不会收到真实路径,甚至 JavaScript 也不允许访问它。相反,浏览器会生成一个假路径,其中唯一的真实值是文件名。
因此,在提交表单后将文件保留在当前设计(即标准表单)中的唯一方法是按照您在问题中描述的方式进行操作。显然,这不是一个好方法,尤其是如果文件太大,那么每次到服务器的往返都会花费很长时间。
要以不同的方式解决它,您需要更改设计。您需要让浏览器保留真实的路径值。唯一的方法是不提交表格。因此,您需要在不提交表单的情况下提交表单值。我能想到的唯一方法是使用 Ajax 提交表单。这意味着 server-side 验证的结果必须作为 Ajax 响应返回(通常是 JSON,但如果你愿意,可以用其他方式序列化)然后 JavaScript 将接受该响应并根据需要在表单上显示错误。这样,文件每次到服务器还是re-submitted,但至少服务器不需要像问题中描述的那样将它发送回客户端。这节省了一半的带宽,但需要更多的编程。如果文件可以大,还是不推荐。在这种情况下,让用户重新选择文件可能会更好,而不是等待很长时间才能再次上传文件。
要将 Ajax 助手添加到 .Net Core,您只需从 project GitHub 下载 JavaScript 文件,然后将 link 下载到 [=43] =] 你的 HTML。但是,我更喜欢在我需要它的页面上只 link 它,因为大多数页面不需要它。我做的是,首先为它创建一个局部视图:
<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"); }
}
这里有一个关于如何使用它的很好的解释:Using Unobtrusive Ajax In Razor Pages。
或者,您可以保留当前的设计,但不是序列化文件并将其发送回浏览器,而是将其保存在服务器上并将 URL 发送到浏览器。现在,在 JavaScript 中,将文件输入字段替换为 "File uploaded successfully" 之类的简单消息或任何其他方式来向用户指示文件不需要 re-selected。当表单为 re-submitted 并且所有服务器验证成功时,获取服务器上的物理文件。您的代码必须知道是上传文件还是从服务器获取文件(通过检查文件输入字段是否有文件)。它还需要知道在服务器上的何处获取它。这种方式适用于所有文件大小,因为文件只上传一次,但需要一些工作。
我有一个经过服务器端验证的表单,如果表单包含无效项目,模型将返回到视图,然后 JavaScript 突出显示无效字段。这是一个包含用于文件上传的输入类型文件的多部分表单。我正在使用 Visual Studio 2017、ASP.Net Core 2.1 和 ASP.Net MVC 设计模式。
问题:用户提交表单并附加了一个文件,但由于一个或多个输入字段,表单无效。当将模型传回带有验证错误的视图时,该文件不再附加,因为您无法从服务器填充输入类型文件(至少我不知道,认为这是一项安全功能)。
期望的行为:用户无需重新select输入中的文件进行第二次上传。
当前解决方案:我将上传的文件序列化并保存在服务器的会话中,然后在模型上设置一个属性,它告诉视图文件是保存在会话中并删除输入类型文件以进行上传,然后在重新提交时,如果表单有效,将文件从会话中反序列化,对文件执行我需要执行的操作,然后将其从会话中删除。
问题:有更好的处理方法吗?如果是这样,请提供示例文档或建议。
我不确定你正在做的验证,但它可以移动到前端吗?通常在前端进行验证更多是为了用户体验。 (例如,格式不正确、字段留空、字段中的数据类型不正确)。如果是这样我会把它移到前端,如果不是你可以把它留在后端。
您可以在离开焦点时使用 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" });
}
Model.cs
public class Dummy
{
public string input { get; set; }
}
如您所料,出于安全原因,没有主流浏览器允许您访问用户选择的文件的实际路径。您的服务器代码将不会收到真实路径,甚至 JavaScript 也不允许访问它。相反,浏览器会生成一个假路径,其中唯一的真实值是文件名。
因此,在提交表单后将文件保留在当前设计(即标准表单)中的唯一方法是按照您在问题中描述的方式进行操作。显然,这不是一个好方法,尤其是如果文件太大,那么每次到服务器的往返都会花费很长时间。
要以不同的方式解决它,您需要更改设计。您需要让浏览器保留真实的路径值。唯一的方法是不提交表格。因此,您需要在不提交表单的情况下提交表单值。我能想到的唯一方法是使用 Ajax 提交表单。这意味着 server-side 验证的结果必须作为 Ajax 响应返回(通常是 JSON,但如果你愿意,可以用其他方式序列化)然后 JavaScript 将接受该响应并根据需要在表单上显示错误。这样,文件每次到服务器还是re-submitted,但至少服务器不需要像问题中描述的那样将它发送回客户端。这节省了一半的带宽,但需要更多的编程。如果文件可以大,还是不推荐。在这种情况下,让用户重新选择文件可能会更好,而不是等待很长时间才能再次上传文件。
要将 Ajax 助手添加到 .Net Core,您只需从 project GitHub 下载 JavaScript 文件,然后将 link 下载到 [=43] =] 你的 HTML。但是,我更喜欢在我需要它的页面上只 link 它,因为大多数页面不需要它。我做的是,首先为它创建一个局部视图:
<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"); }
}
这里有一个关于如何使用它的很好的解释:Using Unobtrusive Ajax In Razor Pages。
或者,您可以保留当前的设计,但不是序列化文件并将其发送回浏览器,而是将其保存在服务器上并将 URL 发送到浏览器。现在,在 JavaScript 中,将文件输入字段替换为 "File uploaded successfully" 之类的简单消息或任何其他方式来向用户指示文件不需要 re-selected。当表单为 re-submitted 并且所有服务器验证成功时,获取服务器上的物理文件。您的代码必须知道是上传文件还是从服务器获取文件(通过检查文件输入字段是否有文件)。它还需要知道在服务器上的何处获取它。这种方式适用于所有文件大小,因为文件只上传一次,但需要一些工作。