-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
控制器Action以接口作为入参,默认FromServices能够从DI得到正确类型的对象,但是内部属性为空,没有读取参数;如果使用Fro…
…mBody,则提示接口无法反序列化。
- Loading branch information
Showing
5 changed files
with
177 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
144 changes: 144 additions & 0 deletions
144
NewLife.Remoting.Extensions/Services/ServicModelMetadataProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
using System.Text; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Mvc.Formatters; | ||
using Microsoft.AspNetCore.Mvc.Infrastructure; | ||
using Microsoft.AspNetCore.Mvc.ModelBinding; | ||
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; | ||
using Microsoft.Extensions.Options; | ||
using NewLife.Remoting.Models; | ||
|
||
namespace NewLife.Remoting.Extensions.Services; | ||
|
||
class ServiceModelBinder : IModelBinder | ||
{ | ||
private readonly IServiceProvider _serviceProvider; | ||
private readonly IList<IInputFormatter> _formatters; | ||
private readonly Func<Stream, Encoding, TextReader> _readerFactory; | ||
private readonly MvcOptions _options; | ||
|
||
public ServiceModelBinder(IServiceProvider serviceProvider) | ||
{ | ||
_serviceProvider = serviceProvider; | ||
_formatters = serviceProvider.GetServices<IInputFormatter>().ToList(); | ||
_readerFactory = _serviceProvider.GetRequiredService<IHttpRequestStreamReaderFactory>().CreateReader; | ||
_options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Value; | ||
} | ||
|
||
public async Task BindModelAsync(ModelBindingContext bindingContext) | ||
{ | ||
ArgumentNullException.ThrowIfNull(bindingContext, nameof(bindingContext)); | ||
var modelBindingKey = (!bindingContext.IsTopLevelObject) ? bindingContext.ModelName : (bindingContext.BinderModelName ?? String.Empty); | ||
var httpContext = bindingContext.HttpContext; | ||
var inputFormatterContext = new InputFormatterContext(httpContext, modelBindingKey, bindingContext.ModelState, bindingContext.ModelMetadata, _readerFactory, false); | ||
IInputFormatter formatter = null; | ||
for (var i = 0; i < _formatters.Count; i++) | ||
{ | ||
if (_formatters[i].CanRead(inputFormatterContext)) | ||
{ | ||
formatter = _formatters[i]; | ||
break; | ||
} | ||
} | ||
if (formatter == null) | ||
{ | ||
var exception = new UnsupportedContentTypeException(httpContext.Request.ContentType); | ||
bindingContext.ModelState.AddModelError(modelBindingKey, exception, bindingContext.ModelMetadata); | ||
return; | ||
} | ||
try | ||
{ | ||
var inputFormatterResult = await formatter.ReadAsync(inputFormatterContext); | ||
if (inputFormatterResult.HasError) | ||
{ | ||
return; | ||
} | ||
if (inputFormatterResult.IsModelSet) | ||
{ | ||
var model = inputFormatterResult.Model; | ||
bindingContext.Result = ModelBindingResult.Success(model); | ||
} | ||
else | ||
{ | ||
var errorMessage = bindingContext.ModelMetadata.ModelBindingMessageProvider.MissingRequestBodyRequiredValueAccessor(); | ||
bindingContext.ModelState.AddModelError(modelBindingKey, errorMessage); | ||
} | ||
} | ||
catch (Exception ex) when (ex is InputFormatterException || ShouldHandleException(formatter)) | ||
{ | ||
bindingContext.ModelState.AddModelError(modelBindingKey, ex, bindingContext.ModelMetadata); | ||
} | ||
} | ||
|
||
private static Boolean ShouldHandleException(IInputFormatter formatter) | ||
{ | ||
return ((formatter as IInputFormatterExceptionPolicy)?.ExceptionPolicy ?? InputFormatterExceptionPolicy.MalformedInputExceptions) == InputFormatterExceptionPolicy.AllExceptions; | ||
} | ||
} | ||
|
||
class ServiceModelBinderProvider : IModelBinderProvider | ||
{ | ||
public IModelBinder? GetBinder(ModelBinderProviderContext context) | ||
{ | ||
if (!context.Metadata.IsComplexType) return null; | ||
|
||
var type = context.Metadata.ModelType; | ||
if (type.IsInterface && context.Services?.GetService(type) != null) | ||
{ | ||
return new ServiceModelBinder(context.Services); | ||
} | ||
|
||
return null; | ||
} | ||
} | ||
|
||
class ServicModelMetadataProvider : DefaultModelMetadataProvider | ||
{ | ||
private readonly IServiceProvider _serviceProvider; | ||
|
||
public ServicModelMetadataProvider(ICompositeMetadataDetailsProvider detailsProvider, IServiceProvider serviceProvider) | ||
: base(detailsProvider) | ||
{ | ||
_serviceProvider = serviceProvider; | ||
} | ||
|
||
public override ModelMetadata GetMetadataForType(Type modelType) | ||
{ | ||
if (modelType.IsInterface) | ||
{ | ||
var momdel = _serviceProvider.GetService(modelType); | ||
if (momdel != null) | ||
{ | ||
modelType = momdel.GetType(); | ||
} | ||
} | ||
|
||
return base.GetMetadataForType(modelType); | ||
} | ||
|
||
//protected override ModelMetadata CreateModelMetadata(DefaultMetadataDetails entry) => base.CreateModelMetadata(entry); | ||
|
||
protected override DefaultMetadataDetails CreateParameterDetails(ModelMetadataIdentity key) => base.CreateParameterDetails(key); | ||
|
||
protected override DefaultMetadataDetails[] CreatePropertyDetails(ModelMetadataIdentity key) => base.CreatePropertyDetails(key); | ||
|
||
protected override DefaultMetadataDetails CreateTypeDetails(ModelMetadataIdentity key) => base.CreateTypeDetails(key); | ||
|
||
public override IEnumerable<ModelMetadata> GetMetadataForProperties(Type modelType) => base.GetMetadataForProperties(modelType); | ||
|
||
protected override ModelMetadata CreateModelMetadata(DefaultMetadataDetails entry) | ||
{ | ||
var modelType = entry.Key.ModelType; | ||
|
||
if (modelType.IsInterface && (modelType == typeof(ILoginRequest) || modelType == typeof(IPingRequest))) | ||
{ | ||
var model = _serviceProvider.GetService(modelType); | ||
if (model != null) | ||
{ | ||
var key = ModelMetadataIdentity.ForType(model.GetType()); | ||
entry = new DefaultMetadataDetails(key, entry.ModelAttributes); | ||
} | ||
} | ||
|
||
return base.CreateModelMetadata(entry); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters