Remove Route from HttpRouteCollection - c#

I am using the attribute routing included in WebAPI 2.0, but cannot figure out how to remove a route based on certain conditions. I map all routes using the MapHttpAttributeRoutes, and then I would like to remove a specific route using the next line of code.
// enable attribute routing support
httpConfiguration.MapHttpAttributeRoutes();
// expose the flag routes only if required
if (DisableFlagEndpoint)
{
httpConfiguration.Routes.Remove(FlagsController.RouteName);
}
But this throws a NotSupportedException. How does one remove a route ? If not, is there another way to achieve this ?

Looks like WebAPI 2.1 introduces the ability to do this with IgnoreRoute(). http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-21#ignoreroute
// disable the flag routes if required
if (DisableFlagEndpoint)
{
httpConfiguration.Routes.IgnoreRoute("Flags", "api/flags/{*paths}");
}
// enable attribute routing support
httpConfiguration.MapHttpAttributeRoutes();

Related

Get Swashbuckle/Swagger route programmatically

I'm using Swashbuckle with ASP.NET Core 6. It serves Swagger at /swagger.
For diagnostic purposes I need to detect that path programmatically at runtime - i.e. assuming swashbuckle is serving swagger at /swagger, then I want to be able to get that path.
I've tried various approaches, but they just give me my own routes. I can't find a way to determine routes added by swashbuckle.
I tried:
// inject IEnumerable<EndpointDataSource>
// ...
var routes = _endpointDataSources.SelectMany(source => source.Endpoints);
and:
var routes = app.Services.GetRequiredService<IActionDescriptorCollectionProvider>()
.ActionDescriptors
.Items.Where(x => x.AttributeRouteInfo != null)
.Select(x => x.AttributeRouteInfo?.Template);
In both cases it enumerates all of my routes - whether they are from RazorPages or WebApi. But the swagger route is not included, even though it is running.
How do I get that path?
Based on #lonix comment, Converting to Ans.
In case UseSwagger(), it does not create any endpoint but a middleware so when you call "/swagger", it checks request for swagger document and returns swagger document if found true. but in case of MapSwagger() it actually creates endpoint.
and yes you can swap it without breaking anything.

Blocking anonymous access by default in ASP .NET 5

My team and I are starting up a new website project in ASP .NET 5 and I'm trying to set up the basis of our user authentication and authorization policy.
So far, I've managed to use the [Authorize] and [AllowAnonymous] attributes to selectively define an authorization policy controllers or actions. The one thing I'm still struggling to achieve is defining a default authorization policy.
Bascially, I'd like every controller and action to behave as if they had an [Authorize] attribute by default, so that only actions specifically tagged as [AllowAnonymous] can be accessed by an anonymous user. Otherwise, we expect that, at some point, someone will forget to add an [Authorize] attribute to their controller and introduce vulnerabilities into the webapp.
It is my understanding that what I'm trying to do could be achieved in previous versions of ASP .NET by adding the following statement in FilterConfig.cs:
filters.Add(new AuthorizeAttribute());
... except that FilterConfig.cs no longer exists in MVC 6. According to How to register a global filter with mvc 6, asp.net 5 I can now access the global filters list using:
services.ConfigureMvc(options =>
{
options.Filters.Add(new YouGlobalActionFilter());
}
... tried it, looks fine, but now it's the AuthorizeAttribute filter that I can't seem to find.
For experimenting purposes I've tried to handcraft an equivalent to the AuthorizeAttribute filter and came up with the following:
public class LoginFilter: AuthorizeFilter
{
public LoginFilter(): base(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build())
{
}
public override Task OnAuthorizationAsync(Microsoft.AspNet.Mvc.AuthorizationContext context)
{
if(!context.HttpContext.User.Identity.IsAuthenticated && context.ActionDescriptor is ControllerActionDescriptor)
{
var action = context.ActionDescriptor as ControllerActionDescriptor;
if(!AcceptAnonymous(action.ControllerTypeInfo) && !AcceptAnonymous(action.MethodInfo))
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
}
return base.OnAuthorizationAsync(context);
}
private static bool AcceptAnonymous(ICustomAttributeProvider o)
{
return o.IsDefined(typeof(AllowAnonymousAttribute), true);
}
}
This kinda works... I can add it to the global filters list, and it does reject queries coming from unauthenticated users unless the query is resolved to an action tagged [AllowsAnonymous].
However...
the AuthorizationPolicyBuilder thingy is ugly and misleading: it does not serve any purpose and is apparently ignored during the whole processing. The only reason I added it is that AuthorizeFilter requires an AuthorizationPolicy in its constructor. I guess, but haven't tried yet, that directly implementing IAsyncAuthorizationFilter would solve this particular issue
nothing in this code is specific to my webapp and the functionality was apparently provided in previous versions of the framework, so I'm willing to bet that there already is (or there will soon be) a component doing exactly the same thing, and I'd rather use a standard component from the framework than handcraft my own.
So, long story short, where has the AuthorizeAttribute filter gone? Or is there any functional equivalent I can use to make rejection of anonymous users the default behavior?
You can use Microsoft.AspNet.Mvc.AuthorizeFilter.
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Authorization;
services.ConfigureMvc(options =>
{
options.Filters.Add(new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()));
});
If you need custom authorization requirements see this answer for more information.

