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
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 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.
I know on client side (javascript) you can use windows.location.hash but could not find anyway to access from the server side. I'm using asp.net.
We had a situation where we needed to persist the URL hash across ASP.Net post backs. As the browser does not send the hash to the server by default, the only way to do it is to use some Javascript:
When the form submits, grab the hash (window.location.hash) and store it in a server-side hidden input field Put this in a DIV with an id of "urlhash" so we can find it easily later.
On the server you can use this value if you need to do something with it. You can even change it if you need to.
On page load on the client, check the value of this this hidden field. You will want to find it by the DIV it is contained in as the auto-generated ID won't be known. Yes, you could do some trickery here with .ClientID but we found it simpler to just use the wrapper DIV as it allows all this Javascript to live in an external file and be used in a generic fashion.
If the hidden input field has a valid value, set that as the URL hash (window.location.hash again) and/or perform other actions.
We used jQuery to simplify the selecting of the field, etc ... all in all it ends up being a few jQuery calls, one to save the value, and another to restore it.
Before submit:
$("form").submit(function() {
$("input", "#urlhash").val(window.location.hash);
});
On page load:
var hashVal = $("input", "#urlhash").val();
if (IsHashValid(hashVal)) {
window.location.hash = hashVal;
}
IsHashValid() can check for "undefined" or other things you don't want to handle.
Also, make sure you use $(document).ready() appropriately, of course.
[RFC 2396][1] section 4.1:
When a URI reference is used to perform a retrieval action on the
identified resource, the optional fragment identifier, separated from
the URI by a crosshatch ("#") character, consists of additional
reference information to be interpreted by the user agent after the
retrieval action has been successfully completed. As such, it is not
part of a URI, but is often used in conjunction with a URI.
(emphasis added)
[1]: https://www.rfc-editor.org/rfc/rfc2396#section-4
That's because the browser doesn't transmit that part to the server, sorry.
Probably the only choice is to read it on the client side and transfer it manually to the server (GET/POST/AJAX).
Regards
Artur
You may see also how to play with back button and browser history
at Malcan
Just to rule out the possibility you aren't actually trying to see the fragment on a GET/POST and actually want to know how to access that part of a URI object you have within your server-side code, it is under Uri.Fragment (MSDN docs).
Possible solution for GET requests:
New Link format: http://example.com/yourDirectory?hash=video01
Call this function toward top of controller or http://example.com/yourDirectory/index.php:
function redirect()
{
if (!empty($_GET['hash'])) {
/** Sanitize & Validate $_GET['hash']
If valid return string
If invalid: return empty or false
******************************************************/
$validHash = sanitizeAndValidateHashFunction($_GET['hash']);
if (!empty($validHash)) {
$url = './#' . $validHash;
} else {
$url = '/your404page.php';
}
header("Location: $url");
}
}
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.
ASP.NET c# project... trying to do a very simple page route.
Please note that I know this is NOT actually doing any dynamic routing... I have the id hard coded like this for a reason.
Example:
RouteTable.Routes.MapPageRoute("Test", "ABC", "~/Test.aspx?id=101");
I can browse to http://www.mysite.com/ABC no problems, the page Test.aspx loads, the routing is working as expected.
BUT... where has my id=101 gone?
Request.QueryString["id"] \\ is null...
Page.RouteData.Values["id"] \\ is null...
How can I get hold of the hard coded id in my target resource for the routing?
I got it working by passing DataTokens.
In my real world scenario I don't know what the URL parameters will be (there could be just the "id" like in my question... or there could be others, sometimes none), so I have to do the following:
First check to see if there is a "?" character in the routing target... if there is, then:
Run the string after the "?" character through HttpUtility.ParseQueryString
Then, loop through that collection and add them to a System.Web.Routing.RouteValueDictionary
Then finally add the route, with the DataTokens property set to the RouteValueDictionary