web-dev-qa-db-ja.com

Asp.net Web Apiは、UTC時間文字列を現地時間に逆シリアル化します

私はこのURLを持っています

http://example.com/api/record/getall?startdate=1994-11-05T17:15:30Z

そしてこのwebapiエンドポイント

[ActionName("GetAll")]
public object GetAll(DateTime startDate)
{
     ...
}

私が直面する問題は、startDateが逆シリアル化された文字列を現地時間として受け取ったということです。「11/5/1994 9:15:30 AM」ではなく「1994年11月5日午後5時15分30秒」というUTC時間にとどまります。

最新のJson.netnugetパッケージであるVS2012update2を使用しています。ただし、別のコンソールアプリでjson.netを使用してテストすると、同じ文字列「1994-11-05T17:15:30Z」が可能になります。 「11/5/1994 5:15:30 PM」に正しく逆シリアル化します。

誰かがここで何が悪いのか知っていますか?

28
Ray

あなたはすでに 解決策を見つけました あなたの質問に対してですが、私はそれがあなたが期待したように機能しなかった理由を説明することを試みようと思いました。

WebApiは、コンテンツタイプネゴシエーションを使用して、データを読み取るときに使用するパーサーを決定します。つまり、リクエストのContent-Typeヘッダーを調べて決定を下します。 Content-Typeヘッダーがapplication/jsonに設定されている場合、Json.Netを使用してコンテンツを解析し、メソッドにフィードします。

ここで作成しているようなHTTPGETリクエストには、コンテンツタイプが設定されていません。この場合の「コンテンツ」は、実際にはURLからのクエリ文字列にすぎません。 WebApiはここでJSONデータを見つけることを期待していないため、JSONパーサーを使用してそれを理解しようとはしません。たとえあったとしても、GetAllメソッドに渡した文字列は有効なJSONではありません。 (有効にするには引用符で囲む必要があります。)

ここで、POSTリクエストを受け入れるようにメソッドを変更し、コンテンツタイプヘッダーをapplication/jsonに設定し、日付をJSON文字列として本文に渡した場合。 WebApiはJson.Netを使用して解析し、期待どおりに機能します。

たとえば、メソッドが次のようになっているとします。

[HttpPost]
public object GetAll([FromBody]DateTime startDate)
{
    try
    {
        return new
        {
            StartDate = startDate.ToString("yyyy-MM-dd HH:mm:ss"),
            StartDateKind = startDate.Kind.ToString(),
        };
    }
    catch (Exception ex)
    {
        return ex.Message;
    }
}

そして、あなたはこのようなリクエストをしました(POSTに注意してください):

POST http://localhost:57524/api/values/GetAll HTTP/1.1
Content-Type: application/json
Content-Length: 22
Host: localhost:57524

"1994-11-05T17:15:30Z"

応答は次のようになります。

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 31 May 2013 01:25:48 GMT
Content-Length: 57

{"StartDate":"1994-11-05 17:15:30","StartDateKind":"Utc"}

ご覧のとおり、このシナリオでは日付がUTCであることを正しく認識しています。

30
Brian Rogers

Asp WebApiがGETリクエストのuriパラメータを解析する方法を変更する場合は、次のようにカスタムIModelBinderを記述できます。

public class UtcDateTimeModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        var stringValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName)?.RawValue as string;
        DateTime parsedDate;
        if (!DateTime.TryParse(stringValue, null, DateTimeStyles.AdjustToUniversal, out parsedDate))
        {
            return false;
        }

        bindingContext.Model = parsedDate;
        return true;
    }
}

次に、デフォルトのバインダーをオーバーライドします。

GlobalConfiguration.Configure(configuration => configuration.BindParameter(typeof(DateTime), new UtcDateTimeModelBinder()););

ASP.NET Web APIのパラメーターバインディング

4
Atomosk