読者です 読者をやめる 読者になる 読者になる

miso_soup3 Blog

主に ASP.NET 関連について書いています。

ASP.NET Web API 2.1 その 1

ASP.NET Web API 2.1 で追加された機能をみてみました。

概要

☆が付いているのは、クライアント側の HttpClient ライブラリにも関係する更新です。

  • Global Error Handling
  • 属性ベースルーティングの改善
  • ヘルプページの機能追加
  • IgnoreRoute のサポート
  • BSON Media Type Formatter の追加(☆クライアント側も)
  • フィルター属性の非同期対応
  • クエリ文字列の解析と構築(☆クライアント側も)
  • バグ修正

リリースノート:What's New in ASP.NET Web API 2.1 : The Official Microsoft ASP.NET Site
NuGet:Microsoft ASP.NET Web API 2.1 5.1.0

Global Error Handling

長くなったので ASP.NET Web API 2.1 その 2 ~Global Error Handling~ にて記載。

属性ベースルーティングの改善

属性ベースルーティングにてルート制約(IHttpRouteConstraint)を追加できるようになりました。これにより、ルーティングの条件にてヘッダーの値を参照するといったことが可能になりました。
ASP.NET Web API 2.1 と ASP.NET MVC 5.1 両方で更新された機能です。

サンプル では、ヘッダー「api-version」で API のバージョンを指定し、該当する ApiController にマッピングするといったことを行っています。
こんな感じ↓

[VersionedRoute("api/Customer", 1)]
public class CustomerVersion1Controller : ApiController {}

[VersionedRoute("api/Customer", 2)]
public class CustomerVersion2Controller : ApiController { }

// VersionedRoute 属性はサンプル内で実装されているクラス

利用するには、RouteFactoryAttribute クラスを継承した属性クラスを作成し、プロパティにルート制約(IHttpRouteContraint)を指定し、属性を付与する、といった流れになります。
RouteFactoryAttribute.cs には、DataToken や Defaults のプロパティがあるので、他にもいろいろ指定できるかもしれません。

今までは

今までは、↓“{id:int}”のようにパラメーターによる制約しか利用できませんでした。

