简体   繁体   中英

What is a correct way to implement Fluent Validation for IFormFile

I'm struggling with implementation of validation for IFormFile. I'm using FluentValidation and here is my validator for 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);
        }
    }

The problem is than during request validator isn't called at all, it just skips it. Here is my api endpoint signatrue:

        [HttpPost("{productId:min(1)}")]
        public IActionResult Upload([FromRoute] int productId, IFormFile file)
        {
          // some code here
        }

In my project I have other validators (however they don't implement AbstractValidator with Interface type) and they are doing job. Is it even possible to validate type that implements interface?

The problem is than during request validator isn't called at all, it just skips it.

Update

In addition to the manual trigger method, we can indeed let PhotoValidator function trigger automatically after submit.

The premise is that we need to configure FluentValidation in the ConfigureServices method of startup as follow:

using FluentValidation;
using FluentValidation.AspNetCore;// this need FluentValidation.AspNetCore dll
//...
public void ConfigureServices(IServiceCollection services)
{
   // ...
   services.AddMvc().AddFluentValidation();
   services.AddTransient<IValidator<FileData>, PhotoValidator>();
 }

Here is a good reference document for your reference.

Besides, to verify the content of IFormFile , you need to put it into a class as an object to verify.

Here I created the FileData class and stored a field of IFormFile type as follows:

 public class FileData
    {
        public IFormFile file { get; set; }
    }

And, there are some details that need to be modified when verifying in PhotoValidator.

When the file is null , you need to add the precondition When(f => f.file != null) to the statement that validates the Length and FileName fields, otherwise an error will occur:

( Since you did not provide the relevant content of PhotoSettings , I will set _settings.MaxBytes to a fixed value here ):

 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);
        } 
    }

Here is the controller:

    [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();
    }

View:

@{
    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>

} 

Here is the test result:

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM