ASP.NET Web API パイプライン拡張サンプル


ASP.NET Web API パイプラインを拡張するサンプルです。
サンプルコードのみですが、内容を忘れないための備忘録として、、、

●参照
[ASP.NET Web API 2](https://msdn.microsoft.com/ja-jp/library/Dn448365%28v=VS.118%29.aspx?f=255&MSPPError=-2147217396)
[ASP.NET Web API HTTP メッセージライフサイクルポスター](https://blogs.msdn.microsoft.com/chack/2013/02/17/asp-net-web-api-http/)

# サンプルソース
## Program.cs
```csharp
    class Program
    {
        private static string baseAddress = "http://localhost:9000/";

        static void Main(string[] args)
        {
            using (WebApp.Start<Startup>(url: baseAddress))
            {
                HttpClient client = new HttpClient();

                var response = client.GetAsync(baseAddress + "MySelfHost/Values").Result;

                Console.WriteLine(response);
                Console.WriteLine(response.Content.ReadAsStringAsync().Result);
            }

            Console.ReadLine();
        }
    }
```

## ValuesController.cs
```csharp
    public class ValuesController : ApiController
    {
        public IEnumerable<string> Get()
        {
            return new string[] { "Value1", "Value2" };
        }

        public string Get(int id)
        {
            throw new Exception("test");
            //return "Value";
        }

        public void Post([FromBody]string value)
        {
        }

        public void Post(int id, [FromBody]string value)
        {
        }

        public void Put(int id, [FromBody]string value)
        {
        }

        public void Delete(int id)
        {
        }
    }
```

## Startup.cs
```csharp
    public class Startup
    {
        public void Configuration(IAppBuilder appBuilder)
        {
            HttpConfiguration config = new HttpConfiguration();

            var previousSelector =
            config.Services.GetService(typeof(IHttpControllerSelector)) as IHttpControllerSelector;
            config.Services.Replace(typeof(IHttpControllerSelector),
                                    new MyWebApiControllerSelector(config) { PreviousSelector = previousSelector });

            config.Services.Replace(typeof(IHttpActionSelector), new MyWebApiActionSelector());

            config.Services.Replace(typeof(IExceptionHandler), new MyExceptionHandler());

            config.MessageHandlers.Add(new MyMessageHandler());

            config.Routes.MapHttpRoute(
                        name: "DefaultApi",
                        routeTemplate: "{namespace}/{controller}",
                        handler: null,
                        defaults: null,
                        constraints: null
            );

            appBuilder.UseWebApi(config);
        }
    }
```

## MyMessageHandler.cs
```csharp
    internal class MyMessageHandler : DelegatingHandler
    {
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
                                                               CancellationToken cancellationToken)
        {
            Debug.WriteLine("request");
            var response = await base.SendAsync(request, cancellationToken);
            Debug.WriteLine("response");
            return response;
        }
    }
```

## MyWebApiControllerSelector.cs
```csharp
    internal class MyWebApiControllerSelector : DefaultHttpControllerSelector
    {
        private HttpConfiguration _config;

        public IHttpControllerSelector PreviousSelector { get; set; }

        public MyWebApiControllerSelector(HttpConfiguration configuration) : 
            base(configuration)
        {
            _config = configuration;
        }

        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            IHttpRouteData routeData = request.GetRouteData();

            string namespaceName = GetRouteVariable<string>(routeData, "namespace");
            string controllerName = GetRouteVariable<string>(routeData, "controller");

            string className = namespaceName + "." + controllerName + "Controller";

            IAssembliesResolver assembliesResolver =
                _config.Services.GetAssembliesResolver();
            IHttpControllerTypeResolver controllersResolver =
                _config.Services.GetHttpControllerTypeResolver();

            ICollection<Type> controllerTypes =
                controllersResolver.GetControllerTypes(assembliesResolver);

            //var hoge = AppDomain.CurrentDomain.GetAssemblies();

            Type typ = null;
            foreach (var t in controllerTypes)
            {
                if (t.FullName == className)
                {
                    typ = t;
                    break;
                }
            }

            if (typ == null)
            {
                throw new Exception("コントローラがありません。");
            }

            HttpControllerDescriptor ret = new HttpControllerDescriptor();
            ret.Configuration = _config;
            ret.ControllerName = controllerName + "Controller";
            ret.ControllerType = typ;

            return ret;
        }

        private static T GetRouteVariable<T>(IHttpRouteData routeData, string name)
        {
            //戻り値
            object result = null;

            //取得対象のデータがrouteDataに含まれていたら、返す。
            //含まれていなければ、<T>のdefaultを返す
            if (routeData.Values.TryGetValue(name, out result))
            {
                return (T)result;
            }
            return default(T);
        }
    }
```

## MyWebApiActionSelector.cs
```csharp
    internal class MyWebApiActionSelector : ApiControllerActionSelector
    {
        public override HttpActionDescriptor SelectAction
            (HttpControllerContext controllerContext)
        {
            HttpActionDescriptor actionDescriptor =
                base.SelectAction(controllerContext);

            

            return actionDescriptor;
        }
    }
```

## MyExceptionHandler.cs
```csharp
    internal class MyExceptionHandler : ExceptionHandler
    {
        public override void Handle(ExceptionHandlerContext context)
        {
            context.Result = new MyErrorResult(context.Request, context.Exception);

            base.Handle(context);
        }
    }
```

## MyErrorResult.cs
```csharp
    internal class MyErrorResult : IHttpActionResult
    {
        private Exception exception;
        private HttpRequestMessage request;

        public MyErrorResult(HttpRequestMessage request, Exception exception)
        {
            this.request = request;
            this.exception = exception;
        }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            JObject json = new JObject();
            json.Add("status", (int)HttpStatusCode.InternalServerError);
            json.Add("message", exception.Message);
            json.Add("stackTrace", exception.StackTrace);

            HttpResponseMessage response =
                request.CreateErrorResponse(
                    HttpStatusCode.InternalServerError, 
                    json.ToString(), exception
                );

            return Task.FromResult(response);
        }
    }
```