I am writing an azure function where I want to be able to handle the routing based on the url as part of the implementation. I want to be able to catch any url regardless of number of segments.
Based on:
https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=csharp#using-route-parameters
https://learn.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#constraints
I wrote the Route to accept up to 3 segments and can easily add more but I want to know if it's possible to just match any url?
[<FunctionName("MyFunction")>]
let myFunction
(
[<HttpTrigger(AuthorizationLevel.Anonymous, Route = "{seg1?}/{seg2?}/{seg3?}")>] req: HttpRequest,
log: ILogger,
context: ExecutionContext
) =
// Implementation
Note: Code is F# but should be equally applicable to C# and easy to adapt.
I have discovered you can match anything with a * at the start of the parameter name Route = "{*any}" will match any url.
Related
Recently I have read an article about URL and its parts and what is defined in the standard.
There is an interesting part about parameters. Usually, you pass parameters in one of the following ways:
In the URL path as a segment;
In the URL query string;
In the request body;
In the request header;
However, according to the article there is one more way - to pass parameters in the URL path segments separating them from the segment with a semicolon:
parameters – talking about parameters, these can also appear after the path but before the query string, also separated from the rest of the URL and from each other by ; characters e.g.:
http://www.blah.com/some/crazy/path.html;param1=foo;param2=bar
I have never encountered this approach before and the article mentions that it is rarely used.
Is is supported in .NET, particularly in ASP.NET or ASP.NET Core?
I don't really care if the kind of weird format for query parameters is standard or not. If you try it yourself, you'll see that ASP.NET Core does not support that format. It's still considered a segment and once parsed, if it's not matched by any route patterns, the response is simply 404.
To support that kind of weird format (I talk about the original format in your question, not another even weirder format in your comment), theoretically you can use a custom QueryStringValueProviderFactory. The default one creates the QueryStringValueProvider from Request.Query. In your custom one, you can create a QueryStringValueProvider from your own set of parameters which can be parsed from the raw request URL. However this way is not that easy because it's too late for your request to come at the model binding phase (where the value providers are used to build up the request model as well as the action parameters). Because it's too late so your request path does not even match with any route pattern so the pipeline will be short-circuited with a response of 404.
Technically to follow that approach, you need to somehow make it reach the model binding phase first (meaning make the request work as if the semicolon-separated query parameters didn't exist). I think it's possible to remove the last segment (if it contains any semicolon) inside the routing process. However it's of course not easy. That way is simply too complicated.
Here I would like to introduce another simpler (and working) approach. We can use a middleware to parse the URL and build-up the query string (maybe appended to the existing standard query string if any) ourselves before the request coming into the MVC pipeline.
It's just simple like this:
//an extension method for conveniently registering your middleware later
public static class ComplexQueryStringMiddlewareExtensions
{
public static IApplicationBuilder UseComplexQueryStringMiddleware(this IApplicationBuilder appBuilder)
{
return appBuilder.Use((context, next) => {
var path = context.Request.Path;
var semicolonSepParameters = path.Value.Split(';');
//the first part is always the correct path
context.Request.Path = semicolonSepParameters[0];
semicolonSepParameters = semicolonSepParameters.Skip(1).Where(e => !string.IsNullOrWhiteSpace(e)).ToArray();
if (semicolonSepParameters.Length > 0)
{
var appendedQueryString = string.Join("&", semicolonSepParameters);
//in case there is some standard query string as well
if (context.Request.Query != null && context.Request.Query.Count > 0)
{
appendedQueryString = context.Request.QueryString + "&" + appendedQueryString;
} else
{
appendedQueryString = "?" + appendedQueryString;
}
context.Request.QueryString = new Microsoft.AspNetCore.Http.QueryString(appendedQueryString);
}
return next();
});
}
}
Now inside the Startup.Configure method, ensure that your middleware registration is placed before UseRouting (if any):
app.UseComplexQueryStringMiddleware();
//if this exists (which is usually by the default generated code)
//this must be after the above
app.UseRouting();
//of course the UseEndpoints is always at the end
Now your http://www.blah.com/some/crazy/path.html;param1=foo;param2=bar should work just like http://www.blah.com/some/crazy/path.html?param1=foo¶m2=bar. You can even mix the 2 formats together like http://www.blah.com/some/crazy/path.html;param1=foo;param2=bar?param3=ok¶m4=yes.
I have an application with ~300 of services. I would like to add authorization for a specific subroute.
I've created a custom authorization attribute to to verify routes. I would like to convert the route templates to regex, so I can assure that any changes in the future will be automatically be applied back to the authorization attribute.
e.g I have a route.
var benefitRoute = "/employees/{employeeId:Guid}/benefit/{benefitId:guid}/enrollments";
I would like to replace all of the {} and their contents with .*
"/employees/.*/benefit/.*/enrollments";
However I tried to match on \{.+\} However it grabs the larger one before the two smaller ones, when I run my regex.
Regex.Replace(route, "\{.+\}", ".*");
How can I convert the route templates into Regex.
You should add '?' which makes quantifiers "lazy", try
var benefitRoute = "/employees/{employeeId:Guid}/benefit/{benefitId:guid}/enrollments";
string pattern = #"{(.*?)}";
String result=Regex.Replace(benefitRoute, pattern, ".*");
You can find more info on it here http://www.regular-expressions.info/repeat.html
I have a .NET Web API v2 and need to define a route that can contain forward slashes at any point in the route. I have it configured (something) like this in WebApiConfig:
config.Routes.MapHttpRoute(
name: "SomeRouteName",
routeTemplate: "api/summary/{*token}",
defaults: new { controller = "Summary" });
Unfortunately the token contains slashes and there's nothing I can do about it at this point. The above route works in most cases if the slash is not at the beginning of the token. So that takes care of most cases. But if token begins with slash(es), it doesn't work because that first slash gets interpreted as part of the URL I assume and gets eaten. So in my controller action, I have the following code (admittedly a hack that I'm trying to avoid):
if (summary == null)
{
summary = _repo.GetSummary($"/{token}");
}
Obviously, this will only work for a single slash. I could do a loop and add more, but there isn't way to know how many it could be. Currently no tokens in my DB begin with two slashes, so this bad code works for now. It was implemented as a band-aid until a better solution is found.
Edit: This references the * route, which mostly fixed my original issue, but still doesn't match the first slashes: URLs with slash in parameter?
Since OP said in some comment:
There is no purpose -- but the tokens are generated and not
necessarily by my code. So I don't have control over the token
generation.
and also:
I've attempted to UrlEncode /Decode to see if this works, but the
slash (encoded as %2F) still gets eaten for whatever reason. As a side
note, I can say that Base64 encoding it will fix this but that I can't
change this at this point because it would break the API for existing
apps.
I would say that one of best choices to avoid issues with special characters is firstly encoding the whole token as a base 64 string, and then url-encode it to encode possible characters like =.
That is, you can configure a route where you don't need {token*} but just {token}. If you need to simplify the token decoding, you can implement an ActionFilterAttribute that decodes the so-called bound parameter under the hoods.
Others have already done this way...
OAuth2 already sends basic authentication's credentials encoding them as a base64 string (you convert user:password to the whole base 64 string).
It's a common way of avoding these issues and base 64 strings can be decoded in every modern client and server platform.
I have the following configured route:
routes.MapHttpRoute("oneKey",
"{controller}/{id}");
If I go to the endpoint /users/Maticicero, the route will be correctly delegated to the GET method in my UsersController class.
However I am getting the id parameter just as it was typed, like Maticicero.
Since this is the primary key of my database, and the comparision is case sensitive, I need to normalize my ids to lowercase, that is:
id = id.ToLower()
I have to do this in every controller and in every method that takes the id parameter.
Is there a way to tell my Web Api (or route handler) to automatically lower case these parameters (or the whole url)?
Try to use routes.LowercaseUrls = true; when you register your routes. MSDN
RouteCollection.LowercaseUrls:
Gets or sets a value that indicates whether URLs are converted to
lower case when virtual paths are normalized.
But be careful when you use your requests like this http://host/controller?id=Maticicero
If a query string is included in the URL, that part of the URL is not
converted to lower case.
Also take a look at this SO question: How can I have lowercase routes in ASP.NET MVC?
You can trap the URL in the Application_BeginRequest event, parse the URL, and use HttpContext.RewritePath to modify the original URL.
https://msdn.microsoft.com/en-us/library/sa5wkk6d(v=vs.110).aspx
I'm trying to build up a proper routing scheme for my products section in MVC 2. I've got the following criteria:
Links of the format
/Products/(MX[0-9]+) and /Products/(BDL[0-9A-Z_])
Need to route to ProductsController.Show(Id = $1)
Links of the format
/Products/([a-zA-Z0-9/]+)
Example: http://www.mysite.com/Products/Cameras/Digital/
Need to route to ProductsController.List(Category = $1)
Then on top of this, I want links like
/Products/AddToCart/{1} to work normally.
So far I've been able to get the above two to work fine through a relatively hack-ish method (all past /Products/ is routed to show, where some conditional logic redirects to .List(Category) if the start of the input isn't MX or BDL
I'm not happy with the current implementation and am open to some help. Thank you in advance.
Use regular expression constraints for your routes and place them with AddToCart first, MX and BDL second, and the catch all products last. If you'd like even more control than that, you can create custorm routes.
I don't recall the syntax off the top of my head, but you can add regex constraints on your routes so that they route to different places.