web-dev-qa-db-ja.com

Web API 2でのDateTimeパラメーターのフォーマットの制御

そのため、C#で記述されたWebAPI 2コントローラーがあり、これはとりわけDateTime型のクエリパラメーターを取ります。これは、日付フィルターに基づいてデータストアからすべての値を返すAPIです。次のようにしましょう:

public MyThing GetThing([FromUri]DateTime startTime)
{
 // filter and return some results
}

2つの問題が発生しています。

  1. ISO 8601 UTC形式(Z付き)で渡されたにもかかわらず、何らかの理由で、WebAPIはそれをUtcではなくローカルのDateTimeとして逆シリアル化しています。これは明らかに望ましくありません。 UTC-0 DateTimesを正しく理解するようにパイプラインを変更する方法がわかりません。
  2. 応答本文の一部としてリソースへのリンクを返します。ここでは、UrlHelperオブジェクト(親ApiController抽象クラスから取得)のLink()メソッドを使用してhrefを生成します。ルートに追加したいクエリパラメータのコレクションを渡しています。何らかの理由で、DateTimeを渡すと、ISO8601以外の形式にフォーマットされます。これが制御されている場所を見つけることができません。普遍的には強制できないため、明示的にToString()を使用したくありません。

要するに、私はそれを確認する方法を見つけたいです

  1. FromUriクエリパラメータを介して渡されるDateTimeは、適切なタイムゾーンオフセットを含め、ISO8601として適切に理解されます。
  2. UrlHelper.Link()は、普遍的に実施可能な静的型付けの方法で、出力URI文字列にISO8601準拠のDateTimeを生成します。

WebAPI 2は、JSONをフォーマットするための素晴らしいフックを提供します。これは、私が使用するJSONボディでDateTimeを返すだけで、ISO8601フォーマットを使用して必要に応じてフォーマットされ、[FromBody] JSONボディでも正しく理解されます。しかし、URI処理の周りに文字列を引き出す方法が見つからないので、本当にやりたいのです。

18
Zoinks

Modelbinderを使用して、受信データをモデルに変換できます。

GetThings([ModelBinder(typeof(UtcDateTimeModelBinder)), FromUri] DateTime dt){//do somthing}


public class UtcDateTimeModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {

        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        if (bindingContext.ModelMetadata.ModelType == typeof(DateTime))
        {
            var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            var str = valueProviderResult.AttemptedValue;
            return DateTime.Parse(str).ToUniversalTime();
        }

        return null;
    }

このようにして、DateTimeのデフォルトのモデルビルダとして設定できます。

ModelBinders.Binders.Add(typeof(DateTime), new UtcDateTimeModelBinder());
7
reza taroosheh

UTCオフセットを保持する場合は、DateTimeの代わりに DateTimeOffset を使用しないのはなぜですか? JSONシリアル化を処理するコードをいくつか次に示します。

APIコントローラ:

public class ValuesController : ApiController
{
    public object Get(DateTimeOffset dt)
    {
        return new {
            Input = dt,
            Local = dt.LocalDateTime,
            Utc = dt.UtcDateTime
        };
    }
}

Razor Viewサンプルコード(MVC + Web API Visual Studioテンプレートで作成されたデフォルトのAPIルートがあると想定)

<a href="@Url.RouteUrl("DefaultApi",new {httproute = "",controller = "values",dt = DateTimeOffset.UtcNow})">Utc Now</a>

としてレンダリング:

<a href="/api/values?dt=06%2F26%2F2018%2009%3A37%3A24%20%2B00%3A00">Utc Now</a>

また、日時オフセットを指定してAPIを呼び出すことができます。

2018-06-26T08:25:48Z: http://localhost:1353/api/values?dt=2018-06-26T08:25:48Z
{"Input":"2018-06-26T08:25:48+00:00","Local":"2018-06-26T10:25:48+02:00","Utc":"2018-06-26T08:25:48Z"}

2018-06-26T08:25:48+01:00: http://localhost:1353/api/values?dt=2018-06-26T08%3A25%3A48%2B01%3A00 (note that : and + must be url encoded)
{"Input":"2018-06-26T08:25:48+01:00","Local":"2018-06-26T09:25:48+02:00","Utc":"2018-06-26T07:25:48Z"}
4
asidis

送信するクエリ文字列パラメーター値is UTC時間。したがって、同じものがローカルクロックに基づいて時間に変換され、ToUniversalTime()を呼び出すと、UTCに変換されます。

それで、質問は正確には何ですか?クエリ文字列として送信されたが、リクエスト本文に投稿されたときにそうではないのはなぜかという質問の場合、その質問に対する答えは、ASP.NET Web APIがURIパス、クエリ文字列などをモデルバインディングを使用してバインドし、本文を使用してパラメータのバインド。後者の場合は、メディアフォーマッタを使用します。 JSONを送信する場合、JSONメディアフォーマッタが使用され、JSON.NETに基づいています。

DateTimeZoneHandling.Utcを指定したので、それはその設定を使用して、希望する日時の種類を取得します。ところで、この設定をDateTimeZoneHandling.Localに変更すると、モデルバインディングと同じ動作になります。

希望するフォーマットを取得するために必要なことは、ToUniversalTime()メソッドを呼び出すことだけです。

2
Barr J

1。

パラメータ 'startTime'のタイムゾーン(サーバー/コンピューターのタイムゾーン)を確認する必要があります。

Web APIが提供するDateTimeは正確で、タイムゾーンに依存します。

2。

ISO8601形式の日付を生成するために、Json DateTimeシリアライザーを作成します。

1
sheep