※ご注意
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 IQueryable Get() {
            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;
        }    
    }
}
   
  

 

コメント