C#
 Computer >> コンピューター >  >> プログラミング >> C#

C# ASP.NET Web APIでクエリ文字列パラメータを使ってAPIバージョニングを実装する方法


ASP.NET Web APIにおいて、DefaultHttpControllerSelectorクラスは、URIでリクエストされたコントローラのアクションメソッドを選択する役割を担っています。

ここでは、次のようにクエリ文字列を使ってAPIのバージョン管理を実装するケースを考えてみましょう。

v=1 → StudentsV1Controller(バージョン1)
v=2 → StudentsV2Controller(バージョン2)

たとえば https://localhost:58174/api/student?v=1 のようにクエリ文字列でバージョン情報を渡した場合、404 Not Foundエラーが返されます。その理由は、DefaultHttpControllerSelectorSelectController()メソッドが「StudentsController」という名前のコントローラを探しに行くためです。プロジェクトにはStudentsV1ControllerとStudentsV2Controllerしか存在しないため、該当するコントローラが見つからずエラーとなってしまいます。

C# ASP.NET Web APIでクエリ文字列パラメータを使ってAPIバージョニングを実装する方法

この問題を解決するには、DefaultHttpControllerSelectorクラスを継承した独自のCustomControllerSelectorを実装する必要があります。

CustomControllerSelectorの実装例

using System.Net.Http;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
namespace WebAPI.Custom{
    public class CustomControllerSelector : DefaultHttpControllerSelector{
        private HttpConfiguration _config;
        public CustomControllerSelector(HttpConfiguration config) : base(config){
            _config = config;
        }
        public override HttpControllerDescriptor SelectController(HttpRequestMessage
        request){
            var controllers = GetControllerMapping();
            var routeData = request.GetRouteData();
            var controllerName = routeData.Values["controller"].ToString();
            string versionNumber = "1";
            var versionQueryString =
            HttpUtility.ParseQueryString(request.RequestUri.Query);
            if (versionQueryString["v"] != null){
                versionNumber = versionQueryString["v"];
            }
            if (versionNumber == "1"){
                controllerName = controllerName + "V1";
            }
            else if (versionNumber == "2"){
                controllerName = controllerName + "V2";
            }
            HttpControllerDescriptor controllerDescriptor;
            if (controllers.TryGetValue(controllerName, out controllerDescriptor)){
                return controllerDescriptor;
            }
            return null;
        }
    }
}

処理の流れ

  • GetControllerMapping()メソッドで、アプリケーション内のすべてのコントローラのマッピング情報を取得します。
  • ルートデータからコントローラ名(例:student)を取り出します。
  • クエリ文字列から「v」パラメータの値を読み取ります。指定がない場合はデフォルト値として「1」を使用します。
  • バージョン番号に応じて、コントローラ名の末尾に「V1」または「V2」を付加します(例:student → studentV1)。
  • マッピングの中から該当するコントローラを検索し、見つかった場合はそのHttpControllerDescriptorを返します。

続いて、デフォルトのコントローラセレクタをカスタムコントローラセレクタに置き換える必要があります。この設定はWebApiConfig.csファイルで行います。IHttpControllerSelectorCustomControllerSelectorで置き換えている点に注目してください。DefaultHttpControllerSelectorはIHttpControllerSelectorインターフェースを実装しているため、このインターフェースを差し替える形で対応できるのです。

WebApiConfig.csでの登録例

public static class WebApiConfig{
    public static void Register(HttpConfiguration config){
        config.Services.Replace(typeof(IHttpControllerSelector), new
        CustomControllerSelector(config));
        config.MapHttpAttributeRoutes();
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

StudentV1Controllerの実装例

using DemoWebApplication.Models;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace DemoWebApplication.Controllers{
    public class StudentV1Controller : ApiController{
        List<StudentV1> students = new List<StudentV1>{
            new StudentV1{
                Id = 1,
                Name = "Mark"
            },
            new StudentV1{
                Id = 2,
                Name = "John"
            }
        };
        public IEnumerable<StudentV1> Get(){
            return students;
        }
        public StudentV1 Get(int id){
            var studentForId = students.FirstOrDefault(x => x.Id == id);
            return studentForId;
        }
    }
}

バージョン1では、学生データをIdとNameというシンプルな構造で返しています。

StudentV2Controllerの実装例

using DemoWebApplication.Models;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace DemoWebApplication.Controllers{
    public class StudentV2Controller : ApiController{
        List<StudentV2> students = new List<StudentV2>{
            new StudentV2{
                Id = 1,
                FirstName = "Roger",
                LastName = "Federer"
            },
            new StudentV2{
                Id = 2,
                FirstName = "Tom",
                LastName = "Bruce"
            }
        };
        public IEnumerable<StudentV2> Get(){
            return students;
        }
        public StudentV2 Get(int id){
            var studentForId = students.FirstOrDefault(x => x.Id == id);
            return studentForId;
        }
    }
}

バージョン2では、NameフィールドがFirstNameとLastNameに分割され、より詳細なデータ構造になっています。このように、同じエンドポイントでありながら、バージョンごとに異なるレスポンス形式を提供することが可能になります。

実行結果

以下の出力は、クエリ文字列によるバージョニングを使用した際に、StudentV1コントローラとStudentV2コントローラそれぞれから返される結果を示しています。

C# ASP.NET Web APIでクエリ文字列パラメータを使ってAPIバージョニングを実装する方法

  1. C# ASP.NET Web APIのアクションメソッドからカスタム結果タイプを返す方法

    ASP.NET Web APIでは、IHttpActionResultインターフェースを実装することで、独自のカスタムクラスを結果タイプとして作成できます。IHttpActionResultインターフェースには、HttpResponseMessageインスタンスを非同期に生成する単一のメソッド ExecuteAsync が定義されています。public interface IHttpActionResult { Task<HttpResponseMessage> ExecuteAsync( CancellationToken cancellationToke

  2. C# ASP.NET Web APIにおけるコントローラーアクションの4つの戻り値の型とは?

    ASP.NET Web APIのアクションメソッドには、主に以下の4種類の戻り値の型を指定することができます。 void(戻り値なし) プリミティブ型/複合型 HttpResponseMessage IHttpActionResult 1. void(戻り値なし) すべてのアクションメソッドが必ずしも何かを返す必要はありません。戻り値の型としてvoidを指定することも可能です。 コード例 using DemoWebApplication.Models; using System.Web.Http; namespace DemoWebApplication.Controllers {