C# 使NodaTime.Instant在AspNetCore中作为查询/路径/表单参数工作需要哪些配置?

C# 使NodaTime.Instant在AspNetCore中作为查询/路径/表单参数工作需要哪些配置?,c#,.net,asp.net-core,nodatime,C#,.net,Asp.net Core,Nodatime,我正试图在我的AspNetCore项目中使用NodaTime JSON序列化工作正常。但是我无法处理form/query/pathparams的模型绑定。正如我在类似问题中看到的,NodaTime中没有TypeConverter实现。也许有一些解决办法 .csproj netcoreapp2.2 过程中 我的启动服务配置: public void配置服务(IServiceCollection服务) { services.AddMvc().SetCompatibilityVersion(Com

我正试图在我的AspNetCore项目中使用
NodaTime

JSON序列化工作正常。但是我无法处理
form/query/path
params的模型绑定。正如我在类似问题中看到的,
NodaTime
中没有TypeConverter实现。也许有一些解决办法

.csproj


netcoreapp2.2
过程中
我的启动服务配置:

public void配置服务(IServiceCollection服务)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(o=>o.SerializerSettings.ConfigureForNodeTime(DateTimeZoneProviders.Tzdb));
}
Api控制器来重现问题:

[路由(“api/[控制器]”)]
[ApiController]
公共类值控制器:控制器库
{
//工作
[HttpGet(“序列化”)]
公共行动结果获取()
{
返回Ok(new{Now=SystemClock.Instance.GetCurrentInstant()});
}
//错误
[HttpGet(“Ping”)]
公共操作结果获取([FromQuery]Instant t)
{
返回Ok(新的{Pong=t});
}
}
网址:
http://localhost:55555/api/values/ping?t=2019-09-02T06:55:52.7077495Z

堆栈跟踪:

System.InvalidOperationException:无法创建类型为的实例 “NodaTime.Instant”。模型绑定的复杂类型不能是抽象的或非抽象的 值类型,并且必须具有无参数构造函数。或者, 为“t”参数指定一个非空的默认值。在 Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(ModelBindingContext bindingContext)位于 Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.bindModelCoreAscync(ModelBindingContext bindingContext)位于 Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext、IModelBinder modelBinder、IValueProvider valueProvider、, ParameterDescriptor参数,ModelMetadata元数据,对象值)
在 Microsoft.AspNetCore.Mvc.Internal.ControllerBinderDelegateProvider.c_uuDisplayClass0_0.d.MoveNext() ---来自引发异常的上一个位置的堆栈结束跟踪---在 Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync() 在 Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter() 在 Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext (上下文)在 Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State&Next, 范围和范围、对象和状态、布尔值和isCompleted)位于 Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync() 在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()上 在Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext)中 httpContext)在 Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)在 Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext (上下文)


我远非ASP.NET核心模型绑定专家,但我已经设法找到了一种方法,如下所示

我们现在在NodeTime 3.0中支持TypeConverter,这在3.0.0-beta01版本中提供。我不希望从现在起到3.0GA发行版之间出现突破性的变化,但我更愿意不做任何保证:)尽管实现代码相当稳定——我不会为此担心

要手动绑定,可以创建模型绑定器提供程序和模型绑定器:

public class InstantModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context) =>
        context.Metadata.ModelType == typeof(Instant) ? new InstantModelBinder(context.Metadata.ParameterName) : null;
}

public class InstantModelBinder : IModelBinder
{
    private readonly string parameterName;

    public InstantModelBinder(string parameterName)
    {
        this.parameterName = parameterName;
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var text = bindingContext.ActionContext.HttpContext.Request.Query[parameterName];
        if (text.Count != 1)
        {
            bindingContext.Result = ModelBindingResult.Failed();
        }
        else
        {
            var result = InstantPattern.ExtendedIso.Parse(text);
            bindingContext.Result = result.Success
                ? ModelBindingResult.Success(result.Value)
                : ModelBindingResult.Failed();
        }
        return Task.CompletedTask;
    }
}
然后将提供程序注册为MVC选项中的第一个提供程序:

services.AddMvc(options => options.ModelBinderProviders.Insert(0, new InstantModelBinderProvider()))
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .AddJsonOptions(o => o.SerializerSettings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb));
如果您需要对多个Noda时间类型执行此操作,我希望您能够使用单个提供程序,根据
ModelBinderProviderContext.Metadata.ModelType
确定要使用哪个绑定器


当前,绑定器假定您是从查询字符串绑定的。我还没有研究过让它更通用有多容易。

中有一个例子说明了
bindingContext.ValueProvider.GetValue(modelName)
是如何从查询字符串和其他位置获取值的更通用的方法。@KirkLarkin:感谢您指出这一点。。。我不清楚模型名是否是这里使用的最佳键。。。我想我可能会保留答案,因为它是一个简单的“仅查询”解决方案,希望处于不同情况的任何人都可以阅读您的评论和文档…我相信
modelName
最终将作为OP的
t
,但可以被覆盖,例如
[FromQuery(Name=“I”)]…
。我同意,考虑到手头的问题,提供的更简单的解决方案更合适。正如你所说,我只是提供了一个链接来帮助解决你答案中的最后一段。