.Net WebApi OData 控制器中的多个 get 方法
Multiple get methods in a .Net WebApi OData Controller
我想知道是否有可能在同一个 ODataController 中实现两个带参数的不同 get 方法。这就是我想要做的,但它不起作用。我能做什么?
在 ODataController 中:
public class ProductsController : ODataController
{
private readonly ProductsService _service;
public ProductsController()
{
_service = new ProductsService();
}
[EnableQuery]
public IQueryable<Product> Get()
{
return _service.GetAll();
}
[EnableQuery]
public Product Get(int key)
{
return _service.GetById(key);
}
[EnableQuery]
public IQueryable<Product> Get(string key)
{
return _service.GetByFilter(key);
}
在 WebApiConfig.cs 文件中我有下一个配置:
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
config.Routes.MapODataServiceRoute("odata", "api/odata", builder.GetEdmModel());
我可以同时使用不带参数的 'get' 方法和带参数的 'get' 方法之一,但我不能同时使用这两种带参数的方法。我已经看到了 OdataRoute,但我没有开始工作。我知道我可以通过通用方法使用 OData 过滤器功能,但我想尝试不使用它。还有其他解决方案吗?
谢谢!
OData 路由不支持 overloads OOTB,这实际上是您在此处描述的两种 GET Collection
方法,但您希望其中一种具有相同的路由作为 GET Resource
方法。
第一个问题是为什么你想让相同的路由指向两个不同的方法,尤其是当其中一个路由return是一个资源时,并且另一个将return一个集合。
Built-In OData Routing Conventions
The built in conventions will route known URLs to methods that match the expected signatures. All other routes need to be registered with the Edm Model either as discrete Functions or Actions. You could also implement your own routes
The arguments are more or less the same if you had intended the Get(string key)
to return a single item or a collection. There is still a clash between a default route and 2 different methods to handle the request for that route. There is a direct duplicate isssue on SO for this type of issue:
在 OData 中,我们区分适合默认 ~/Controller(key)
路由的资源 key
和有效的所有其他路由。对于所有其他非默认路由,我们只需在 OData EdmModel 中声明它们即可使它们有效
还有其他可能的解决方案,例如 or a custom IODataRoutingConvention as explained here。
然而,我发现最好尽可能地尝试并坚持 OData 标准约定,因此当遇到路由问题时,可以在 OData 常规方式 和我们的业务需要,AND 你想在全球范围内支持这种语法,那么你可以使用一个简单的 url 重写模块。
- 在 Edm 模型中将您的自定义方法端点声明为单独的函数。
- 创建一个 Url 重写以在旧路由和 OData 路由之间映射。
在 Edm 模型中声明自定义函数:
要通过 Edm 模型访问您的自定义函数,我们需要进行两项更改:
将方法名称更改为不同于 Get
的名称。在此示例中,我们将使用术语 Search
[HttpGet]
[EnableQuery]
public IQueryable<Product> Search(string key)
{
return _service.GetByFilter(key);
}
修改您的构建器流利表示法:
ODataModelBuilder builder = new ODataConventionModelBuilder();
var products = builder.EntitySet<Product>("Products");
products.EntityType.Collection.Function(nameof(ProductsController.Search))
.ReturnsCollectionFromEntitySet<Facility>("Products")
.Parameter<string>("key");
config.Routes.MapODataServiceRoute("odata", "api/odata", builder.GetEdmModel());
NOTE: The name of the route must match the name of the method for this configuration to work, to enforce this nameof(ProductsController.Search)
was used however, "Search"
is all that is necessary.
将匹配此模板的最终 URL:
GET: ~/api/odata/Products/Search(key='Foo')
Url重写模块
public class ODataConventionUrlRewriter : OwinMiddleware
{
/// <summary>
/// Create a Url Rewriter to manipulate incoming OData requests and rewrite or redirect to the correct request syntax
/// </summary>
/// <param name="next"></param>
public ODataConventionUrlRewriter(OwinMiddleware next)
: base(next)
{
}
/// <summary>
/// Process the incoming request, if it matches a known path, rewrite accordingly or redirect.
/// </summary>
/// <param name="context">OWin Request context to process</param>
/// <returns>Chains the next processor if the url is OK or rewritten, otherwise the response is to redirect</returns>
public override async Task Invoke(IOwinContext context)
{
// Match ANY /Controller(NonNumeric)
// Rewrite to /Controller/Search(key='NonNumeric')
var regex = new System.Text.RegularExpressions.Regex(@"\(([^\d=\'\(]+)\)$");
match = regex.Match(context.Request.Path.Value);
if (match != null && match.Success)
{
// We have to use redirect here, we can't affect the query inflight
context.Response.Redirect($"{context.Request.Uri.GetLeftPart(UriPartial.Authority)}{regex.Replace(context.Request.Path.Value, $"/Search(key='{match.Groups[1].Value}')")}");
}
else
await Next.Invoke(context);
}
}
最后,在 StartUp.cs 或配置 OWin 上下文的地方,将此模块添加到管道 之前 OData配置:
public void Configuration(IAppBuilder app)
{
...
// Rewrite URLs
app.Use(typeof(ODataConventionUrlRewriter));
...
// Register routes
config.MapHttpAttributeRoutes();
ODataModelBuilder builder = new ODataConventionModelBuilder();
var products = builder.EntitySet<Product>("Products");
products.EntityType.Collection.Function(nameof(ProductsController.Search))
.ReturnsCollectionFromEntitySet<Facility>("Products")
.Parameter<string>("key");
config.Routes.MapODataServiceRoute("odata", "api/odata", builder.GetEdmModel());
...
// Start Web API
app.UseWebApi(config);
}
现在我们的自定义函数支持的最终 Url 是这样的:
GET: ~/api/odata/Products(Foo)
Try to use the conventions when you can, you have chosen OData for a reason, if you do use a rewrite module, try to be as specific in your conditional logic as you can be to avoid breaking other standard routes that might be in use.
I try to reserve rewrite solutions for common legacy query routes.
我想知道是否有可能在同一个 ODataController 中实现两个带参数的不同 get 方法。这就是我想要做的,但它不起作用。我能做什么?
在 ODataController 中:
public class ProductsController : ODataController
{
private readonly ProductsService _service;
public ProductsController()
{
_service = new ProductsService();
}
[EnableQuery]
public IQueryable<Product> Get()
{
return _service.GetAll();
}
[EnableQuery]
public Product Get(int key)
{
return _service.GetById(key);
}
[EnableQuery]
public IQueryable<Product> Get(string key)
{
return _service.GetByFilter(key);
}
在 WebApiConfig.cs 文件中我有下一个配置:
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
config.Routes.MapODataServiceRoute("odata", "api/odata", builder.GetEdmModel());
我可以同时使用不带参数的 'get' 方法和带参数的 'get' 方法之一,但我不能同时使用这两种带参数的方法。我已经看到了 OdataRoute,但我没有开始工作。我知道我可以通过通用方法使用 OData 过滤器功能,但我想尝试不使用它。还有其他解决方案吗?
谢谢!
OData 路由不支持 overloads OOTB,这实际上是您在此处描述的两种 GET Collection
方法,但您希望其中一种具有相同的路由作为 GET Resource
方法。
第一个问题是为什么你想让相同的路由指向两个不同的方法,尤其是当其中一个路由return是一个资源时,并且另一个将return一个集合。
Built-In OData Routing Conventions
The built in conventions will route known URLs to methods that match the expected signatures. All other routes need to be registered with the Edm Model either as discrete Functions or Actions. You could also implement your own routes
The arguments are more or less the same if you had intended the
Get(string key)
to return a single item or a collection. There is still a clash between a default route and 2 different methods to handle the request for that route. There is a direct duplicate isssue on SO for this type of issue:
在 OData 中,我们区分适合默认 ~/Controller(key)
路由的资源 key
和有效的所有其他路由。对于所有其他非默认路由,我们只需在 OData EdmModel 中声明它们即可使它们有效
还有其他可能的解决方案,例如
然而,我发现最好尽可能地尝试并坚持 OData 标准约定,因此当遇到路由问题时,可以在 OData 常规方式 和我们的业务需要,AND 你想在全球范围内支持这种语法,那么你可以使用一个简单的 url 重写模块。
- 在 Edm 模型中将您的自定义方法端点声明为单独的函数。
- 创建一个 Url 重写以在旧路由和 OData 路由之间映射。
在 Edm 模型中声明自定义函数:
要通过 Edm 模型访问您的自定义函数,我们需要进行两项更改:
将方法名称更改为不同于
Get
的名称。在此示例中,我们将使用术语Search
[HttpGet] [EnableQuery] public IQueryable<Product> Search(string key) { return _service.GetByFilter(key); }
修改您的构建器流利表示法:
ODataModelBuilder builder = new ODataConventionModelBuilder(); var products = builder.EntitySet<Product>("Products"); products.EntityType.Collection.Function(nameof(ProductsController.Search)) .ReturnsCollectionFromEntitySet<Facility>("Products") .Parameter<string>("key"); config.Routes.MapODataServiceRoute("odata", "api/odata", builder.GetEdmModel());
NOTE: The name of the route must match the name of the method for this configuration to work, to enforce this
nameof(ProductsController.Search)
was used however,"Search"
is all that is necessary.
将匹配此模板的最终 URL:
GET: ~/api/odata/Products/Search(key='Foo')
Url重写模块
public class ODataConventionUrlRewriter : OwinMiddleware
{
/// <summary>
/// Create a Url Rewriter to manipulate incoming OData requests and rewrite or redirect to the correct request syntax
/// </summary>
/// <param name="next"></param>
public ODataConventionUrlRewriter(OwinMiddleware next)
: base(next)
{
}
/// <summary>
/// Process the incoming request, if it matches a known path, rewrite accordingly or redirect.
/// </summary>
/// <param name="context">OWin Request context to process</param>
/// <returns>Chains the next processor if the url is OK or rewritten, otherwise the response is to redirect</returns>
public override async Task Invoke(IOwinContext context)
{
// Match ANY /Controller(NonNumeric)
// Rewrite to /Controller/Search(key='NonNumeric')
var regex = new System.Text.RegularExpressions.Regex(@"\(([^\d=\'\(]+)\)$");
match = regex.Match(context.Request.Path.Value);
if (match != null && match.Success)
{
// We have to use redirect here, we can't affect the query inflight
context.Response.Redirect($"{context.Request.Uri.GetLeftPart(UriPartial.Authority)}{regex.Replace(context.Request.Path.Value, $"/Search(key='{match.Groups[1].Value}')")}");
}
else
await Next.Invoke(context);
}
}
最后,在 StartUp.cs 或配置 OWin 上下文的地方,将此模块添加到管道 之前 OData配置:
public void Configuration(IAppBuilder app)
{
...
// Rewrite URLs
app.Use(typeof(ODataConventionUrlRewriter));
...
// Register routes
config.MapHttpAttributeRoutes();
ODataModelBuilder builder = new ODataConventionModelBuilder();
var products = builder.EntitySet<Product>("Products");
products.EntityType.Collection.Function(nameof(ProductsController.Search))
.ReturnsCollectionFromEntitySet<Facility>("Products")
.Parameter<string>("key");
config.Routes.MapODataServiceRoute("odata", "api/odata", builder.GetEdmModel());
...
// Start Web API
app.UseWebApi(config);
}
现在我们的自定义函数支持的最终 Url 是这样的:
GET: ~/api/odata/Products(Foo)
Try to use the conventions when you can, you have chosen OData for a reason, if you do use a rewrite module, try to be as specific in your conditional logic as you can be to avoid breaking other standard routes that might be in use.
I try to reserve rewrite solutions for common legacy query routes.