[英]OData V4 WebAPI routing convention for 'Cast'
使用基本的Get方法为以下查询构建ODataController:
http://localhost:8080/api/Bases
非常简单:
[EnableQuery]
public IHttpActionResult Get()
{
return Ok(new List<Base>());
}
以相同的方式,我尝试实现“ cast”路由(“〜/ entityset / cast”),该路由在OData V4约定的第4.9部分中定义,但是这还没有记载。 因此,我研究了一些源代码,并发现了以下URL:
http://localhost:8080/api/Bases/MyNamespace.DerivedA
我可以在同一控制器中定义以下方法:
[EnableQuery]
public IHttpActionResult GetFromDerivedA()
{
return Ok(new List<DerivedA>());
}
但是我可以从Base
继承的十几种类型都可以工作。 除了可以为每个派生类型声明一个方法之外,还有一种方法可以使用类似以下内容的方法:
[EnableQuery]
public IHttpActionResult GetFrom<T>()
where T : Base
{
return Ok(new List<T>());
}
我正在使用:
我可以创建一个新的RoutingConvention并让重写的SelectAction返回我的通用方法,但是看来我不得不忘记通用方法:
“无法在控制器'MyProject.Controllers.BasesController'上调用动作方法
'System.Web.Http.IHttpActionResult GetFrom[T]()'
,因为该动作方法是通用方法。”
那这可能吗?
[EnableQuery]
public IHttpActionResult GetFrom(Type derivedType)
{
//snip!
}
如果没有,还有其他想法吗?
这是我能够完成该工作的一种方式,并有所反思。 这还有很长的路要走,但是生成的控制器方法是如此简单,值得这样做。
首先,创建一个新的RoutingConvention。 注意,我们将所有GetFrom
请求转发到名为GetFrom
的方法:
public class CastRoutingConvention : EntitySetRoutingConvention
{
public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
{
if (odataPath.PathTemplate == "~/entityset/cast")
{
HttpMethod httpMethod = controllerContext.Request.Method;
var collectionType = (IEdmCollectionType)odataPath.EdmType;
var entityType = (IEdmEntityType)collectionType.ElementType.Definition;
var type = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => !a.IsDynamic)
.SelectMany(a => a.DefinedTypes)
.FirstOrDefault(t => t.FullName == entityType.FullTypeName());
controllerContext.RouteData.Values["type"] = type;
if (httpMethod == HttpMethod.Get)
return "GetFrom";
else if (httpMethod == HttpMethod.Post)
return "PostFrom";
else
return base.SelectAction(odataPath, controllerContext, actionMap);
}
else
return base.SelectAction(odataPath, controllerContext, actionMap);
}
}
接下来,将其添加到OData配置中:
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
var builder = new ODataConventionModelBuilder() { Namespace = "Default" };
builder.DataServiceVersion = Version.Parse("4.0");
//snip! entity configuration
var conventions = ODataRoutingConventions.CreateDefault();
conventions.Insert(0, new CastRoutingConvention());
config.MapODataServiceRoute(
routeName:"ODataRoute",
routePrefix: "api",
routingConventions: conventions,
pathHandler: new DefaultODataPathHandler(),
model: builder.GetEdmModel());
}
现在,由于默认模型绑定程序不会从路由数据字典中读取任意参数名称,因此我们需要为路由数据使用自定义模型绑定程序:
using System;
using System.Web.Http.Controllers;
using System.Web.Http.ModelBinding;
namespace Example
{
public class RouteDataModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
object model;
if (!actionContext.RequestContext.RouteData.Values.TryGetValue(bindingContext.ModelName, out model))
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, $"No route data named '{bindingContext.ModelName}'.");
return false;
}
else if (!bindingContext.ModelType.IsAssignableFrom(model.GetType()))
{
try
{
model = Convert.ChangeType(model, bindingContext.ModelType);
}
catch
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, $"Route data cannot be converted to type '{bindingContext.ModelType.FullName}'.");
return false;
}
}
bindingContext.Model = model;
return true;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)]
public class RouteDataAttribute : ModelBinderAttribute
{
public RouteDataAttribute()
{
this.BinderType = typeof(RouteDataModelBinder);
}
}
}
最后,在控制器中添加所需的方法。 请注意这是多么琐碎:
[EnableQuery]
public IHttpActionResult GetFrom([RouteData]Type type)
{
var ofType = typeof(Queryable).GetMethod("OfType").MakeGenericMethod(type);
return Ok((IQueryable<Base>)ofType.Invoke(null, new object[] { this.Context.Bases }));
}
由于我使用的是Entity Framework,因此无法使用GetType()
,因此我必须使用另一个反射技巧来对Type
实例调用OfType<T>()
。 如果您使用的是内存中实体,则只需废弃最后一部分并使用普通格式即可:
return Ok(inRamEntities.Where(e => e.GetType() == type));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.