- 追加された行はこの色です。
- 削除された行はこの色です。
[[Open棟梁Project>http://opentouryo.osscons.jp/]] - [[マイクロソフト系技術情報 Wiki>http://techinfoofmicrosofttech.osscons.jp/]]
「[[マイクロソフト系技術情報 Wiki>http://techinfoofmicrosofttech.osscons.jp/]]」は、「[[Open棟梁Project>https://github.com/OpenTouryoProject/]]」,「[[OSSコンソーシアム .NET開発基盤部会>https://www.osscons.jp/dotNetDevelopmentInfrastructure/]]」によって運営されています。
* 目次 [#ec67115c]
* 目次 [#z3757435]
#contents
*概要 [#zb9de49d]
[[WCF]]や[[WebAPI]]で、指定のREST Formatに適合するJSONを返す方法を説明する。
*概要 [#w304b6b2]
「[[JSON]]を受信したら[[REST]]じゃなくて[[JSON-RPC>RPC#g8bee649]]だろ。」的なセルフ・ツッコミが入ったため。
*はじめに [#vf3529ba]
**出力したいJSONを確認する [#dbc5c33d]
まず、どのようなJSONを出力したいのかを確認します。
-[[JSONを送信するRESTサービスを作成する方法]]
-[[JSONを受信するJSON-RPCサービスを作成する方法]]
これがなければ、始まりません。
にタイトルを変更しました。
**出力したいJSONに合わせて、クラスやプロパティを定義する [#e10b9cbb]
次に、出力したいJSONに合わせて、クラスやプロパティを定義します。
***基本 [#i16a4292]
基本的には、以下のルールに従って、クラスやプロパティを定義します。
-JSONオブジェクト ({ キー名 : 値 }) の場合
--「キー名」を名前に持つプロパティを定義する
-JSON配列 ([ 値1, 値2, ... ]) の場合
--リストまたは配列のプロパティを定義する
-キー名が不定の場合
--キー名が不定 (実行時に決まる) の場合、事前のクラス/プロパティ定義ができない。~
--このようなときは、Dictionary<TKey, TValue>を、Dictionary<string, object> として定義する。
***ルール [#t6d4fac1]
|出力したいJSON|定義するクラス・プロパティ|h
|{ "key1" : "value1", "key2" : "value2" }|public class Sample&br;{&br; ''string key1'' { get; set; }&br; ''string key2'' { get; set; }&br;}|
|[ "1", "2", "3" ]|''List<string>'' listData { get; set; }|
|{ "key1" : [ "1", "2", "3" ] }|public class Sample&br;{&br; ''List<string> key1'' { get; set; }&br;}|
|{ "key" : { "key1" : "value1", "key2" : "value2" } }|public class Sample&br;{&br; ''string key1'' { get; set; }&br; ''string key2'' { get; set; }&br;}&br;public class Sample2&br;{&br; ''Sample key'' { get; set; }&br;}|
|[ { "key1" : "value1", "key2" : "value2" }, &br;{ "key1" : "value3", "key2" : "value4" } ]|public class Sample&br;{&br; ''string key1'' { get; set; }&br; ''string key2'' { get; set; }&br;}&br;''List<Sample>'' listData { get; set; }|
***BeanとDictionaryの違い [#y3f29fd0]
BeanとDictionaryでSerializeされるJSONのformatが異なる。
-Bean にマップする場合
{
"Prop1": "Value1",
"Prop2": "Value2"
}
-Dictionary<string, string> にマップする場合
[
{
"Key": "Prop1",
"Value": "Value1"
},
{
"Key": "Prop2",
"Value": "Value2"
}
]
*JSONを返すサービスを作成する [#n81c4388]
**WCFの場合 [#wdb092a0]
WCFでJSONを返す場合、既定ではDataContractJsonSerializerが使用されます。
***既定のDataContractJsonSerializerを使用してJSONを返す方法 [#c008c778]
-WCFサービスを作成し、コントラクト部分にWebGet属性を追加します。ポイントは以下の2点です。
--ResponseFormatに、WebMessageFormat.Jsonを指定し、JSONを返すサービスであることを宣言する
--サービスメソッドの戻り値の型に、先ほど定義したクラス/プロパティの型を指定する
-サービスを実装します。
[ServiceContract]
public interface IJSONService
{
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "GetJson")]
Sample GetJson();
}
public class JSONService : IJSONService
{
public Sample GetJson()
{
// JSON にシリアライズする元となるオブジェクトを作成する
Sample sample = new Sample()
{
StringKey = "StringValue",
IntKey = 123,
ListKey = new List<string>() { "List1", "List2", "List3" }
};
// クライアントにデータを返す
return sample;
}
}
public class Sample
{
public string StringKey { get; set; }
public int IntKey { get; set; }
public List<string> ListKey { get; set; }
}
-作成したWCFサービスを、RESTサービスとして公開するための設定をweb.configに行います
<system.serviceModel>
<services>
<service name="WebApplication1.JSONService">
<endpoint address="" binding="webHttpBinding" behaviorConfiguration="MyBehavior"
contract="WebApplication1.IJSONService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="MyBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
</system.serviceModel>
-"http://~/JSONService.svc/GetJson" にリクエストを送ったときの実行結果
{"IntKey":123,"ListKey":["List1","List2","List3"],"StringKey":"StringValue"}
-既定のDataContractJsonSerializerの注意点~
既定のDataContractJsonSerializerでは、Dictionary型のオブジェクトを正しく扱えません。~
Dictionary型のオブジェクトを扱う場合は、[http://www.newtonsoft.com/json JSON.NET]やJavaScriptSerializerなど、その他のSerializerを使用する必要があります。その方法は、次に紹介します。
***DataContractJsonSerializer以外のSerializerを使用してJSONを返す方法 [#t49e9049]
-WCFサービスを作成し、コントラクト部分にWebGet属性を追加します。ポイントは以下の2点です。
--ResponseFormatには何も指定しない
--サービスメソッドの戻り値の型は、System.ServiceModel.Channels.Messageを指定する~
(SOAPのMessage BodyにJSONを直接書き込む)
-サービスを実装します。
[ServiceContract]
public interface IJSONService2
{
[OperationContract]
[WebGet(UriTemplate = "GetJson")]
Message GetJson();
}
public class JSONService2 : IJSONService2
{
public Message GetJson()
{
// JSON にシリアライズする元となるオブジェクトを作成する
Sample2 sample = new Sample2()
{
key = new Dictionary<string, string>()
{
{"key1", "value1"},
{"key2", "value2"},
{"key3", "value3"}
}
};
// JSON.NET を使用して JSON 形式にシリアライズ
string jsonStr = JsonConvert.SerializeObject(sample);
// 'X-Content-Type-Options: nosniff' ヘッダーを追加する
WebOperationContext.Current.OutgoingResponse.Headers.Add("X-Content-Type-Options", "nosniff");
// JSON を返す
return WebOperationContext.Current.CreateTextResponse(jsonStr,
"application/json; charset=utf-8",
Encoding.UTF8);
}
}
public class Sample2
{
public Dictionary<string, string> key { get; set; }
}
-作成したWCFサービスを、RESTサービスとして公開するための設定をweb.configに行います
<system.serviceModel>
<services>
<service name="WebApplication1.JSONService2">
<endpoint address="" binding="webHttpBinding" behaviorConfiguration="MyBehavior"
contract="WebApplication1.IJSONService2" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="MyBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
</system.serviceModel>
-"http://~/JSONService2.svc/GetJson" にリクエストを送ったときの実行結果
{"key":{"key1":"value1","key2":"value2","key3":"value3"}}
**ASP.NET Web APIの場合 [#paf23e5b]
ASP.NET Web APIでは、既定でJSON.NETによってSerializeされます。~
このため、Dictionary型も問題なくSerializeできます。
-Webアプリケーションに、Web APIを作成します。
public class ValuesController : ApiController
{
// GET api/values
public Sample Get()
{
// JSON にシリアライズする元となるオブジェクトを作成する
Sample sample = new Sample()
{
key = new Dictionary<string, string>()
{
{"key1", "value1"},
{"key2", "value2"},
{"key3", "value3"}
}
};
// JSON を返す
return sample;
}
}
public class Sample
{
public Dictionary<string, string> key { get; set; }
}
-"http://~/api/Values" にリクエストを送ったときの実行結果
{"key":{"key1":"value1","key2":"value2","key3":"value3"}}
-ASP.NET Web APIの注意点~
既定では、ASP.NET Web APIは、HTTPリクエストに含まれるAcceptヘッダーの内容により、クライアントに返すデータの形式が決められます。
--Acceptに''application/xml''が含まれていた場合は、XMLとして返されます。(レスポンスのContent-Typeヘッダーが''application/xml''となる)
--Acceptに''application/json''が含まれていた場合は、JSONとして返されます。(レスポンスのContent-Typeヘッダーが''application/json''となる)~
このため、常にJSONとしてデータを受け取りたい場合は、リクエストヘッダーに''Accept: application/json''を必ずつけるようにしてください。
*相互運用 [#nf281504]
はやり、Serializer毎に挙動が違うので、~
RESTのAPIのデータ・コントラクトは、XMLやJSONのformatレベルで行うのが良いようです。
**Javaでの検証結果 [#h8e9a2e5]
***検証内容 [#la5c4259]
以下を参考に、Tomcat 上で実行する Java の Web API を作成してみました。
-覚えておくといつか必ず役に立つ。Javaで簡単Web API作成。- JAX-RS~
http://qiita.com/ukiuni@github/items/a516439b30c340703292
***結果結果 [#d4fc2a47]
-Bean にマップする場合
{
"Prop1": "Value1",
"Prop2": "Value2"
}
>→ .NET と「同じ」挙動
-HashMap<String, String> にマップする場合
{
"Prop1": "Value1",
"Prop2": "Value2"
}
>→ .NET と「異なる」挙動
**REST APIのリファレンス [#lcad8d73]
数個、AzureのREST APIをピックアップしてみました。
-List all virtual machines in a resource group~
https://msdn.microsoft.com/en-us/library/azure/mt163572.aspx
>GrsourceGroupのnameが RESTのUriに含まれていて、ResponceがJOSN形式で返されます
-Start a virtual machine~
https://msdn.microsoft.com/en-us/library/azure/mt163628.aspx
>ResponceがHTTP 200 だけのもあります
-Create or update a virtual machine~
https://msdn.microsoft.com/en-us/library/azure/mt163591.aspx
>呼び出すパラメタが大きく、変更を加える操作は、PUT で行われ、Request Body に パラメタがJOSN形式で渡されます。
やはり、JSONのformatが明記されており、formatレベルでデータ・コントラクトを結ぶ必要があります。