Using attribute routing and convention based routing with Web API and MVC simultaneously

I have an Asp.net MVC web application that uses convention based routing. I recently added some Web Api 2 controllers, for which I used attribute routing. Despite the documentation claiming that you can use both, I can either get the (attribute routed) API methods to work, or the (convention routed) web application methods.
This is RouteConfig.RegisterRoutes():
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Tables", action = "Index", id = UrlParameter.Optional },
namespaces: new string[] { "Foo.Cms.Controllers" }
);
}
This is WebApiConfig.Register():
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
//config.EnableQuerySupport();
// The models currently only serialize succesfully to xml, so we'll remove the json formatter.
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.JsonFormatter);
}
And this is Application_Start():
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
}
This way, only the routing to the web api controllers works. If I switch GlobalConfiguration.Register() and RouteConfig.RegisterRoutes(), like so:
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configure(WebApiConfig.Register);
...only the convention-based routing works.
I'm at a loss. What's going on here?
Edit:
What I'm trying to achieve:
The application currently uses the basic {controller}/{action}/parameters convention. So I have a controller called ElementsController that has, for instance, an Index() method that is routed to /Elements or a ListPublic() method that is routed to /Elements/ListPublic. I achieved this with the convention based routing mentioned above.
I also have a bunch of Web Api controllers (for instance, TablesController) that I want to route to using a /api/v0/tables route. I tried to achieve this like so:
[RoutePrefix("api/v0/tables")]
public class TablesController : ApiController
{
[Route()]
public string Get()
{
// ...
}
}
As you can see, it's not the same route pattern: api calls are all prefixed with api/v0/. For some reason though, it still appears to treat them as the default {controller}/{action} routes.
What's occurring is that the "first registered" route is taking effect. If I have a MVC route defined as
{controller}/{action}/{id}
and a Web API route defined as
{controller}/{action}/{id}
The first registered route will take effect.
Why is this the case? Imagine you send a request to the server
foo/bar/1
Which route does this match?
Both!
It will choose the first result that matches the route regardless of the type of routing used. If you want some examples as to how to make these routings work, check out this link
If an URL matches two different route templates, it doesn't matter if they refer to MVC or Web API, the only rule is that the first registered route will be used to select the action to execute.
In your particular case, if you make a GET request to this URL: /api/v0/tables, the action selector starts checking the registered routes.
The first route that you're registering is an MVC route like this: /{controller}/{action}/{id}. So, the template matches with this values:
controller = "api"
action = "v0"
id = "tables"
If you register the attribute routes before the MVC route, the first matching route template will be your route attribute template, so the Web API controller action will be correctly selected.
In other words, if you need to route Web API and MVC in the same application you have to use routes which match different URLs for each action, and be careful with the order in which they are registered: register first the more specific templates, and then the more generic ones, so that the generic templates doesn't swallow the URIs which should be matched with the specific templates.
This is not the only option. You can also use route constraints, for example, if all your API has a particular naming convention which can be checked with a constraint (for example a Regex constraint), you can still use convention routing for Web API.
NOTE: apart from the route matching, it's also necessary that the HTTP method (like POST, GET, etc.) is supported by the action. So you can have two similar actions in the same route that accept different methods, and the Action selector would know which one to choose, but this doesn't solve your problem

Attribute based webapi2 routing returns 404 for some methods

