为 IFormFile 实现流利验证的正确方法是什么
What is a correct way to implement Fluent Validation for IFormFile
我正在努力实现 IFormFile 的验证。我正在使用 FluentValidation,这是我的 FormFile 验证器:
public class PhotoValidator : AbstractValidator<IFormFile>
{
private readonly PhotoSettings _settings;
public PhotoValidator(IOptionsSnapshot<PhotoSettings> snapshot)
{
_settings = snapshot.Value;
RuleFor(f => f).NotNull().WithMessage("Please Attach your photo");
RuleFor(f => f.Length).ExclusiveBetween(0, _settings.MaxBytes)
.WithMessage($"File length should be greater than 0 and less than {_settings.MaxBytes / 1024 / 1024} MB");
RuleFor(f => f.FileName).Must(HaveSupportedFileType);
}
private bool HaveSupportedFileType(string fileName)
{
return _settings.IsSupported(fileName);
}
}
问题是请求验证器根本没有被调用,它只是跳过了它。
这是我的 api 端点签名:
[HttpPost("{productId:min(1)}")]
public IActionResult Upload([FromRoute] int productId, IFormFile file)
{
// some code here
}
在我的项目中,我有其他验证器(但是它们没有实现接口类型的 AbstractValidator)并且它们正在工作。甚至可以验证实现接口的类型吗?
The problem is than during request validator isn't called at all, it
just skips it.
更新
除了手动触发的方法,我们确实可以让PhotoValidator
函数提交后自动触发
前提是我们需要在startup的ConfigureServices方法中配置FluentValidation如下:
using FluentValidation;
using FluentValidation.AspNetCore;// this need FluentValidation.AspNetCore dll
//...
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddMvc().AddFluentValidation();
services.AddTransient<IValidator<FileData>, PhotoValidator>();
}
这里有一个很好的参考document供大家参考
此外,要验证IFormFile
的内容,需要将其作为对象放入a class中进行验证。
这里我创建了FileData
class,存储了一个IFormFile类型的字段如下:
public class FileData
{
public IFormFile file { get; set; }
}
并且,在PhotoValidator中验证时,还有一些细节需要修改。
当文件为null时,需要在验证Length和FileName[=55=的语句中加入前置条件When(f => f.file != null)
]字段,否则会报错:
(由于你没有提供PhotoSettings
的相关内容,我这里将_settings.MaxBytes设为固定值):
public class PhotoValidator : AbstractValidator<FileData>
{
private readonly PhotoSettings _settings;
public PhotoValidator(/*PhotoSettings snapshot*/)
{
// _settings = snapshot;
_settings = new PhotoSettings() { MaxBytes = 2048000 };
RuleFor(f => f.file).NotNull().WithMessage("Please Attach your photo");
RuleFor(f => f.file.Length).ExclusiveBetween(0, _settings.MaxBytes)
.WithMessage($"File length should be greater than 0 and less than {_settings.MaxBytes / 1024 / 1024} MB")
.When(f => f.file != null);
RuleFor(f => f.file.FileName).Must(HaveSupportedFileType).When(f => f.file != null);
}
private bool HaveSupportedFileType(string fileName)
{
return _settings.IsSupported(fileName);
}
}
这是控制器:
[HttpPost("{productId}")]
public IActionResult Upload([FromRoute]int productId, FileData data)
{
string errorMessages = null;
if (!ModelState.IsValid)
{
errorMessages = ModelState.Where(x => x.Value.ValidationState == ModelValidationState.Invalid).FirstOrDefault()
.Value.Errors.FirstOrDefault().ErrorMessage;
return Content(errorMessages);//return error message to show in view
}
return Ok();
}
查看:
@{
ViewData["Title"] = "Upload";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Upload</h1>
<form enctype="multipart/form-data" method="post">
<div class="form-group">
productId: <input name="productId" type="text" class="form-control" />
</div>
<div class="form-group">
<input type="file" name="file" /><br />
<span class="text-danger" id="fileMessage"></span>
</div>
<input id="Submit1" type="submit" value="submit" />
</form>
@section Scripts{
<script>
$("form").submit(function () {
event.preventDefault();
var productId = $("input[name=productId]").val();
var fd = new FormData();
var files = $('input[name=file]')[0].files[0];
fd.append('file', files);
var data = {
"file": fd,
};
$.ajax({
type: "post",
url: "/" + productId,
data: fd,
contentType: false,
processData: false,
dataType: "html",
success: function (message) {
$("#fileMessage").html(message);
if (message == "") {
alert("upload Success!");
}
}
})
})
</script>
}
测试结果如下:
我正在努力实现 IFormFile 的验证。我正在使用 FluentValidation,这是我的 FormFile 验证器:
public class PhotoValidator : AbstractValidator<IFormFile>
{
private readonly PhotoSettings _settings;
public PhotoValidator(IOptionsSnapshot<PhotoSettings> snapshot)
{
_settings = snapshot.Value;
RuleFor(f => f).NotNull().WithMessage("Please Attach your photo");
RuleFor(f => f.Length).ExclusiveBetween(0, _settings.MaxBytes)
.WithMessage($"File length should be greater than 0 and less than {_settings.MaxBytes / 1024 / 1024} MB");
RuleFor(f => f.FileName).Must(HaveSupportedFileType);
}
private bool HaveSupportedFileType(string fileName)
{
return _settings.IsSupported(fileName);
}
}
问题是请求验证器根本没有被调用,它只是跳过了它。 这是我的 api 端点签名:
[HttpPost("{productId:min(1)}")]
public IActionResult Upload([FromRoute] int productId, IFormFile file)
{
// some code here
}
在我的项目中,我有其他验证器(但是它们没有实现接口类型的 AbstractValidator)并且它们正在工作。甚至可以验证实现接口的类型吗?
The problem is than during request validator isn't called at all, it just skips it.
更新
除了手动触发的方法,我们确实可以让PhotoValidator
函数提交后自动触发
前提是我们需要在startup的ConfigureServices方法中配置FluentValidation如下:
using FluentValidation;
using FluentValidation.AspNetCore;// this need FluentValidation.AspNetCore dll
//...
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddMvc().AddFluentValidation();
services.AddTransient<IValidator<FileData>, PhotoValidator>();
}
这里有一个很好的参考document供大家参考
此外,要验证IFormFile
的内容,需要将其作为对象放入a class中进行验证。
这里我创建了FileData
class,存储了一个IFormFile类型的字段如下:
public class FileData
{
public IFormFile file { get; set; }
}
并且,在PhotoValidator中验证时,还有一些细节需要修改。
当文件为null时,需要在验证Length和FileName[=55=的语句中加入前置条件When(f => f.file != null)
]字段,否则会报错:
(由于你没有提供PhotoSettings
的相关内容,我这里将_settings.MaxBytes设为固定值):
public class PhotoValidator : AbstractValidator<FileData>
{
private readonly PhotoSettings _settings;
public PhotoValidator(/*PhotoSettings snapshot*/)
{
// _settings = snapshot;
_settings = new PhotoSettings() { MaxBytes = 2048000 };
RuleFor(f => f.file).NotNull().WithMessage("Please Attach your photo");
RuleFor(f => f.file.Length).ExclusiveBetween(0, _settings.MaxBytes)
.WithMessage($"File length should be greater than 0 and less than {_settings.MaxBytes / 1024 / 1024} MB")
.When(f => f.file != null);
RuleFor(f => f.file.FileName).Must(HaveSupportedFileType).When(f => f.file != null);
}
private bool HaveSupportedFileType(string fileName)
{
return _settings.IsSupported(fileName);
}
}
这是控制器:
[HttpPost("{productId}")]
public IActionResult Upload([FromRoute]int productId, FileData data)
{
string errorMessages = null;
if (!ModelState.IsValid)
{
errorMessages = ModelState.Where(x => x.Value.ValidationState == ModelValidationState.Invalid).FirstOrDefault()
.Value.Errors.FirstOrDefault().ErrorMessage;
return Content(errorMessages);//return error message to show in view
}
return Ok();
}
查看:
@{
ViewData["Title"] = "Upload";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Upload</h1>
<form enctype="multipart/form-data" method="post">
<div class="form-group">
productId: <input name="productId" type="text" class="form-control" />
</div>
<div class="form-group">
<input type="file" name="file" /><br />
<span class="text-danger" id="fileMessage"></span>
</div>
<input id="Submit1" type="submit" value="submit" />
</form>
@section Scripts{
<script>
$("form").submit(function () {
event.preventDefault();
var productId = $("input[name=productId]").val();
var fd = new FormData();
var files = $('input[name=file]')[0].files[0];
fd.append('file', files);
var data = {
"file": fd,
};
$.ajax({
type: "post",
url: "/" + productId,
data: fd,
contentType: false,
processData: false,
dataType: "html",
success: function (message) {
$("#fileMessage").html(message);
if (message == "") {
alert("upload Success!");
}
}
})
})
</script>
}
测试结果如下: