最近RESTが本格的に主流になりそうな感じですね。
SOAPでいい〜んだよSOAPでと思ってたら、以下のようなニュースとかも出てきたりして、ちょっと下火な感じがします。
ということでトレンドを追いかけるということと、どうせなら得意なC#でやっちゃえ!ということで今回はWCFでRESTでJSONなサービスを作る&使うことをやってみようと思います。下のような記事も出てるので、WCFでRESTやっておいて損はないでしょう。
ということで早速作っていきましょう。
プロジェクトの作成と実行方法の設定
まず、サービスを公開するためのWebアプリケーションを作成します。新規作成から「ASP.NET 空のWebアプリケーション」を作ります。余計なページとかは今回邪魔なのでシンプルに行きます。名前はRESTWcfAppにしました。
ここでWCFのサービスを公開します。
次に「WCF サービス ライブラリ」のプロジェクトを作成します。名前はRESTWcfApp.Libにしました。ここに、WCFのサービスのインターフェースと実装を作ります。
最後に「コンソールアプリケーション」のプロジェクトを作成します。名前はRESTWcfApp.Clientにしました。ここに、WCFのサービスを使うプログラムを作ります。
全部のプロジェクトのターゲットのフレームワークを.NET Framework 4にします(Client Profileじゃない)。そして、System.ServiceModelとSystem.ServiceModel.Webを参照に追加します。(最初から追加されてるのもあるだろうけど)
RESTWcfAppプロジェクトにはSystem.ServiceModel.Activationも追加します。
そして、RESTWcfApp.ClientとRESTWcfAppプロジェクトにRESTWcfApp.Libを参照に追加します。
Webアプリケーションは、デフォルトだと適当なポート番号で起動するようになってるので8080番ポートで起動するように設定しておきます。
そして、ソリューションのプロパティからスタートアップの方法をマルチスタートアッププロジェクトにして以下のように設定します。
設定の説明だけで、すごい長くなりましたが、以上でプロジェクトの設定完了です!!
WCFのサービスを作る
今回は、名前を渡すと「こんにちは名前さん」という文字列を返してくれるという非常に簡単なサービスを作ります。文字列を渡すパターンとオブジェクトを渡すパターンの2パターン定義します。ついでに、文字列を渡す方をGETで、オブジェクトを渡す方をPOSTで定義してみます。
まず、RESTWcfApp.Libプロジェクトに最初からあるService1とかいうのは容赦なく消します。邪魔なだけなので。
そこに、GreetService.csを作ります。今回は、小さなサンプルなので1ファイルに全部書いちゃいました。
namespace RESTWcfApp.Lib { using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.ServiceModel.Activation; [ServiceContract] public interface IGreetService { // GETメソッドで /greet/name引数の値の形で呼び出される [OperationContract] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "greet/{name}")] string Greet(string name); // POSTメソッドでgreet2というURLで呼び出される [OperationContract] [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, UriTemplate = "greet2")] GreetResponse Greet2(GreetRequest req); } // 単純にIGreetServiceを実装する // ASP.NET互換で動くように設定する [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] public class GreetService : IGreetService { public string Greet(string name) { return "こんにちは" + name + "さん"; } public GreetResponse Greet2(GreetRequest req) { return new GreetResponse { Message = this.Greet(req.Name) }; } } // こんなJSONになるイメージ? { "name" : "たろう" } [DataContract] public class GreetRequest { [DataMember(Name = "name")] public string Name { get; set; } } // こんなJSONになるイメージ? { "message" : "こんにちはたろうさん" } [DataContract] public class GreetResponse { [DataMember(Name = "message")] public string Message { get; set; } } }
ポイントはWebGetやWebInvoke属性と、DataContractやDataMemberでJSONの形を決めてる所だと思います。後は、ASP.NET互換で動くようにしてるところです。これは、あとでどういうことか示します。
Webサービスとして公開する
さて、今までWCFでサービスを公開する際の一般的な方法は.svcファイルを作ってWebアプリケーションに置くことでした。今回は、ASP.NET4で追加されたルーティングを使って公開する方法でやってみます。
この方が構成ファイルでうだうだ書くより、個人的に好きです。
まず、グローバルアプリケーションクラスを作成して、Application_Startに以下の記述を追加します。
protected void Application_Start(object sender, EventArgs e) { // /GreetServiceというURLでアクセスGreetServiceクラスで実装してる // サービスにアクセスできるようにする。 RouteTable.Routes.Add( new ServiceRoute("GreetService", new WebServiceHostFactory(), typeof(GreetService))); }
このようにすることで、.svcみたいな、わけわからない拡張子じゃなくて綺麗で、よりRESTっぽいURLでWCFのサービスを公開することが出来ます。
このルーティングを使うにはWCFがASP.NETの上で動くようにしないといけません。ということでWeb.configを以下のようにします。
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> <compilation debug="true" targetFramework="4.0" /> </system.web> <system.serviceModel> <!-- WCFがASP.NET環境で動くように --> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> </system.serviceModel> </configuration>
これでサービスとして公開することが出来ました。実行してみましょう。Webブラウザとコンソールアプリが起動すると思います。コンソールアプリは、まだ何も作ってないので放置しておいて、ブラウザのほうに以下のURLを入力してみましょう。
http://localhost:8080/GreetService/greet/ugaya40
とりあえず動作は確認できました。
クライアントアプリケーションの作成
REST形式のWebサービスは公開できたので、今度はこれを使っていきます。コンソールアプリケーションを以下のように書いてしまえばOKです。
namespace RESTWcfApp.Client { using System; using System.ServiceModel.Web; using RESTWcfApp.Lib; class Program { static void Main(string[] args) { // FiddlerでキャプチャしたいのでURLをFiddlerがlocalhostを捕捉するためのURLにする // var f = new WebChannelFactory<IGreetService>(new Uri("http://localhost:8080/GreetService")); var f = new WebChannelFactory<IGreetService>(new Uri("http://ipv4.fiddler:8080/GreetService")); var service = f.CreateChannel(); // greetのほう(GET) var message = service.Greet("太郎"); // greet2のほう(POST) var response = service.Greet2( new GreetRequest { Name = "田中" }); // 結果を表示 Console.WriteLine("Greet: {0}", message); Console.WriteLine("Greet2: {0}", response.Message); // 後始末 f.Close(); } } }
WebChannelFactory使ってProxy作って、あとはメソッドを呼ぶだけです。URLがFiddlerを使ってリクエストとレスポンスをキャプチャしたいので、それ用のURLになってます。Fiddlerを使わない場合はlocalhostでOKです。
ということでFiddlerを起動して、アプリケーションを実行すると以下のように表示されます。ちゃんとサービスが呼べているのがわかると思います。
Greet: こんにちは太郎さん Greet2: こんにちは田中さん 続行するには何かキーを押してください . . .
Fiddlerでキャプチャした結果を見てみよう
まずは、最初のGreetメソッドの呼び出しです。これはGETで呼び出されてるのがわかると思います。パラメータもURLの一部として渡されてます。
そして、Grret2メソッドの呼び出しに該当する部分も見てみます。
これはPOSTで、BODYにJSON形式でデータが渡されてることがわかると思います。レスポンスもばっちりJSONです。
まとめ
ということで.NET Framework 4のWCFでRESTのサービスを公開・呼び出す方法のポイントは以下のようになりそうです。
- Client Profileは残念ながらダメ
- WebGet, WebInvokeでRESTで呼ばれた時の挙動を設定
- DataContractとDataMemberでJSONのフォーマットを設定
- [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]をサービスの実装クラスに設定する
- .svcファイルじゃなくてRouteTableを使ってサービスを公開できる
- 別に.svcファイルでもRESTのサービスは公開できるけど、ここらへんは好みの問題?
- RESTの時はWebServiceHostFactoryをファクトリに設定する
- Web.configに
を追加する - WebChannelFactoryを使ってサービスを呼び出すためのProxyを作る
以上、ちょっと長めのエントリになりましたけど終わりです。
追記:プロジェクトは、こちらからダウンロードできます。