-
Notifications
You must be signed in to change notification settings - Fork 712
Problem with non-API routes. Any way to filter out controllers? #399
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
That's correct. API Versioning doesn't make any concessions about controller types. In ASP.NET Core, a controller is a controller. API versioning does not allow no API version. The closest you can get is assuming a default (or initial) version or be API version-neutral, which means you accept every API version. There are at least 3 ways you can deal with this:
[ApiVersionNeutral]
public abstract class UIController : Controller
{
protected UIController() { }
}
public class HomeController : UIController
{
} |
Thank you for your answer. I am using ASP.NET Core 2.0 (can't upgrade to 2.1 for now) and API Version 2.3.0 so I suppose the only way I can deal with the problem is with MapWhen. But I'm not sure how to do it since I'm using AddApiVersioning like this:
How can I manipulate the registration of the ApiVersioning middleware? |
API versioning doesn't add middleware, it (currently) adds a router. Starting in 3.0, it will technically add middleware, but that is only to inject the API versioning feature, which has little-to-no bearing on the execution pipeline. If you want customize the routes, you'll need to remap it yourself as is shown in #194. The simplest way to achieve your goal is probably to apply a convention to all your UI controllers. This will work if all your UI controllers inherit from Controller and all your APIs inherit from ControllerBase. var mvcBuilder = services.AddMvc();
services.AddApiVersioning( options =>
{
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
var controllerType = typeof( Controller );
foreach ( var parts in mvcBuilder.PartManager.ApplicationParts )
{
foreach ( var part in parts.OfType<IApplicationPartTypeProvider>() )
{
foreach ( var type in part.Types )
{
if ( controllerType.IsAssignableFrom( type ) )
{
options.Conventions.Controller( type ).IsApiVersionNeutral();
}
}
}
}
} ); There were some issues with delayed evaluation when configuring the API versioning options prior to 3.0. It's possible that all of the controller types may not have yet been discovered (I didn't test this). If that turns out to be the case, you'll just need to provide your own mechanism for enumerating the UI controller type or migrate to 3.0 (Beta 2+). Enumerating all the types in a namespace is pretty straight forward.
|
Am I wrong or the ApiVersionNeutral attribute has no bearing in my problem? I see that my UI controller is being treated as version neutral (by debuging the source code) and still the mentioned error ("does not support HTTP method POST") is raised. As there is a route for GET... If I'm not wrong, I understand from your answer that there is no way to work around this except to use version 3 and use the "hack" from issue #194. Right? |
Oh ... I think I understand what's happening. In the context of REST, the general expectation is that an unsupported HTTP method on the server returns 405 (Method Not Supported) back to the client. This behavior is somewhat conflated with API versioning. When a route could match, but doesn't due to an unmatched HTTP method, you'll get 405. If the requested HTTP method matches, but the rest of the route doesn't, you'll receive 404 (or 400). This behavior doesn't matter if you're API version-neutral. You can alter the behavior in the options using a custom IErrorResponseProvider. I hope that helps clear things up. I'm happy to answer more questions or add additional clarifications. |
Well, the error response provider allows me to change the status code (the message, etc.) but that is not what I want. What I want is for versioning to simply ignore routes that are not /api/* and do nothing. Because those other routes are not an API. I see that this component doesn't support that. It doesn't make sense to me. Maybe in the future...? Thanks for trying to help me. |
Gotcha. Unfortunately, there isn't a particularly easy way to do that. ASP.NET Core doesn't intrinsically have a way to disambiguate UI and API controllers so there's not a whole lot for versioning to draw upon. Using one of previously mentioned methods is usually how it's addressed by devs that are hosting UIs and versioned APIs together. ASP.NET 2.1 Core introduced the concept of API Behaviors via the I'm always open to new ideas to make things easier. A key design point, however, has been that you don't need to learn anything new about routing. Your scenario is achievable, it's just not as straight forward as one might like. |
one thought: split the project in two, one for mvc stuff the other for api stuff , make them child applications if you use iis. |
It seems like this issue is resolved. I mulled it over some more and the behavioral change to require |
I thought I would quickly mention that I've changed the behavior in 3.1. The built-in specifications are:
If these do not satisfy your particular needs, you can roll you own specification and register it with the other services. For example, public class MyApiControllerSpec : IApiControllerSpecification
{
// consider all controllers that inherit from Controller to be a UI controller
readonly Type UIControllerType = typeof( Controller ).GetTypeInfo();
public bool IsSatisfiedBy( ControllerModel controller ) =>
!UIControllerType.IsAssignableFrom( controller.ControllerType )
} And register like: services.TryAddEnumerable( ServiceDescriptor.Transient<IApiControllerSpecification, MyApiControllerSpec>() ); This prevents you having to muck with route configurations and so on. I'll add a section in the wiki about this. In the meantime, this should provide some information for those spelunking the issues. Thanks. |
Still does not work. I have an area with controllers that are decorated with the ApiController attribute and an rea where controllers are not decorated with the attribute. I even specifically set the option ApiVersioning is still not leaving my controllers without the attribute alone. I then went as far as specifying and register an IApiControllerSpecification myself:
It is being called and obviously returns FALSE Now what am I suppose to do? ========================================================= I found out, that if I have a routing error of some kind (name/method) it will always result in an ApiVersioning error being returned. Either missing api or not supported etc... This caused a lot of confusion. In my case the error was a simple typo in the route and had nothing to do with versioning! Kind regards |
ApiVersioning 3.1.6 same problem but... |
@fabiomaulo , make sure:
|
Hello.
I have a host that runs a set of normal MVC routes (with views) and MVC API routes. These are segmented this way:
/{route} -> views
/api/v{version:apiVersion}/{route} -> API
If I have a route like this:
[GET] /myroute
And for some reason, I call it with a POST I get the following error, instead of the expected 404:
The HTTP resource that matches the request URI 'http://localhost:5000/myroute' does not support HTTP method 'POST'.
The problem here is that the "versioning middleware" is kicking in and I haven't found a way to tell it to completly ignore the MVC controllers (or a specific one for that matter).
Is there any way to achieve this?
Thanks in advance.
The text was updated successfully, but these errors were encountered: