miso_soup3 Blog

主に ASP.NET 関連について書いています。

ASP.NET Web API で クラスを定義せずに POST された Body 値を取得する

クラスを定義することが面倒な場合、JToken や dynamic で POST の Body 値を受け取ることができる。

f:id:miso_soup3:20150420173535p:plain

以下のような HTTP Request を送信し、JSON で書かれた Body 値を読み取るとする。

Content-Type : application/json
Body : { "name" : "taro", "message" : "hello" }

クラスを定義する場合

Body 値をバインドするためのクラスを定義する:

public class HogeRequest
{
	public string Name { get; set; }
	public string Message { get; set; }
}

ApiController に以下のようにアクションを定義する:

[Route("api/model")]
public void PostModel(HogeRequest request)
{

	Debug.WriteLine(request.Name);
	Debug.WriteLine(request.Message);
}

これで Body 値を読み取ることができるが、クラスを定義することが面倒な場合(定義したくない場合)、次のように値を取得することも可能。

JToken

アクションの引数に JToken(Newtonsoft.Json.Linq 名前空間)を定義する

[Route("api/test3")]
public void PostTest3(JToken token)
{
	Debug.WriteLine(token["name"]);
	Debug.WriteLine(token["message"]);

	dynamic request = token;

	Debug.WriteLine((string)request.name);
	Debug.WriteLine((string)request.message);
}

dynamic として取得することもできる。
この JToken の便利なところは、Content-Type が application/json, application/x-www-form-urlencoded の両方に対応していること。下の HTTP Request のように x-www-form-urlencoded で送信しても Body 値を取得できる。

Content-Type : application/x-www-form-urlencoded
Body : name=taro&message=hello

JToken を使用する場合、NuGet から「Newtonsoft.Json」のインストールが必要(今回は version 6.0.4 を使った)。

JObject

JToken とほぼ同じで、JObject(Newtonsoft.Json.Linq 名前空間)も定義できる。

[Route("api/jobject")]
public void PostTest2(JObject jObject)
{
	Debug.WriteLine(jObject.GetValue("name"));
	Debug.WriteLine(jObject.GetValue("message"));

	dynamic request = jObject;

	Debug.WriteLine((string)request.name);
	Debug.WriteLine((string)request.message);
}

これも application/json, application/x-www-form-urlencoded の双方に対応。

dynamic

引数に dynamic を定義することが一番単純な場合もある。しかし、dynamic の場合は JToken, JObject と違って application/x-www-form-urlencoded では取得できないので注意。

[Route("api/dynamic")]
public void PostTest(dynamic request)
{
	Debug.WriteLine((string)request.name);
	Debug.WriteLine((string)request.message);
}

(※dynamic は application/json で送信された Body 値がバインドされる。)

FormDataCollection

最後に、application/json では取得できないが、application/x-www-form-urlencoded では取得できる FormDataCollection(System.Net.Http.Formatting 名前空間)。

[Route("api/form")]
public void PostTest(FormDataCollection form)
{
	Debug.WriteLine(form.Get("name"));
	Debug.WriteLine(form.Get("message"));
}

(※FormDataCollection は application/x-www-form-urlencoded で送信された Body 値がバインドされる。)
System.Net.Http.Formatting.dll への参照が必要(NuGet で Microsoft.AspNet.WebApi.Client をインストール)。

最後に

JSON のみって決まっていれば dynamic、x-www-form-urlencoded と JSON 両方を受けたい場合は JToken、を利用するという感じ?
JToken も model class みたいなものですが。
と、ここまで書いていたら KeyValuePair のことを思い出したが検証予定なし。階層もあったか。

サンプルコード

今回検証に利用したコード。
ASP.NET Web API のバージョンは 5.2.2。

WebApiConfig.cs:
using System.Web.Http;

namespace WebApplication2
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API の設定およびサービス

            // Web API ルート
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

SampleController:

using Newtonsoft.Json.Linq;
using System.Diagnostics;
using System.Net.Http.Formatting;
using System.Web.Http;

namespace WebApplication2.Controllers
{
	public class HogeRequest
	{
		public string Name { get; set; }
		public string Message { get; set; }
	}

	public class SampleController : ApiController
	{
		[Route("api/model")]
		public void PostModel(HogeRequest request)
		{

			Debug.WriteLine(request.Name);
			Debug.WriteLine(request.Message);
		}

		[Route("api/form")]
		public void PostForm(FormDataCollection form)
		{
			Debug.WriteLine(form.Get("name"));
			Debug.WriteLine(form.Get("message"));
		}

		[Route("api/dynamic")]
		public void PostDynamic(dynamic request)
		{
			Debug.WriteLine((string)request.name);
			Debug.WriteLine((string)request.message);
		}

		[Route("api/jobject")]
		public void PostJObject(JObject jObject)
		{
			Debug.WriteLine(jObject.GetValue("name"));
			Debug.WriteLine(jObject.GetValue("message"));

			dynamic request = jObject;

			Debug.WriteLine((string)request.name);
			Debug.WriteLine((string)request.message);
		}

		[Route("api/jtoken")]
		public void PostJToken(JToken token)
		{
			Debug.WriteLine(token["name"]);
			Debug.WriteLine(token["message"]);

			dynamic request = token;

			Debug.WriteLine((string)request.name);
			Debug.WriteLine((string)request.message);
		}
	}
}