Web APIといっても、どのようなURLの、どのようなAPIを定義すればよいのか? RESTfulなHTTPサービスを実装するためのAPIの定義方法の基礎を説明する。
本連載は、ASP.NET Web APIを基礎から解説している。前回は、簡単なHello, Worldコードを確認しただけであったが、今回から本格的な実装の解説に入る。
ASP.NET Web APIにおいて、最低限必要になる実装は、「ルーティングの設定」と「APIコントローラの実装」である。今回〜次回は、そのうちの「APIコントローラの実装」について解説を行う*1。
開発環境はMicrosoft Visual Studio Express 2012 for Web(Update3)、言語はC#、対象とするASP.NET Web APIのバージョンは1とする*2。
*1 ルーティングの設定については、第4回目で紹介する予定だ。
*2 開発環境は、本稿執筆時点でリリースされているMicrosoft Visual Studio Express 2013 for Webでも構わない。ただし、プロジェクト・テンプレートに含まれるASP.NET Web APIのバージョンは2なので注意。 以降の解説にて、バージョン2との違いがある場合は随時補足する。
本稿(今回〜次回)の目次は、以下のとおりとなる。
【今回】
1. RESTfulなAPIの設計を学ぼう
1-1. REST とは
1-2. HTTPメソッドは、リソースをどのように操作したいかを表す
1-3. URLはリソースの名前を表す
1-4. APIの処理の結果は、ステータス・コードで表す
【次回】
2. APIコントローラの実装
2-1. APIコントローラの役割とは
2-2. APIコントローラの用意
2-3. HTTPリクエストを取得する
2-4. HTTPレスポンスの内容を指定する
今回〜次回は、より実践的なAPIコントローラの実装方法を学ぶため、以下の2つの要素を取り入れて解説する。
実装に入る前にはまず、どのようなAPIを定義するかを決定する必要がある。ASP.NET Web APIは、RESTfulなHTTPサービスを提供することに特化したフレームワークであるため、初めにRESTfulなAPIを定義する方法について紹介する。
また、APIの振る舞いは、データの基本処理であるCRUDを想定し、サーバとクライアントの間でどのようなHTTPリクエスト、HTTPレスポンスのやりとりが行われるか、例を挙げながら解説する。APIコントローラを実装する際は、ぜひ本稿を参考にしていただきたい。
本稿で扱う要素 ―― RESTfulなAPIとCRUDを備える、APIコントローラ ―― のサンプル・コードは、Visual Studio 2012(または、Visual Studio Express 2012 for Web)のスキャフォールディング機能*3により自動生成されるAPIコントローラ・クラスのコードだ。ASP.NET Web APIのスキャフォールディング機能は、コードの記述を簡略化するだけでなく、ASP.NET Web APIの基本パターンを実装しているという側面も併せ持つ。
スキャフォールディングにより自動生成されるAPIコントローラ・クラスのコード(リスト1)と、定義されるAPIを図で表したもの(図1)を掲載する。CRUDの対象となるデータは、例として顧客データ(今回のサンプルのために定義したCustomerクラス)とした。以降、このAPIコントローラ・クラスで定義されるAPI(顧客APIと呼称する)を引用しながら解説を行うので、今は簡単に眺め、必要なときに参照してほしい。
*3 スキャフォールディング機能とは、開発者が定義するモデルから、APIコントローラ・クラスのコードを自動生成する機能だ。この機能は、ASP.NET MVCにおいても有効だ(参考:「ASP.NET MVC入門:第2回 スキャフォールディング機能で軽々DB連携アプリケーション」)。
ASP.NET Web APIのスキャフォールディング機能を利用する手順については、「Windows Azure ドキュメント: ASP.NET Web API と SQL データベースを使用したモバイル対応の REST サービス」を参考のこと。
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using MyApiProject.Models;
namespace MyApiProject.Controllers
{
public class CustomersController : ApiController
{
// (1)
private MyApiProjectContext db = new MyApiProjectContext();
// GET ~/api/Customers
public IEnumerable<Customer> GetCustomers()
{
// Customersテーブルの全てのデータを取得し、
// IEnumerable<Customer>型に変換して返す。
return db.Customers.AsEnumerable();
}
// GET ~/api/Customers/5
public Customer GetCustomer(int id)
{
// Customersテーブルから、idが一致するデータを取得
Customer customer = db.Customers.Find(id);
if (customer == null)
{
// 一致するデータ存在しない場合は、ステータス・コード404を返す。
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
// Customerデータを返す。
return customer;
}
// PUT ~/api/Customers/5
public HttpResponseMessage PutCustomer(int id, Customer customer)
{
if (!ModelState.IsValid)
{
// モデルに検証エラーが存在する場合は、ステータス・コード400とエラー内容を返す。
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
if (id != customer.Id)
{
// URLで指定したidと、HTTPリクエストのボディで指定したidが一致しない場合は、
// ステータス・コード400を返す。
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
// Customerデータを更新する。
db.Entry(customer).State = EntityState.Modified;
try
{
// データベースに変更をコミットする。
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
// データベースの変更中に例外が発生した場合は、
// ステータス・コード404と例外のメッセージを返す。
return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
}
// データの変更処理が正常に終了した場合、ステータス・コード200を返す。
return Request.CreateResponse(HttpStatusCode.OK);
}
// POST ~/api/Customers
public HttpResponseMessage PostCustomer(Customer customer)
{
if (ModelState.IsValid)
{
// Customersテーブルに新しいデータを追加する。
db.Customers.Add(customer);
db.SaveChanges();
// ステータス・コード201と、追加したCustomerデータ、
// 追加したCustomerデータを取得するためのURLを返す。
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, customer);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = customer.Id }));
return response;
}
else
{
// モデルに検証エラーが存在する場合は、ステータス・コード400とエラー内容を返す。
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
// DELETE ~/api/Customers/5
public HttpResponseMessage DeleteCustomer(int id)
{
// Customersテーブルから、idが一致するデータを取得
Customer customer = db.Customers.Find(id);
if (customer == null)
{
// 一致するデータ存在しない場合は、ステータス・コード404を返す。
return Request.CreateResponse(HttpStatusCode.NotFound);
}
// Customersテーブルからデータを削除する
db.Customers.Remove(customer);
try
{
// データベースに変更をコミットする。
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
// データベースの変更中に例外が発生した場合は、
// ステータス・コード404と例外のメッセージを返す。
return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
}
// 削除処理が正常に終了した場合、ステータス・コード200と、
// 削除したCustomerデータを返す。
return Request.CreateResponse(HttpStatusCode.OK, customer);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
}
*4 Entity Frameworkについては「ADO.NET Entity Framework入門」、Entity Frameworkのコード・ファーストについては『第1回 EF 4.1の目玉機能「コード・ファースト」を理解しよう』を参考のこと。
Copyright© Digital Advantage Corp. All Rights Reserved.