OData / WebApi

OData Web API: A server library built upon ODataLib and WebApi
https://docs.microsoft.com/odata
Other
855 stars 473 forks source link

ODataRoutePrefix and custom HttpControllerSelector #100

Closed LianwMS closed 7 years ago

LianwMS commented 9 years ago

Hi, I am working with webapi and oData v4.

I am exposing an entity, say "Order", both via oData and via webapi,
So I have two OrdersController, one deriving from ApiController and the other from ODataController.

The default "DefaultHttpControllerSelector" obviously find two different controllers with the same name OrdersController (in different namespaces) and throws an exception.

So I wrote a custom HttpControllerSelector to select the correct one, and it works.
The problem is that by manually building the HttpControllerDescriptor, it is no more possible to use the ODataRoutePrefixAttribute because the "AttributeRouteData" route value is not populated and the Action selection fails.

Looking at the sources I found that this value is populated in AttributeRoutingConvention.SelectController but it's not obvious how to put this together with a custom HttpControllerSelector.

Any suggestions?

Thanks

Work Item Details

Original CodePlex Issue: Issue 1931 Status: Proposed Reason Closed: Unassigned Assigned to: Unassigned Reported on: May 9, 2014 at 4:11 PM Reported by: raffaeler Updated on: Sep 29, 2014 at 9:13 PM Updated by: wmlrose

LianwMS commented 9 years ago

On 2014-05-09 23:18:50 UTC, raffaeler commented:

Just one more comment to this problem. I can make it work just by renaming the controller class as the ODataRoutePrefixAttribute will select the correct one.

My scenario is more complex and I need to use the custom HttpControllerDescriptor to select the controller. So how can I make the ODataRoutePrefixAttribute work when a custom HttpControllerDescriptor make the controller choice?

Thanks again

LianwMS commented 9 years ago

On 2014-05-15 07:29:38 UTC, yishaigalatzer commented:

I don't know the answer at this point. But I want to warn you that create your own HttpControllerDescriptor leads to perf loss if done naively. You will want to cache the HttpControllerDescriptor you created, otherwise the next stages in the pipeline will start compiling lambda expressions on every request.

Can you be more specific about why you want to write your own controller selector, typically we see that there are better ways around to deal with issues. You are replacing a very fundamental part of the system and shouldn't do it lightly.

Did you consider using attribute routing for Web API?

LianwMS commented 9 years ago

On 2014-05-15 15:24:57 UTC, raffaeler commented:

Hi Yishai, I was already writing a dictionary to provide a cached version. Totally agree on the perf problem. I already partially resolved the issue by using ODataRoutePrefixAttribute but I will need to inject the controllers dynamically in the system and the conflict between webapi and odata controller names is an issue.

So let's go back to the initial problem: the default "DefaultHttpControllerSelector" find two different controllers with the same name. It would be nice to make the default selector distinguish between them by, for example, looking for a base interface acting as a tag for oData or WebApi.

Add to the infrastructure two empty interfaces IODataControllerTag and IApiControllerTag. When the default selector find a naming collision, it will search for these interfaces to resolve the collision Do you have a better / simpler solution? Thank you

LianwMS commented 9 years ago

On 2014-05-15 22:51:05 UTC, yishaigalatzer commented:

My suggestion is to use attribute routing on your WebAPI controllers, this way there is no conflict.

For your suggestion -

Web API does not distinguish between controller types because OData is something you can write using extensibility and not an integral part of the core Web API, in fact that is how it was shipped originally. Note that now for example we are going to ship two versions of OData side by side, and potentially third parties can do the same. So it doesn't make sense to do so in the global selector.

The solution lies in routing, Odata adds a constraint to the Odata route, and I think you might have the routes crosses. The OData route should appear before your Web API routes.

LianwMS commented 9 years ago

On 2014-05-22 05:46:00 UTC, jacalvar commented:

@raffaeler I'm not sure if there is anything wrong with the information you have provided. Can you provide more info on how you are reimplementing the IHttpControllerSelector or a repro if you have one?

raffaeler commented 9 years ago

Currently I derived DefaultHttpControllerSelector and:

For a better pluggability I would like to obtain from the infrastructure which is the controller chosen by default and then decide whether to call it or another one. The problem of the current implementation is that you can't know what would be the infrastructure decision before deciding to call it or not.

Secondly, even if oData controllers derive from webapi controllers, it should be pretty easy decide with a marker (empty) interface which "tags" the controller as oData.