繁体   English   中英

Asp Net Core,嵌套模型的自定义模型绑定不起作用

[英]Asp Net Core, Custom Model Binding for nested Models not working

简介:我想用未附加到Asp .Net的我自己的类替换IFormFile IFormFileCollection,因为我的视图模型位于带有poco类的不同项目中。 我的自定义类是ICommonFile,ICommonFileCollection,IFormFile(不是Asp .net核心类)和IFormFileCollection。

我将在这里分享:

ICommonFile.cs

/// <summary>
/// File with common Parameters including bytes
/// </summary>
public interface ICommonFile
{
    /// <summary>
    /// Stream File
    /// </summary>
    Stream File { get; }

    /// <summary>
    /// Name of the file
    /// </summary>
    string Name { get; }

    /// <summary>
    /// Gets the file name with extension.
    /// </summary>
    string FileName { get; }

    /// <summary>
    /// Gets the file length in bytes.
    /// </summary>
    long Length { get; }

    /// <summary>
    /// Copies the contents of the uploaded file to the <paramref name="target"/> stream.
    /// </summary>
    /// <param name="target">The stream to copy the file contents to.</param>
    void CopyTo(Stream target);

    /// <summary>
    /// Asynchronously copies the contents of the uploaded file to the <paramref name="target"/> stream.
    /// </summary>
    /// <param name="target">The stream to copy the file contents to.</param>
    /// <param name="cancellationToken">Enables cooperative cancellation between threads</param>
    Task CopyToAsync(Stream target, CancellationToken cancellationToken = default(CancellationToken));
}

ICommonFileCollection.cs

/// <inheritdoc />
/// <summary>
/// Represents the collection of files.
/// </summary>
public interface ICommonFileCollection : IReadOnlyList<ICommonFile>
{
    /// <summary>
    /// File Indexer by name
    /// </summary>
    /// <param name="name">File name index</param>
    /// <returns>File with related file name index</returns>
    ICommonFile this[string name] { get; }

    /// <summary>
    /// Gets file by name
    /// </summary>
    /// <param name="name">file name</param>
    /// <returns>File with related file name index</returns>
    ICommonFile GetFile(string name);

    /// <summary>
    /// Gets Files by name
    /// </summary>
    /// <param name="name"></param>>
    /// <returns>Files with related file name index</returns>
    IReadOnlyList<ICommonFile> GetFiles(string name);
}

IFormFile.cs

    /// <inheritdoc />
/// <summary>
/// File transferred by HttpProtocol, this is an independent
/// Asp.net core interface
/// </summary>
public interface IFormFile : ICommonFile
{
    /// <summary>
    /// Gets the raw Content-Type header of the uploaded file.
    /// </summary>
    string ContentType { get; }

    /// <summary>
    /// Gets the raw Content-Disposition header of the uploaded file.
    /// </summary>
    string ContentDisposition { get; }
}

IFormFileCollection.cs

/// <summary>
/// File Collection transferred by HttpProtocol, this is an independent
/// Asp.net core implementation
/// </summary>
public interface IFormFileCollection
{
    //Use it when you need to implement new features to Form File collection over HttpProtocol
}

我终于成功创建了我的模型活页夹,我也会分享它:

FormFileModelBinderProvider.cs

 /// <inheritdoc />
/// <summary>
/// Model Binder Provider, it inspects
/// any model when the request is triggered
/// </summary>
public class FormFileModelBinderProvider : IModelBinderProvider
{
    /// <inheritdoc />
    ///  <summary>
    ///  Inspects a Model for any CommonFile class or Collection with
    ///  same class if exist the FormFileModelBinder initiates
    ///  </summary>
    ///  <param name="context">Model provider context</param>
    ///  <returns>a new Instance o FormFileModelBinder if type is found otherwise null</returns>
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null) throw new ArgumentNullException(nameof(context));
        if (!context.Metadata.IsComplexType) return null;

        var isSingleCommonFile = IsSingleCommonFile(context.Metadata.ModelType);

        var isCommonFileCollection = IsCommonFileCollection(context.Metadata.ModelType);

        if (!isSingleCommonFile && !isCommonFileCollection) return null;

        return new FormFileModelBinder();
    }

    /// <summary>
    /// Checks if object type is a CommonFile Collection
    /// </summary>
    /// <param name="modelType">Context Meta data ModelType</param>
    /// <returns>If modelType is a collection of CommonFile returns true otherwise false</returns>
    private static bool IsCommonFileCollection(Type modelType)
    {
        if (typeof(ICommonFileCollection).IsAssignableFrom(modelType))
        {
            return true;
        }

        var hasCommonFileArguments = modelType.GetGenericArguments()
            .AsParallel().Any(t => typeof(ICommonFile).IsAssignableFrom(t));

        if (typeof(IEnumerable).IsAssignableFrom(modelType) && hasCommonFileArguments)
        {
            return true;
        }

        if (typeof(IAsyncEnumerable<object>).IsAssignableFrom(modelType) && hasCommonFileArguments)
        {
            return true;
        }

        return false;
    }

    /// <summary>
    /// Checks if object type is CommonFile or an implementation of ICommonFile
    /// </summary>
    /// <param name="modelType"></param>
    /// <returns></returns>
    private static bool IsSingleCommonFile(Type modelType)
    {
        if (modelType == typeof(ICommonFile) || modelType.GetInterfaces().Contains(typeof(ICommonFile)))
        {
            return true;
        }

        return false;
    }
}

FormFileModelBinder.cs

/// <inheritdoc />
/// <summary>
/// Form File Model binder
/// Parses the Form file object type to a commonFile
/// </summary>
public class FormFileModelBinder : IModelBinder
{
    /// <summary>
    /// Expression to map IFormFile object type to CommonFile
    /// </summary>
    private readonly Func<Microsoft.AspNetCore.Http.IFormFile, ICommonFile> _expression;

    /// <summary>
    /// FormFile Model binder constructor
    /// </summary>
    public FormFileModelBinder()
    {
        _expression = x => new CommonFile(x.OpenReadStream(), x.Length, x.Name, x.FileName);
    }

    /// <inheritdoc />
    ///  <summary>
    ///  It Binds IFormFile to Common file, getting the file
    ///  from the binding context
    ///  </summary>
    ///  <param name="bindingContext">Http Context</param>
    ///  <returns>Completed Task</returns>
    // TODO: Bind this context to ICommonFile or ICommonFileCollection object
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        dynamic model;
        if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));

        var formFiles = bindingContext.ActionContext.HttpContext.Request.Form.Files;

        if (!formFiles.Any()) return Task.CompletedTask;

        if (formFiles.Count > 1)
        {
            model = formFiles.AsParallel().Select(_expression);
        }
        else
        {
           model = new FormFileCollection();
           model.AddRange(filteredFiles.AsParallel().Select(_expression));
        }

        bindingContext.Result = ModelBindingResult.Success(model);
        return Task.CompletedTask;
    }
}

实际上,除了当我有嵌套模型时,其他所有东西都运行良好。 我分享了一个我正在使用的模型的示例,我将对一些可行的方案做一些评论,而不是Test.cs。

public class Test
{
    //It's Working
    public ICommonFileCollection Files { get; set; }

    //It's Working
    public ICommonFileCollection Files2 { get; set; }

    //This is a nested model
    public TestExtra TestExtra { get; set; }
}

TestExtra.cs

public class TestExtra
{
    //It's not working
    public ICommonFileCollection Files { get; set; }
}

实际上,当我向我的API发出请求时,我得到了以下内容(截图): Visual Studio调试

我也分享了我的邮递员请求的屏幕截图,以阐明我的请求是好的。 邮递员要求

如果有任何让嵌套模型起作用的主意,那就太好了。

Asp Net Core Model Binder不会仅将模型与一个属性绑定,如果您在一个类中具有一个属性,则该属性将为null,但是当您添加两个或多个属性时,它将绑定该属性。 我的错误是我在嵌套类中只有一个属性。 整个代码是正确的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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