GETとPOSTを別々のAPIコントローラータイプに分離し、同じルートプレフィックスを使用してそれらにアクセスすることは可能ですか?
これが私のコントローラーです:
[RoutePrefix("api/Books")]
public class BooksWriteController : EventStoreApiController
{
[Route("")]
public void Post([FromBody] CommandWrapper commandWrapper){...}
}
[RoutePrefix("api/Books")]
public class BooksReadController : MongoDbApiController
{
[Route("")]
public Book[] Get() {...}
[Route("{id:int}")]
public Book Get(int id) {...}
}
Web API(1.x-2.x)は、異なるコントローラー上の同じパスを持つ複数の属性ルートをサポートしていません。すべてのルートが複数のコントローラーと一致し、その時点でWeb APIは結果があいまいであると見なすため、結果は404になります。
MVCコア はこのシナリオをサポートすることに注意してください。MVCコアはMVCとWeb APIの両方として機能します。
Web API 2.11以降を使用する場合は、コントローラーごとにhttpメソッドのルート制約を作成し、組み込みのルート属性の代わりにそれを使用できます。以下のサンプルは、RoutePrefixまたは直接Routes(kmacdonaldの回答など)を使用できることを示しています。
using System.Collections.Generic;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Routing;
public class BooksWriteController : ApiController
{
[PostRoute("api/Books")]
public void Post() { }
}
[RoutePrefix("api/books")]
public class BooksReadController : ApiController
{
[GetRoute]
public void Get() { }
[GetRoute("{id:int}")]
public void Get(int id) { }
}
これら2つのクラスは、制約ルート属性の使用を簡素化します
class GetRouteAttribute : MethodConstraintedRouteAttribute
{
public GetRouteAttribute(string template) : base(template ?? "", HttpMethod.Get) { }
}
class PostRouteAttribute : MethodConstraintedRouteAttribute
{
public PostRouteAttribute(string template) : base(template ?? "", HttpMethod.Post) { }
}
このクラスにより、生成されたルートに制約を追加できます
class MethodConstraintedRouteAttribute : RouteFactoryAttribute
{
public MethodConstraintedRouteAttribute(string template, HttpMethod method)
: base(template)
{
Method = method;
}
public HttpMethod Method
{
get;
private set;
}
public override IDictionary<string, object> Constraints
{
get
{
var constraints = new HttpRouteValueDictionary();
constraints.Add("method", new MethodConstraint(Method));
return constraints;
}
}
}
これは単なる標準のルート制約です。nit:制約オブジェクトをキャッシュして割り当てを減らすことができます。
class MethodConstraint : IHttpRouteConstraint
{
public HttpMethod Method { get; private set; }
public MethodConstraint(HttpMethod method)
{
Method = method;
}
public bool Match(HttpRequestMessage request,
IHttpRoute route,
string parameterName,
IDictionary<string, object> values,
HttpRouteDirection routeDirection)
{
return request.Method == Method;
}
}
コントローラーで常にRoutePrefix
を指定する必要はありません。ルートを直接Webメソッドに置くことができます:
public class BooksWriteController : EventStoreApiController
{
[Route("api/Books")]
public void Post([FromBody] CommandWrapper commandWrapper){...}
}
public class BooksReadController : MongoDbApiController
{
[Route("api/Books")]
public TaskTypeInfo[] Get() {...}
[Route("api/Books/{id:int}")]
public TaskTypeInfo Get(int id) {...}
}
ただし、あなたのRoutePrefix
は両方のコントローラーで正常に動作すると思います。属性RoutePrefix
は、ルートを実際に定義するRoute
属性と組み合わせて使用されると思います。つまり、競合するルートがない限り(これは非常に重要です)、大丈夫です。