スポンサーリンク

ASP.NET Web APIサーバー編

以前WCF Web APIについて書きましたが、その後紆余曲折あり、まず、WCFのプロダクトでは無くなり、それに従いWCFとしてホストされることは出来なくなり、ASP.NET MVCによるホスト機能に特化され、名前もASP.NET Web APIとなり、実質的にASP.NET MVC 4の1機能になっています。

また、新たに公開されたVisual Studio 2012ではASP.NET 4 Webアプリケーションテンプレートでサポートされています。

※以下記事の元になった記事は日本マイクソフト井上さんの記事(Web APIをはじめてみよう)が元になっています

そこで、以前の記事でWCF Web APIで書かれた内容をASP.NET Web APIで書き直したいと思います。

準備

ASP.NET 4 Webアプリケーションテンプレートを使って新しいプロジェクトを作成します。下の図と異なりますが、ソリューション名はSuperHero2とします。

新しいASP.NET MVC 4プロジェクトダイアログでWeb APIを選択します。

OKをクリックすると、基本のものに加えてWeb API関連のコンポーネントへの参照やファイルが追加されたASP.NET MVC 4のプロジェクトが作成されます。

Modelの追加

APIを通して提供するモデルとなるクラスを追加します。Modelsのフォルダの下にHeroクラスを追加します。EFを使ってSQL Serverにデータ保存するようにしています。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-keyword">using</span> System.Data;
<span class="hljs-keyword">using</span> System.Data.Entity;
<span class="hljs-keyword">using</span> System.Data.Entity.Infrastructure;
<span class="hljs-keyword">using</span> System.Data.SqlClient;
<span class="hljs-keyword">using</span> System.ComponentModel.DataAnnotations;
<span class="hljs-keyword">namespace</span> <span class="hljs-title">SuperHero2.Models</span>
{
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Hero</span>
{
[<span class="hljs-meta">Key</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> Id { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
<span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">HeroContext</span> : <span class="hljs-title">DbContext</span>
{
<span class="hljs-keyword">public</span> DbSet Heroes { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
}
<span class="hljs-keyword">using</span> System.Data; <span class="hljs-keyword">using</span> System.Data.Entity; <span class="hljs-keyword">using</span> System.Data.Entity.Infrastructure; <span class="hljs-keyword">using</span> System.Data.SqlClient; <span class="hljs-keyword">using</span> System.ComponentModel.DataAnnotations; <span class="hljs-keyword">namespace</span> <span class="hljs-title">SuperHero2.Models</span> { <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Hero</span> { [<span class="hljs-meta">Key</span>] <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> Id { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } } <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">HeroContext</span> : <span class="hljs-title">DbContext</span> { <span class="hljs-keyword">public</span> DbSet Heroes { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } } }
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.SqlClient;
using System.ComponentModel.DataAnnotations;

namespace SuperHero2.Models
{
    public class Hero
    {
        [Key]
        public int Id { get; set; }
        public string Name { get; set; }

    }

    public class HeroContext : DbContext
    {
        public DbSet Heroes { get; set; }
    }

}

APIの実装

RESTのAPIの実装を追加します。GET, POST, DELETE, PUTを追加します。

まず、Controllersの下にデフォルトで生成された ValuesController.cs の名前をモデル名に合わせて HeroesController.cs に変更します。

HeroesController.csにModelへの参照を追加します。

using SuperHero2.Models;

まずはGetです。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-comment"><span class="hljs-doctag">///</span></span>
<span class="hljs-comment"><span class="hljs-doctag">///</span> ヒーローのリストを取得する。</span>
<span class="hljs-comment"><span class="hljs-doctag">///</span> </span>
<span class="hljs-comment"><span class="hljs-doctag">///</span> </span>
<span class="hljs-comment"><span class="hljs-doctag">///</span> ODataのインターフェイスをサポートするように戻り値をIQueryable(T)型に変更した。</span>
<span class="hljs-comment"><span class="hljs-doctag">///</span> </span>
<span class="hljs-comment"><span class="hljs-doctag">///</span> ヒーローのリスト(JSON).</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> IQueryable <span class="hljs-title">Get</span>(<span class="hljs-params"></span>)</span> {
List heroes;
<span class="hljs-keyword">using</span> (<span class="hljs-keyword">var</span> context = <span class="hljs-keyword">new</span> HeroContext()) {
heroes = context.Heroes.ToList();
}
<span class="hljs-comment">//ListをIQueryableに変換する。</span>
<span class="hljs-keyword">return</span> heroes.AsQueryable();
}
<span class="hljs-comment"><span class="hljs-doctag">///</span></span> <span class="hljs-comment"><span class="hljs-doctag">///</span> ヒーローのリストを取得する。</span> <span class="hljs-comment"><span class="hljs-doctag">///</span> </span> <span class="hljs-comment"><span class="hljs-doctag">///</span> </span> <span class="hljs-comment"><span class="hljs-doctag">///</span> ODataのインターフェイスをサポートするように戻り値をIQueryable(T)型に変更した。</span> <span class="hljs-comment"><span class="hljs-doctag">///</span> </span> <span class="hljs-comment"><span class="hljs-doctag">///</span> ヒーローのリスト(JSON).</span> <span class="hljs-function"><span class="hljs-keyword">public</span> IQueryable <span class="hljs-title">Get</span>(<span class="hljs-params"></span>)</span> { List heroes; <span class="hljs-keyword">using</span> (<span class="hljs-keyword">var</span> context = <span class="hljs-keyword">new</span> HeroContext()) { heroes = context.Heroes.ToList(); } <span class="hljs-comment">//ListをIQueryableに変換する。</span> <span class="hljs-keyword">return</span> heroes.AsQueryable(); }
///
/// ヒーローのリストを取得する。
/// 
/// 
/// ODataのインターフェイスをサポートするように戻り値をIQueryable(T)型に変更した。
/// 
/// ヒーローのリスト(JSON).
public IQueryable Get() {
    List heroes;
    using (var context = new HeroContext()) {
        heroes = context.Heroes.ToList();
    }
    //ListをIQueryableに変換する。
    return heroes.AsQueryable();
        
}

エンティティモデルをリスト化して返しています。

次はPOST。

前回は複数のヒーローをリストで一気に塚で着るようにしていましたが、今回は名前を渡すと一人のヒーローを追加するようにしました。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-comment">///</span>
<span class="hljs-comment">/// ヒーローを追加する</span>
<span class="hljs-comment">/// </span>
<span class="hljs-comment">///追加するヒーローの名前</span>
<span class="hljs-comment">/// </span>
<span class="hljs-comment">/// Fiddler2などでのテストの仕方</span>
<span class="hljs-comment">/// httpヘッダに追加</span>
<span class="hljs-comment">/// Accept: text/html</span>
<span class="hljs-comment">/// Content-Type: text/json; charset=utf-8</span>
<span class="hljs-comment">/// ボディに追加するヒーローの名前をutf-8でダブルクォート(")で囲う。</span>
<span class="hljs-comment">/// 例:"仮面ライダー1号"</span>
<span class="hljs-comment">/// </span>
<span class="hljs-comment">/// 追加に成功した場合201を返して、追加したヒーローを返す。</span>
public HttpResponseMessage <span class="hljs-constructor">Post([FromBody]<span class="hljs-params">string</span> <span class="hljs-params">heroName</span>)</span> {
Hero _hero;
using (var context = <span class="hljs-keyword">new</span> <span class="hljs-constructor">HeroContext()</span>) {
_hero = <span class="hljs-keyword">new</span> <span class="hljs-constructor">Hero()</span>;
_hero.Name = heroName;
context.Heroes.<span class="hljs-constructor">Add(<span class="hljs-params">_hero</span>)</span>;
context.<span class="hljs-constructor">SaveChanges()</span>;
<span class="hljs-comment">//201でレスポンスメッセージを作成する。</span>
return Request.<span class="hljs-constructor">CreateResponse(HttpStatusCode.Created, <span class="hljs-params">_hero</span>)</span>;
}
}
<span class="hljs-comment">///</span> <span class="hljs-comment">/// ヒーローを追加する</span> <span class="hljs-comment">/// </span> <span class="hljs-comment">///追加するヒーローの名前</span> <span class="hljs-comment">/// </span> <span class="hljs-comment">/// Fiddler2などでのテストの仕方</span> <span class="hljs-comment">/// httpヘッダに追加</span> <span class="hljs-comment">/// Accept: text/html</span> <span class="hljs-comment">/// Content-Type: text/json; charset=utf-8</span> <span class="hljs-comment">/// ボディに追加するヒーローの名前をutf-8でダブルクォート(")で囲う。</span> <span class="hljs-comment">/// 例:"仮面ライダー1号"</span> <span class="hljs-comment">/// </span> <span class="hljs-comment">/// 追加に成功した場合201を返して、追加したヒーローを返す。</span> public HttpResponseMessage <span class="hljs-constructor">Post([FromBody]<span class="hljs-params">string</span> <span class="hljs-params">heroName</span>)</span> { Hero _hero; using (var context = <span class="hljs-keyword">new</span> <span class="hljs-constructor">HeroContext()</span>) { _hero = <span class="hljs-keyword">new</span> <span class="hljs-constructor">Hero()</span>; _hero.Name = heroName; context.Heroes.<span class="hljs-constructor">Add(<span class="hljs-params">_hero</span>)</span>; context.<span class="hljs-constructor">SaveChanges()</span>; <span class="hljs-comment">//201でレスポンスメッセージを作成する。</span> return Request.<span class="hljs-constructor">CreateResponse(HttpStatusCode.Created, <span class="hljs-params">_hero</span>)</span>; } }
///
/// ヒーローを追加する
/// 
///追加するヒーローの名前
/// 
/// Fiddler2などでのテストの仕方
/// httpヘッダに追加
///   Accept: text/html
///   Content-Type: text/json; charset=utf-8
/// ボディに追加するヒーローの名前をutf-8でダブルクォート(")で囲う。
///   例:"仮面ライダー1号"
/// 
/// 追加に成功した場合201を返して、追加したヒーローを返す。
public HttpResponseMessage Post([FromBody]string heroName) {
    Hero _hero;
    using (var context = new HeroContext()) {
        _hero = new Hero();
        _hero.Name = heroName;
        context.Heroes.Add(_hero);
        context.SaveChanges();
        //201でレスポンスメッセージを作成する。
        return Request.CreateResponse(HttpStatusCode.Created, _hero);
    }
}

Web APIのメソッド引数に[FormUrl]属性がついた場合か、何も指定が無い場合には、Urlより引数パースしようとします。[FromBody]属性がついた場合にはHTTPメッセージのボディのデータより引数のデータをパースしようとします。

引数でな与えられた名前で、EFを使ってヒーローのレコードを追加し、成功した場合には201を返すように処理しています。

次はDELETE。

Idを引数にとって、そのIdのヒーローを削除します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-comment">///</span>
<span class="hljs-comment">/// 引数で与えられたヒーローを削除する。</span>
<span class="hljs-comment">/// </span>
<span class="hljs-comment">/// </span>
<span class="hljs-comment">/// 引数idで与えられたヒーローが存在しない場合には404エラーが発生する。</span>
<span class="hljs-comment">/// </span>
<span class="hljs-comment">///</span>
public void <span class="hljs-constructor">Delete(<span class="hljs-params">int</span> <span class="hljs-params">id</span>)</span> {
Hero _hero;
using (var context = <span class="hljs-keyword">new</span> <span class="hljs-constructor">HeroContext()</span>) {
_hero = context.Heroes.<span class="hljs-constructor">Find(<span class="hljs-params">id</span>)</span>;
<span class="hljs-keyword">if</span> (_hero != null) {
context.Heroes.<span class="hljs-constructor">Remove(<span class="hljs-params">_hero</span>)</span>;
context.<span class="hljs-constructor">SaveChanges()</span>;
}
<span class="hljs-keyword">else</span> {
<span class="hljs-comment">//idのヒーローが見つからない場合には404を返す。</span>
throw <span class="hljs-keyword">new</span> <span class="hljs-constructor">HttpResponseException(HttpStatusCode.NotFound)</span>;
}
}
}
<span class="hljs-comment">///</span> <span class="hljs-comment">/// 引数で与えられたヒーローを削除する。</span> <span class="hljs-comment">/// </span> <span class="hljs-comment">/// </span> <span class="hljs-comment">/// 引数idで与えられたヒーローが存在しない場合には404エラーが発生する。</span> <span class="hljs-comment">/// </span> <span class="hljs-comment">///</span> public void <span class="hljs-constructor">Delete(<span class="hljs-params">int</span> <span class="hljs-params">id</span>)</span> { Hero _hero; using (var context = <span class="hljs-keyword">new</span> <span class="hljs-constructor">HeroContext()</span>) { _hero = context.Heroes.<span class="hljs-constructor">Find(<span class="hljs-params">id</span>)</span>; <span class="hljs-keyword">if</span> (_hero != null) { context.Heroes.<span class="hljs-constructor">Remove(<span class="hljs-params">_hero</span>)</span>; context.<span class="hljs-constructor">SaveChanges()</span>; } <span class="hljs-keyword">else</span> { <span class="hljs-comment">//idのヒーローが見つからない場合には404を返す。</span> throw <span class="hljs-keyword">new</span> <span class="hljs-constructor">HttpResponseException(HttpStatusCode.NotFound)</span>; } } }
///
/// 引数で与えられたヒーローを削除する。
/// 
/// 
/// 引数idで与えられたヒーローが存在しない場合には404エラーが発生する。
/// 
///
public void Delete(int id) {
    Hero _hero;
    using (var context = new HeroContext()) {
        _hero = context.Heroes.Find(id);
        if (_hero != null) {
            context.Heroes.Remove(_hero);
            context.SaveChanges();
        }
        else {
            //idのヒーローが見つからない場合には404を返す。
            throw new HttpResponseException(HttpStatusCode.NotFound);
        }
    }
}

次はPUT。

URL引数のidで与えられたIdのヒーローに対して、ボディに書かれている新しい名前でヒーローの名前委を変更します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-comment">///</span>
<span class="hljs-comment">/// ヒーローの名前を変更する</span>
<span class="hljs-comment">/// </span>
<span class="hljs-comment">///名前を変更したいヒーローのid</span>
<span class="hljs-comment">///変更する名前</span>
<span class="hljs-comment">/// </span>
<span class="hljs-comment">/// Fiddler2などでのテストの仕方</span>
<span class="hljs-comment">/// URL</span>
<span class="hljs-comment">/// http://(hostname):(port)/api/heroes?id=(変更するid)</span>
<span class="hljs-comment">/// httpヘッダに追加</span>
<span class="hljs-comment">/// Accept: text/html</span>
<span class="hljs-comment">/// Content-Type: text/json; charset=utf-8</span>
<span class="hljs-comment">/// ボディに追加するヒーローの名前をutf-8でダブルクォート(")で囲う。</span>
<span class="hljs-comment">/// 例:"仮面ライダー1号"</span>
<span class="hljs-comment">/// </span>
<span class="hljs-comment">/// </span>
<span class="hljs-comment">/// 変更に成功した場合には200を返して、変更結果を返す。</span>
<span class="hljs-comment">/// </span>
public HttpResponseMessage <span class="hljs-constructor">Put(<span class="hljs-params">int</span> <span class="hljs-params">id</span>, [FromBody]<span class="hljs-params">string</span> <span class="hljs-params">name</span>)</span> {
using (var context = <span class="hljs-keyword">new</span> <span class="hljs-constructor">HeroContext()</span>) {
var _hero = context.Heroes.<span class="hljs-constructor">Find(<span class="hljs-params">id</span>)</span>;
<span class="hljs-keyword">if</span> (_hero != null) {
_hero.Name = name;
context.<span class="hljs-constructor">SaveChanges()</span>;
return Request.<span class="hljs-constructor">CreateResponse(HttpStatusCode.OK, <span class="hljs-params">_hero</span>)</span>;
}
<span class="hljs-keyword">else</span> {
<span class="hljs-comment">//idのヒーローが見つからない場合には404を返す。</span>
throw <span class="hljs-keyword">new</span> <span class="hljs-constructor">HttpResponseException(HttpStatusCode.NotFound)</span>;
}
}
}
<span class="hljs-comment">///</span> <span class="hljs-comment">/// ヒーローの名前を変更する</span> <span class="hljs-comment">/// </span> <span class="hljs-comment">///名前を変更したいヒーローのid</span> <span class="hljs-comment">///変更する名前</span> <span class="hljs-comment">/// </span> <span class="hljs-comment">/// Fiddler2などでのテストの仕方</span> <span class="hljs-comment">/// URL</span> <span class="hljs-comment">/// http://(hostname):(port)/api/heroes?id=(変更するid)</span> <span class="hljs-comment">/// httpヘッダに追加</span> <span class="hljs-comment">/// Accept: text/html</span> <span class="hljs-comment">/// Content-Type: text/json; charset=utf-8</span> <span class="hljs-comment">/// ボディに追加するヒーローの名前をutf-8でダブルクォート(")で囲う。</span> <span class="hljs-comment">/// 例:"仮面ライダー1号"</span> <span class="hljs-comment">/// </span> <span class="hljs-comment">/// </span> <span class="hljs-comment">/// 変更に成功した場合には200を返して、変更結果を返す。</span> <span class="hljs-comment">/// </span> public HttpResponseMessage <span class="hljs-constructor">Put(<span class="hljs-params">int</span> <span class="hljs-params">id</span>, [FromBody]<span class="hljs-params">string</span> <span class="hljs-params">name</span>)</span> { using (var context = <span class="hljs-keyword">new</span> <span class="hljs-constructor">HeroContext()</span>) { var _hero = context.Heroes.<span class="hljs-constructor">Find(<span class="hljs-params">id</span>)</span>; <span class="hljs-keyword">if</span> (_hero != null) { _hero.Name = name; context.<span class="hljs-constructor">SaveChanges()</span>; return Request.<span class="hljs-constructor">CreateResponse(HttpStatusCode.OK, <span class="hljs-params">_hero</span>)</span>; } <span class="hljs-keyword">else</span> { <span class="hljs-comment">//idのヒーローが見つからない場合には404を返す。</span> throw <span class="hljs-keyword">new</span> <span class="hljs-constructor">HttpResponseException(HttpStatusCode.NotFound)</span>; } } }
///
/// ヒーローの名前を変更する
/// 
///名前を変更したいヒーローのid
///変更する名前
/// 
/// Fiddler2などでのテストの仕方
/// URL
///   http://(hostname):(port)/api/heroes?id=(変更するid)
/// httpヘッダに追加
///   Accept: text/html
///   Content-Type: text/json; charset=utf-8
/// ボディに追加するヒーローの名前をutf-8でダブルクォート(")で囲う。
///   例:"仮面ライダー1号"
/// 
/// 
/// 変更に成功した場合には200を返して、変更結果を返す。
/// 
public HttpResponseMessage Put(int id, [FromBody]string name) {
    using (var context = new HeroContext()) {
        var _hero = context.Heroes.Find(id);
        if (_hero != null) {
            _hero.Name = name;
            context.SaveChanges();
            return Request.CreateResponse(HttpStatusCode.OK, _hero);
        }
        else {
            //idのヒーローが見つからない場合には404を返す。
            throw new HttpResponseException(HttpStatusCode.NotFound);
        }
    }
}

基礎的な内容は以上のようになります。

以前のWCF Web APIとの大きな違いはコントローラの各APIメソッドに対して属性が無くなり、それよりもメソッド名でRESTのどの命令に対応したメソッドかわかるようにしており、属性という設定が廃されてASP.NET MVCの「設定より規約」が徹底されています。

WCF Web APIのときにはWCFなので、サービスのテストページが自動的に作成されていましたが、ASP.NET Web APIとなって、現在その機能は実装されていませんので、Fiddllerなどでテストされる場合のhttpヘッダの設定等については、コード中のXMLコメントに書いています。またヘルプページの自動作成機能も無くなっています。ただ、ASP.NETのロードマップには両方とも含まれているので、いずれこれらの機能も追加されるかもしれません。

今回GitHubでソースコードを公開していますので、全体コードを確認されたい方や、ご興味をもたれた方は下をご確認下さい。

GitHub - ishisaka/superhero2: SuperHero2
SuperHero2. Contribute to ishisaka/superhero2 development by creating an account on GitHub.

コメント

  1. […] ASP.NET MVC クライアント編サーバー編に続き、クライアント側についてもWCF Web APIの場合と同様にコンソールアプリケーションでクライアントライブラリの使用方法を見ていきます。 […]

タイトルとURLをコピーしました