※ご注意
WCF Web APIはマイクロソフトにより現在開発はキャンセルされ、ASP.NET MVC 4のASP.NET Web APIとして制作・公開されています。
.NET Framework/ASP.NETでRESTfulなAPI構築を目的に記事を探されている方は是非以下のリンクからASP.NET Web APIについてご確認下さい。
https://opcdiary.net/?page_id=5981
とりあえず前々回と前回でサービスと、そのクライアントを一通り作ったわけですが、現状RESTfulなWEBサービスとして考えるとあまり良くありません。
どの辺が良くないかというと、POSTのリターンが今のままだと200(OK)となっていて、実際にやっていることはサービス内でヒーローというリソースの新規作成(レコードの追加)しているので、ここは201(Created)を返したい。また、DELETEとPUTでリソースが見つからなかった場合に500(サーバーエラー)としていたのですが、これもやはり良い返し方ではなく、404(Not Found)でクライアントに返すべきでしょう。
ということで、今回は前々回で作成したサービス側にこれらの修正を加えます。
using句の追加
SuperHeroApi.csに以下のusing句を追加します。
using System.Net; using Microsoft.ApplicationServer.Http; using Microsoft.ApplicationServer.Http.Dispatcher;
POSTの修正
POSTの成功で201を返すようにコードを修正します。
////// POSTコマンド /// ヒーローを追加する /// /// 追加するHeroのリスト ///追加したHeroのリスト [WebInvoke(UriTemplate = "", Method = "POST")] public HttpResponseMessage> Add(IEnumerable
heroes) { List addedHeroes = new List (); using (var context = new Models.HeroContext()) { //最後のIDE接続の場合を取得する。 int lastId = 0; if (context.Heros.Count() > 0) { lastId = context.Heros.OrderByDescending(h => h.HeroId).First().HeroId; } //データの追加 foreach (var _hero in heroes) { context.Heros.Add(_hero); } //DBへの反映 context.SaveChanges(); //クライアントに返す反映後のデータのリストを作成する if (lastId != 0) { addedHeroes = context.Heros.Where(h => h.HeroId > lastId) .OrderBy(h => h.HeroId).ToList(); } else { addedHeroes = context.Heros.ToList(); } } //ステータスコードとして201(Created)を返すように修正。 var result = new HttpResponseMessage >(addedHeroes); result.StatusCode = HttpStatusCode.Created; return result; }
変更した部分は34行です。201(Created)をリターンする応答メッセージのステータスにセットしています。
HTTPリクエストにたする応答のステータスを任意に変更したい場合にはまず、メソッドの戻り値をデータのカスタムタイプそのものではなくHttpResponseMessage(T)を戻り値の方として使用し、HttpResponseMessage.StatusCodeにHttpStatusCode列挙子をセットし、返すようにします。データのカスタムタイプそのものを返すようした場合にはステータスは200がWeb APIにより自動的につけられます。
DELETEの修正
引数で与えられたIDが見つからなかった場合に404を返すように修正します。
////// DELETEコマンド /// ヒーローを削除する /// /// 削除するヒーローのid番号 ///削除されたヒーロー [WebInvoke(UriTemplate = "{id}", Method = "DELETE")] public Models.Hero Delete(int id) { Models.Hero _hero; using (var context = new Models.HeroContext()) { _hero = context.Heros.Find(id); if (_hero != null) { context.Heros.Remove(_hero); context.SaveChanges(); } else { //500エラーにするのではなく404(Not Found)を返すようにする。 throw new HttpResponseException(HttpStatusCode.NotFound); } } return _hero; }
修正したの18行目です。HttpResponseExceptionをスローするように変更しています。ここではHttpResponseExceptionの引数にHttpStatusCode列挙子を渡して404(Not Found)を返すようにしています。HttpResponseExceptionの引数なしのコンストラクタで初期化した場合には500を返すように初期化されます。
PUTに対しても同様の修正を行います。
以上の修正を加えることでよりRESTfulになるよう修正できました。
全体コード
SuperHeroAPi.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.ServiceModel; using System.ServiceModel.Web; using System.Net.Http; using SuperHero.Resources; using Models = SuperHero.Models; //以下を追加する using System.Net; using Microsoft.ApplicationServer.Http; using Microsoft.ApplicationServer.Http.Dispatcher; namespace SuperHero.APIs { ////// REST API Sample /// [ServiceContract] public class SuperHeroApi { ////// ヒーローのリストを取得する。 /// ODataのインターフェイスをサポートしている /// 基本的に@Chackさんのサンプルを変更 /// ///ヒーローのリスト [WebGet(UriTemplate = "")] public IQueryableGet() { List heroes; using (var context = new Models.HeroContext()) { heroes = context.Heros.ToList(); } return heroes.AsQueryable(); } /// /// POSTコマンド /// ヒーローを追加する /// /// 追加するHeroのリスト ///追加したHeroのリスト [WebInvoke(UriTemplate = "", Method = "POST")] public HttpResponseMessage> Add(IEnumerable
heroes) { List addedHeroes = new List (); using (var context = new Models.HeroContext()) { //最後のIDE接続の場合を取得する。 int lastId = 0; if (context.Heros.Count() > 0) { lastId = context.Heros.OrderByDescending(h => h.HeroId).First().HeroId; } //データの追加 foreach (var _hero in heroes) { context.Heros.Add(_hero); } //DBへの反映 context.SaveChanges(); //クライアントに返す反映後のデータのリストを作成する if (lastId != 0) { addedHeroes = context.Heros.Where(h => h.HeroId > lastId) .OrderBy(h => h.HeroId).ToList(); } else { addedHeroes = context.Heros.ToList(); } } //ステータスコードとして201(Created)を返すように修正。 var result = new HttpResponseMessage >(addedHeroes); result.StatusCode = HttpStatusCode.Created; return result; } ///
/// DELETEコマンド /// ヒーローを削除する /// /// 削除するヒーローのid番号 ///削除されたヒーロー [WebInvoke(UriTemplate = "{id}", Method = "DELETE")] public Models.Hero Delete(int id) { Models.Hero _hero; using (var context = new Models.HeroContext()) { _hero = context.Heros.Find(id); if (_hero != null) { context.Heros.Remove(_hero); context.SaveChanges(); } else { //500エラーにするのではなく404(Not Found)を返すようにする。 throw new HttpResponseException(HttpStatusCode.NotFound); } } return _hero; } ////// PUTコマンド /// ヒーローを更新する /// /// 更新するヒーロー ///更新されたヒーロー [WebInvoke(UriTemplate = "", Method = "PUT")] public Models.Hero Update(Models.Hero hero) { using (var context = new Models.HeroContext()) { var _hero = context.Heros.Find(hero.HeroId); if (_hero != null) { _hero.Name = hero.Name; context.SaveChanges(); } else { //500エラーにするのではなく404(Not Found)を返すようにする。 throw new HttpResponseException(HttpStatusCode.NotFound); } } return hero; } } }
コメント