[Route("users/{id:int}"]
public User GetUserById(int id) { ... }

(ちなみにこの “int”といった文字列指定による制約は、カスタマイズ可能です。参照:ASP.NET Web API 2 – Route Constraints

ヘルプページの機能追加

ヘルプページに次の2つの機能が追加されました。

  • API のパラメーターと戻り値に定義される型の情報、型専用ページの追加。
  • Data Annotations 属性の情報。

ヘルプページの NuGet パッケージは「Microsoft.AspNet.WebApi.HelpPage -Version 5.1.0」になります。

例として、↓のように Data Annotations 属性を付けた Book クラスと、戻り値・パラメーターがともに Book クラスであるAPI の場合を見てみます。

/// <summary>
/// 本
/// </summary>
public class Book
{
	public int Id { get; set; }
	/// <summary>
	/// タイトル
	/// </summary>
	[Required]
	public string Title { get; set; }
	/// <summary>
	/// 番号
	/// </summary>
	[Range(1, 100)]
	public int Number { get; set; }
	/// <summary>
	/// ノート
	/// </summary>
	[StringLength(100, MinimumLength = 3)]
	public string Note { get; set; }
}
public class BooksController : ApiController
{
	public Book PostBook(Book book);
}

Book クラスのドキュメントページが新たに追加され、API ページにはそのリンクと、型や Data Annotations 属性の情報と共に、パラメーターと戻り値の説明が表示されます。

(↓の左は以前のバージョンのものです。)
f:id:miso_soup3:20140122005849p:plain

プロパティが複合型の場合は、その型専用ページへのリンクが貼ってあります。Enum も対応。

f:id:miso_soup3:20140122010025p:plain

今回の更新により、クエリ文字列を↓のように [FromUri] を付けてクラスで定義した場合でも、どのようなクエリ文字のキーでリクエストすれば良いか分かるようになりました。

public ItemDetails GetBooks([FromUri]Book parameter);

ヘルプページ作成のために使用されるクラスは、以前と同様にプロジェクト内に含まれるため、カスタマイズは可能です。独自の検証属性への対応も簡単そうです。

f:id:miso_soup3:20140122010309p:plain

IgnoreRoute のサポート

ASP.NET MVC と同じように、ルーティングの設定にて特定のルートを無視する設定ができるようになりました。↓のように、IgnoreRoute メソッドで無視したい URL パターンを定義します。

public static class WebApiConfig
{
	public static void Register(HttpConfiguration config)
	{
		//↓無視ルート追加 api/values は無視するように設定。
		config.Routes.IgnoreRoute(
			routeName: "IgnoreRoute", 
			routeTemplate: "api/values");
		// ↓デフォルトのルート
		config.Routes.MapHttpRoute(
			name: "DefaultApi",
			routeTemplate: "api/{controller}/{id}",
			defaults: new { id = RouteParameter.Optional }
		);			
	}
}

この例の場合、「api/values」でアクセスすると 404 Not Found を返します。
これまでと同様に記述する順番に注意。(例の場合、デフォルトのルートの後に IgnoreRoute(…) を記述すると、「api/values」はデフォルトのルートの方にマッチします。)

BSON Media Type Formatter の追加

System.Net.Http.Formatting ライブラリに、コンテンツタイプ「application/bson」に対応する BsonMediaTypeFormatter クラスが追加されました。クライアント(HttpClient)とサーバー側双方での対応になります。

サーバー側で対応する場合は、↓のように Media Type Formatter を追加する必要があります。

using System.Net.Http.Formatting;
using System.Web.Http;

namespace WebApplication39
{
	public static class WebApiConfig
	{
		public static void Register(HttpConfiguration config)
		{
			config.Formatters.Add(new BsonMediaTypeFormatter());
			…

公式にてサンプルが用意されています。:BSONSample
BSON についてはこちら:http://bsonspec.org/

フィルター属性の非同期対応

既存の3つのフィルター属性にて非同期がサポートされました。

  • AuthorizationFilterAttribute
  • ActionFilterAttribute
  • ExceptionFilterAttribute

(ASP.NET Web API 2.0 で追加された IAuthenticationFilter は非同期対応で、もともと同期で処理する実装クラスが用意されてません。)

今までは、OnActionExecuting メソッド等に処理を記述していましたが、新たに用意された On*Async メソッドをオーバーライドすることで、非同期に処理することができます。

例えば、ActionFilterAttribute クラスを継承してカスタム属性を作成する場合、以下の4つのメソッドをオーバーライドすることができます。

using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Threading;
using System.Threading.Tasks;

namespace WebApplication39.Models
{
	public class MyFilterAttribute : ActionFilterAttribute
	{
		public override void OnActionExecuting(HttpActionContext actionContext)
		{
		}
		public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
		{
		}
		public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
		{
			//await Task.Delay(1000);
		}
		public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
		{
			//await Task.Delay(1000);
		}	
	}
}

後半の 2 つの On*Async が新たに用意されたメソッドです。他のフィルター属性でも、既存の同期用メソッドに対応して、非同期用のメソッドが新たに用意されています。

同期と非同期の両方をオーバーライドしてコードを書いたらどうなるの?と気になるところです。On*Async をオーバーライドした場合、同期の方のメソッドである On* メソッドは実行されません。(On*Async をオーバーライドせずに On* メソッドを記述すると、On* メソッドは実行されます。)

また、もともと Web API のフィルター属性は非同期設計されているので、2.1 にしなくともActionFilterAttribute クラスのように自前で実装すれば実現できます。

クエリ文字列の解析と構築

これは、クライアント側の Portable Class Library の更新になります。System.Net.Http.Formatting ライブラリにて、クエリ文字列の解析・構築用の HttpValueCollection クラスが公開されます。
(ASP.NET Web API プロジェクトでは利用できない?)

内容は、リリースノートにあるサンプルコードを見たほうが早いです。
Query parsing support for the client formatting library

NuGet : Microsoft ASP.NET Web API 2.1 Client Libraries 5.1.0
HttpValueCollection クラス(System.Net.Http.Formatting)


続きの記事:ASP.NET Web API 2.1 その 2 ~Global Error Handling~