属性ルーティングで ASP.NET Web API 2 を使用していますが、メディア タイプを使用したバージョン管理が機能していないようですapplication/vnd.company[.version].param[+json]
。
次のエラーが表示されます。
指定されたキーはディクショナリに存在しませんでした。
これは、キーインメソッドのテストに由来し_actionParameterNames[descriptor]
ますFindActionMatchRequiredRouteAndQueryParameters()
。
foreach (var candidate in candidatesFound)
{
HttpActionDescriptor descriptor = candidate.ActionDescriptor;
if (IsSubset(_actionParameterNames[descriptor], candidate.CombinedParameterNames))
{
matches.Add(candidate);
}
}
ソース: ApiControllerActionSelector.cs
さらにデバッグした後、コントローラーが2つある場合に気付きました
[RoutePrefix("api/people")]
public class PeopleController : BaseApiController
{
[Route("")]
public HttpResponseMessage GetPeople()
{
}
[Route("identifier/{id}")]
public HttpResponseMessage GetPersonById()
{
}
}
[RoutePrefix("api/people")]
public class PeopleV2Controller : BaseApiController
{
[Route("")]
public HttpResponseMessage GetPeople()
{
}
[Route("identifier/{id}")]
public HttpResponseMessage GetPersonById()
{
}
}
ApiVersioningSelector : DefaultHttpControllerSelector
上記のように、同じキーを持つすべてのコントローラーからキーをテストし、[RoutePrefix("api/people")]
明らかに例外がスローされるため、カスタムを使用することはできません。
正しいコントローラーが選択されていることを確認するため
これがバグかどうかはわかりませんが、ルートを使用[RoutePrefix("api/v1/people")] to version API
すると悲しくなります。
注:これは、属性ルーティングなしでうまく機能します。
アップデート
public class ApiVersioningSelector : DefaultHttpControllerSelector
{
private HttpConfiguration _HttpConfiguration;
public ApiVersioningSelector(HttpConfiguration httpConfiguration)
: base(httpConfiguration)
{
_HttpConfiguration = httpConfiguration;
}
public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
IDictionary<string, HttpControllerDescriptor> controllers = GetControllerMapping();
var attributedRoutesData = request.GetRouteData().GetSubRoutes();
var subRouteData = attributedRoutesData.LastOrDefault(); //LastOrDefault() will get PeopleController, FirstOrDefault will get People{version}Controller which we don't want
var actions = (ReflectedHttpActionDescriptor[])subRouteData.Route.DataTokens["actions"];
var controllerName = actions[0].ControllerDescriptor.ControllerName;
//For controller name without attribute routing
//var controllerName = (string)routeData.Values["controller"];
HttpControllerDescriptor oldControllerDescriptor;
if (controllers.TryGetValue(controllerName, out oldControllerDescriptor))
{
var apiVersion = GetVersionFromMediaType(request);
var newControllerName = String.Concat(controllerName, "V", apiVersion);
HttpControllerDescriptor newControllerDescriptor;
if (controllers.TryGetValue(newControllerName, out newControllerDescriptor))
{
return newControllerDescriptor;
}
return oldControllerDescriptor;
}
return null;
}
private string GetVersionFromMediaType(HttpRequestMessage request)
{
var acceptHeader = request.Headers.Accept;
var regularExpression = new Regex(@"application\/vnd\.mycompany\.([a-z]+)\.v([0-9]+)\+json",
RegexOptions.IgnoreCase);
foreach (var mime in acceptHeader)
{
var match = regularExpression.Match(mime.MediaType);
if (match != null)
{
return match.Groups[2].Value;
}
}
return "1";
}
}