簡體   English   中英

Asp .Net Core中的自定義模型綁定

[英]Custom Model Binding in Asp .Net Core

我正在嘗試將具有IFormFileIFormFileCollection屬性的模型綁定到我的自定義類CommonFile 我沒有在互聯網上找到這么多關於它使用asp .net核心的文檔,我試圖在ASP.Net Core 1.0中關注此鏈接自定義模型綁定,但它綁定了一個SimpleType屬性,我需要綁定一個復雜的類型。 無論如何,我試圖使我的這個綁定版本,我有以下代碼:

FormFileModelBinderProvider.cs

public class FormFileModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null) throw new ArgumentNullException(nameof(context));

        if (!context.Metadata.IsComplexType) return null;

        var isIEnumerableFormFiles = context.Metadata.ModelType.GetInterfaces().Contains(typeof(IEnumerable<CommonFile>));

        var isFormFile = context.Metadata.ModelType.IsAssignableFrom(typeof(CommonFile));

        if (!isFormFile && !isIEnumerableFormFiles) return null;

        var propertyBinders = context.Metadata.Properties.ToDictionary(property => property,
            context.CreateBinder);
        return new FormFileModelBinder(propertyBinders);
    }
}

FromFileModelBinder.cs

以下代碼不完整,因為我沒有使用bindingContext.ValueProvider.GetValue(bindingContext.ModelName);得到任何結果bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 雖然我正在調試一切順利,直到bindingContext.ModelName沒有值,我無法綁定我的模型從httpContext到強類型模型。

public class FormFileModelBinder : IModelBinder
{
    private readonly ComplexTypeModelBinder _baseBinder;

    public FormFileModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders)
    {
        _baseBinder = new ComplexTypeModelBinder(propertyBinders);
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {

        if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));

        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        return Task.CompletedTask;

   }
}

有什么建議么?

10個月后,我找到了一個我想做的解決方案。

總結:我想用我自己的沒有附加到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