From 123db2cfa1df38ff14e7f45414093f24f142fec2 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 21 Dec 2021 13:55:28 +0800 Subject: [PATCH] Enhance `AbpRemoteStreamContentModelBinder` class. --- .../AbpRemoteStreamContentModelBinder.cs | 54 ++++++++++++++++--- ...pRemoteStreamContentModelBinderProvider.cs | 12 +++-- .../PersonAppServiceClientProxy_Tests.cs | 2 +- .../Application/Dto/CreateFileInput.cs | 2 +- .../Dto/CreateMultipleFileInput.cs | 2 +- 5 files changed, 58 insertions(+), 14 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinder.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinder.cs index 2b1fc8c125..0ce59fd5da 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinder.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinder.cs @@ -8,7 +8,8 @@ using Volo.Abp.Content; namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters { - public class AbpRemoteStreamContentModelBinder : IModelBinder + public class AbpRemoteStreamContentModelBinder : IModelBinder + where TRemoteStreamContent: class, IRemoteStreamContent { public async Task BindModelAsync(ModelBindingContext bindingContext) { @@ -17,7 +18,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters throw new ArgumentNullException(nameof(bindingContext)); } - var postedFiles = new List(); + var postedFiles = GetCompatibleCollection(bindingContext); // If we're at the top level, then use the FieldName (parameter or property name). // This handles the fact that there will be nothing in the ValueProviders for this parameter @@ -42,7 +43,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters } object value; - if (bindingContext.ModelType == typeof(IRemoteStreamContent) || bindingContext.ModelType == typeof(RemoteStreamContent)) + if (bindingContext.ModelType == typeof(TRemoteStreamContent)) { if (postedFiles.Count == 0) { @@ -63,7 +64,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters // Perform any final type mangling needed. var modelType = bindingContext.ModelType; - if (modelType == typeof(IRemoteStreamContent[]) || modelType == typeof(RemoteStreamContent[])) + if (modelType == typeof(TRemoteStreamContent[])) { value = postedFiles.ToArray(); } @@ -91,7 +92,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters private async Task GetFormFilesAsync( string modelName, ModelBindingContext bindingContext, - ICollection postedFiles) + ICollection postedFiles) { var request = bindingContext.HttpContext.Request; if (request.HasFormContentType) @@ -108,14 +109,53 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters if (file.Name.Equals(modelName, StringComparison.OrdinalIgnoreCase)) { - postedFiles.Add(new RemoteStreamContent(file.OpenReadStream(), file.FileName, file.ContentType, file.Length)); + postedFiles.Add(new RemoteStreamContent(file.OpenReadStream(), file.FileName, file.ContentType, file.Length).As()); } } } else if (bindingContext.IsTopLevelObject) { - postedFiles.Add(new RemoteStreamContent(request.Body, null, request.ContentType, request.ContentLength)); + postedFiles.Add(new RemoteStreamContent(request.Body, null, request.ContentType, request.ContentLength).As()); } } + + private static ICollection GetCompatibleCollection(ModelBindingContext bindingContext) + { + var model = bindingContext.Model; + var modelType = bindingContext.ModelType; + + // There's a limited set of collection types we can create here. + // + // For the simple cases: Choose List if the destination type supports it (at least as an intermediary). + // + // For more complex cases: If the destination type is a class that implements ICollection, then activate + // an instance and return that. + // + // Otherwise just give up. + if (typeof(T).IsAssignableFrom(modelType)) + { + return new List(); + } + + if (modelType == typeof(T[])) + { + return new List(); + } + + // Does collection exist and can it be reused? + if (model is ICollection collection && !collection.IsReadOnly) + { + collection.Clear(); + + return collection; + } + + if (modelType.IsAssignableFrom(typeof(List))) + { + return new List(); + } + + return (ICollection)Activator.CreateInstance(modelType); + } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinderProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinderProvider.cs index 6c2e106c57..fdecec5c27 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinderProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/AbpRemoteStreamContentModelBinderProvider.cs @@ -14,12 +14,16 @@ namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters throw new ArgumentNullException(nameof(context)); } - if (context.Metadata.ModelType == typeof(IRemoteStreamContent) || - context.Metadata.ModelType == typeof(RemoteStreamContent) || - typeof(IEnumerable).IsAssignableFrom(context.Metadata.ModelType) || + if (context.Metadata.ModelType == typeof(RemoteStreamContent) || typeof(IEnumerable).IsAssignableFrom(context.Metadata.ModelType)) { - return new AbpRemoteStreamContentModelBinder(); + return new AbpRemoteStreamContentModelBinder(); + } + + if (context.Metadata.ModelType == typeof(IRemoteStreamContent) || + typeof(IEnumerable).IsAssignableFrom(context.Metadata.ModelType)) + { + return new AbpRemoteStreamContentModelBinder(); } return null; diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs index 789c552861..8bd3a9bfa8 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs @@ -263,7 +263,7 @@ namespace Volo.Abp.Http.DynamicProxying var result = await _peopleAppService.CreateMultipleFileAsync(new CreateMultipleFileInput() { Name = "123.rtf", - Contents = new List() + Contents = new List() { new RemoteStreamContent(memoryStream, "1-1.rtf", "application/rtf"), new RemoteStreamContent(memoryStream2, "1-2.rtf", "application/rtf2") diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/CreateFileInput.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/CreateFileInput.cs index 9f0b3fe4e7..d5d8278810 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/CreateFileInput.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/CreateFileInput.cs @@ -6,6 +6,6 @@ namespace Volo.Abp.TestApp.Application.Dto { public string Name { get; set; } - public RemoteStreamContent Content { get; set; } + public IRemoteStreamContent Content { get; set; } } } diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/CreateMultipleFileInput.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/CreateMultipleFileInput.cs index e9697af933..eeda9001c3 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/CreateMultipleFileInput.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/Dto/CreateMultipleFileInput.cs @@ -7,7 +7,7 @@ namespace Volo.Abp.TestApp.Application.Dto { public string Name { get; set; } - public IEnumerable Contents { get; set; } + public IEnumerable Contents { get; set; } public CreateFileInput Inner { get; set; } }