I'm presently working on a project that has been upgraded to Webapi2 from Webapi. Part of the conversion includes the switch to using attribute based routing.
I've appropriately setup my routes in the Global.asax (as follows)
GlobalConfiguration.Configure(config => config.MapHttpAttributeRoutes());
and removed the previous routing configuration.
I have decorated all of my API controllers with the appropriate System.Web.Http.RouteAttribute and System.Web.Http.RoutePrefixAttribute attributes.
If I inspect System.Web.Http.GlobalConfiguration.Configuration.Routes with the debugger I can see that all my expected routes are registered in the collection. Likewise the appropriate routes are available within the included generated Webapi Help Page documentation as expected.
Even though all appears to be setup properly a good number of my REST calls result in a 404 not found response from the server.
I've found some notable similarities specific to GET methods (this is all I've tested so far)
If a method accepts 0 parameters it will fail
If a route overrides the prefix it will fail
If a method takes a string parameter it is likely to succeed
return type seems to have no affect
Naming a route seems to have no affect
Ordering a route seems to have no affect
Renaming the underlying method seems to have no affect
Worth noting is that my API controllers appear in a separate area, but given that some routes do work I don't expect this to be the issue at hand.
Example of non-functional method call
[RoutePrefix("api/postman")]
public class PostmanApiController : ApiController
{
...
[HttpGet]
[Route("all", Name = "GetPostmanCollection")]
[ResponseType(typeof (PostmanCollectionGet))]
public IHttpActionResult GetPostmanCollection()
{
return Ok(...);
}
...
}
I expect this to be available via http://[application-root]/api/postman/all
Interestingly enough a call to
Url.Link("GetPostmanCollection", null)
will return the above expected url
A very similar example of method calls within the same controller where some work and some do not.
[RoutePrefix("api/machine")]
public class MachineApiController : ApiController
{
...
[HttpGet]
[Route("byowner/{owner}", Name = "GetPostmanCollection")]
public IEnumerable<string> GetByOwner([FromUri] string owner)
{
...
}
...
[HttpGet]
[Route("~/api/oses/{osType}")]
public IEnumerable<OsAndVersionGet> GetOSes([FromUri] string osType)
{
...
}
...
}
Where a call to http://[application-root]/api/machineby/ownername succeeds and http://[application-root]/api/oses/osType does not.
I've been poking at this far too long, any idea as to what the issue may be?
Check that you configure your HttpConfiguration via the MapHttpAttributeRoutes method before any ASP.NET MVC routing registration.
In accordance to Microsoft's CodePlex entry on Attribute Routing in MVC and Web API the Design section states:
In most cases, MapHttpAttributeRoutes or MapMvcAttributeRoutes will be
called first so that attribute routes are registered before the global
routes (and therefore get a chance to supersede global routes).
Requests to attribute routed controllers would also be filtered to
only those that originated from an attribute route.
Therefore, within the Global.asax (or where registering routes) it is appropriate to call:
GlobalConfiguration.Configure(c => c.MapHttpAttributeRoutes()); // http routes
RouteTable.Routes.MapRoute(...); // mvc routes
In my case it was a stupid mistake, I am posting this so people behind me making the same mistake may read this before they check everything else at quantum level.
My mistake was, my controller's name did not end with the word Controller.
Happy new year

Query parameter route constraints

I just started using ASP.NET Web API 2.1 and have encountered a limitation. Using Attribute Routing, I can do the following:
[Route("item/{id:int}")]
public IHttpActionResult GetItem(int id)
{
...
}
The URL /item/5 will be routed to this action, but the URL /item/abc will not, because of the int constraint in {id:int}.
I tried changing my URL so that the id parameter was in the query string along with its constraint, despite the use of route constraints on query parameters never being mentioned or demonstrated in the documentation.
[Route("item?{id:int}")]
public IHttpActionResult GetItem(int id)
{
...
}
If I try to run now, I get an error on the Configure method call in Application_Start.
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
The message is as follows.
ArgumentException was unhandled by user code
The route template cannot start with a '/' or '~' character and it cannot contain a '?' character.
Two things bother me about this.
First, the section documenting Route Prefixes on MSDN makes clear that placing a ~ character at the start of the route template is completely acceptable. I tried it, and it works as documented.
Second, if not like this, how can I place a route constraint on a query parameter? Consider the following, with the route constraint removed.
[Route("item")]
public IHttpActionResult GetItem(int id)
{
...
}
The URL /item/5 will be routed to this action, with id set to 5 - but so will the URL /item/abc, with id set to 0.
Is there no way to place a route constraint on a query parameter?
According to http://attributerouting.net/#asp-net-web-api (†), this is not possible:
"Beware! Due to integration issues with the Web API WebHost framework, the following features will not work: …
querystring parameter constraints, …"
†) Note that this answer was written for a previous version of Web API where attribute routing was done using a separate AttributeRouting.WebApi NuGet package. Attribute routing has since been incorporated into the Web API core. Nevertheless, it appears that constraints on query string parameters are still not supported out of the box.

Categories

Resources