简体   繁体   English

如何接受ISO格式的WebAPI TimeSpan参数

[英]How to accept WebAPI TimeSpan parameter in ISO format

I recently built a service with Web API and there's an API which needs to accept a TimeSpan parameter in ISO 8601 format (eg PT5M ). 我最近使用Web API构建了一个服务,并且有一个API需要接受ISO 8601格式的TimeSpan参数(例如PT5M )。

Here's my api controller: 这是我的api控制器:

[RoutePrefix("api")]
public class StatisticsController : ApiController
{
    [Route("statistics")]
    public async Task<IEnumerable<StatPoint>> GetStatistics([FromUri] TimeSpan duration)
    {
        // ...
    }
}

And I need to access this API with url like /api/statistics?duration=PT5M . 我需要使用/api/statistics?duration=PT5M等网址访问此API。

I tried to add a JsonConverter as some people said online: 有人在网上说我试图添加一个JsonConverter

public class IsoTimeSpanConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var ts = (TimeSpan) value;
        var tsString = XmlConvert.ToString(ts);
        serializer.Serialize(writer, tsString);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            return null;
        }

        var value = serializer.Deserialize<String>(reader);
        return XmlConvert.ToTimeSpan(value);
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof (TimeSpan) || objectType == typeof (TimeSpan?);
    }
}

And in GlobalConfiguration.Configure (called in Application_start() ): GlobalConfiguration.Configure (在Application_start()调用):

config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new IsoTimeSpanConverter());

However it did not work, I always get this error message: 但是它不起作用,我总是收到此错误消息:

{
    message: "The request is invalid.",
    messageDetail: "The parameters dictionary contains a null entry for parameter 'duration' of non-nullable type 'System.TimeSpan' for method 'System.Threading.Tasks.Task`1[System.Collections.Generic.IEnumerable`1[WebRole.Api.Models.StatPoint]] GetStatistics(System.TimeSpan)' in 'WebRole.Api.StatisticsController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}

Any help is appreciated. 任何帮助表示赞赏。

I guess I found the solution. 我想我找到了解决方案。 I was on the wrong way since JsonConverter handles the body serialization, while the parsing of URL parameter is handled by ModelBinder . 我是错误的,因为JsonConverter处理了身体序列化,而URL参数的解析由ModelBinder处理。 These are two different mechanisms. 这是两种不同的机制。

I read this article and created the following ModelBinder: 我读了这篇文章并创建了以下ModelBinder:

public class IsoTimeSpanModelBinder: IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        if(bindingContext.ModelType != typeof(TimeSpan))
        {
            return false;
        }

        var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if(val == null)
        {
            return false;
        }

        var key = val.RawValue as string;
        if(key == null)
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Wrong value type for TimeSpan");
            return false;
        }

        try
        {
            bindingContext.Model = XmlConvert.ToTimeSpan(key);
            return true;
        }
        catch (Exception e)
        {
            bindingContext.ModelState.AddModelError(
                bindingContext.ModelName, "Cannot convert value to TimeSpan: " + e.Message);
            return false;
        }
    }
}

Then config to use it to parse all TimeSpan : 然后配置使用它来解析所有TimeSpan

public class Global : System.Web.HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        GlobalConfiguration.Configure(Register);
    }

    public static void Register(HttpConfiguration config)
    {
        // Parse TimeSpan in ISO format (e.g. PT5M)
        config.Services.Insert(
            typeof(ModelBinderProvider), 0,
            new SimpleModelBinderProvider(typeof(TimeSpan), new IsoTimeSpanModelBinder()));
    }
}

It is also possible to parse any user-defined type in custom formats. 也可以用自定义格式解析任何用户定义的类型。 Very flexible! 非常灵活!

Give a try to this in Application_start() 在Application_start()中试一试

var formatters = GlobalConfiguration.Configuration.Formatters;
var jsonFormatter = formatters.JsonFormatter;
jsonFormatter.SerializerSettings.Converters.Add(new IsoTimeSpanConverter());

Update: 更新:

Is there any reason you are taking parameter as TimeSpan in function? 您是否有任何理由在函数中将参数作为TimeSpan?

You can take string duration and then start your function with 您可以使用字符串持续时间,然后启动您的功能

        try
        {
            TimeSpan tt = XmlConvert.ToTimeSpan(duration);
        }
        catch(Exception ex)
        {

        }

as your custom code is doing the same converting a string (ISO Time) to timespan. 因为您的自定义代码正在将字符串(ISO时间)转换为时间跨度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM