Implementation of the FromBody binding converter for the ASP.NET Core Integration
This commit is contained in:
Родитель
11e96ab0b6
Коммит
3829bbd22d
|
@ -0,0 +1,100 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Http.Converters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore
|
||||
{
|
||||
internal sealed class FromBodyConverstionFeature : IFromBodyConversionFeature
|
||||
{
|
||||
public static FromBodyConverstionFeature Instance { get; } = new();
|
||||
|
||||
public async ValueTask<object?> ConvertAsync(FunctionContext context, Type targetType)
|
||||
{
|
||||
var httpContext = context.GetHttpContext()
|
||||
?? throw new InvalidOperationException($"The '{nameof(FromBodyConverstionFeature)} expects an '{nameof(HttpContext)}' instance in the current context.");
|
||||
|
||||
var metadata = httpContext.RequestServices
|
||||
.GetService<IModelMetadataProvider>()?
|
||||
.GetMetadataForType(targetType);
|
||||
|
||||
if (metadata is null)
|
||||
{
|
||||
context.GetLogger<FromBodyConverstionFeature>()
|
||||
.LogWarning("Unable to resolve a model metadata provider for the target type ({TargetType}).", targetType);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var binderModelName = metadata.BinderModelName ?? string.Empty;
|
||||
|
||||
var modelBinder = (httpContext.RequestServices
|
||||
.GetService<IModelBinderFactory>()?
|
||||
.CreateBinder(new ModelBinderFactoryContext
|
||||
{
|
||||
Metadata = metadata,
|
||||
BindingInfo = new BindingInfo()
|
||||
{
|
||||
BinderModelName = binderModelName,
|
||||
BindingSource = BindingSource.Body,
|
||||
BinderType = metadata.BinderType,
|
||||
PropertyFilterProvider = metadata.PropertyFilterProvider,
|
||||
},
|
||||
CacheToken = null
|
||||
}))
|
||||
?? throw new InvalidOperationException($"Unable to resolve a request body model binder for the target type ({metadata.BinderType}.");
|
||||
|
||||
var modelBindingContext = new DefaultModelBindingContext
|
||||
{
|
||||
ModelMetadata = metadata,
|
||||
ModelName = binderModelName,
|
||||
BindingSource = BindingSource.Body,
|
||||
ModelState = new ModelStateDictionary(),
|
||||
ActionContext = new ActionContext
|
||||
{
|
||||
HttpContext = httpContext
|
||||
}
|
||||
};
|
||||
|
||||
await modelBinder.BindModelAsync(modelBindingContext);
|
||||
|
||||
if (modelBindingContext.Result.IsModelSet)
|
||||
{
|
||||
return modelBindingContext.Result.Model;
|
||||
}
|
||||
else if (!modelBindingContext.ModelState.IsValid)
|
||||
{
|
||||
var messageBuilder = new StringBuilder();
|
||||
foreach (var key in modelBindingContext.ModelState.Keys)
|
||||
{
|
||||
var dictionary = modelBindingContext.ModelState[key];
|
||||
|
||||
if (dictionary == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var error in dictionary.Errors)
|
||||
{
|
||||
messageBuilder.AppendLine(error.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(messageBuilder.ToString());
|
||||
}
|
||||
|
||||
context.GetLogger<FromBodyConverstionFeature>()
|
||||
.LogWarning("Unable to bind the request body to the target type ({TargetType}).", targetType);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Http;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Azure.Functions.Worker.Extensions.Http.Converters;
|
||||
using Microsoft.Azure.Functions.Worker.Http;
|
||||
using Microsoft.Azure.Functions.Worker.Middleware;
|
||||
|
||||
|
@ -41,6 +42,9 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore
|
|||
|
||||
AddHttpContextToFunctionContext(context, httpContext);
|
||||
|
||||
// Register additional context features
|
||||
context.Features.Set<IFromBodyConversionFeature>(FromBodyConverstionFeature.Instance);
|
||||
|
||||
await next(context);
|
||||
|
||||
if (context.GetInvocationResult()?.Value is IActionResult actionResult)
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\DotNetWorker\DotNetWorker.csproj" />
|
||||
<ProjectReference Include="..\..\Worker.Extensions.Abstractions\src\Worker.Extensions.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\..\Worker.Extensions.Http\src\Worker.Extensions.Http.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Azure.Functions.Worker;
|
||||
using FromBodyAttribute = Microsoft.Azure.Functions.Worker.Http.FromBodyAttribute;
|
||||
|
||||
namespace AspNetIntegration
|
||||
{
|
||||
public class BodyBindingHttpTrigger
|
||||
{
|
||||
[Function(nameof(BodyBindingHttpTrigger))]
|
||||
public IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
|
||||
[FromBody] Person person)
|
||||
{
|
||||
return new OkObjectResult(person);
|
||||
}
|
||||
}
|
||||
|
||||
public record Person(string Name, int Age);
|
||||
}
|
|
@ -18,9 +18,7 @@ namespace AspNetIntegration
|
|||
|
||||
// operations can be performed using HttpContext
|
||||
// example of getting route information from HttpContext:
|
||||
RouteEndpoint? endpoint = httpContext.GetEndpoint() as RouteEndpoint;
|
||||
|
||||
if (endpoint != null)
|
||||
if (httpContext.GetEndpoint() is RouteEndpoint endpoint)
|
||||
{
|
||||
string? displayName = endpoint.DisplayName;
|
||||
string? routePattern = endpoint.RoutePattern.RawText;
|
||||
|
|
Загрузка…
Ссылка в новой задаче