I have a route in controller that should match everything in part of url and put it into string parameter.
What I have is:
[Route("api/proxy/{proxyId}/{*parameter}")]
public Task<HttpResponseMessage> Mediate(int proxyId, string parameter)
and for an unknown url, for example:
http://localhost/api/proxy/1/test?a=1&b=2
I would like "parameter" variable to contain:
test?a=1&b=2
Instead, it contains:
test
How can I specify route to not cut everything after question mark?
For this particular case I can extract it from Request.RequestUri object, but it would be.. inelegant.
You cannot do that. By definition the URL segments doesn't include the query string.
However, you can do something really easy: inside your WebApi controller you have the Request property which contains the Query String:
Request.RequestUri.Query
You simply have to concatenate the url param with this to have what you need. This includes the leading question mark:
The Query property contains any query information included in the URI. Query information is separated from the path information by a question mark (?) and continues to the end of the URI. The query information returned includes the leading question mark.
from Uri.Query Property
If you still want to force it to work in a different way, you'd need to include your own custom route provider, implementeing your own IDirectRouteProvider and registering it. See this: get a list of attribute route templates asp.net webapi 2.2
But doing something like this is unnatural. Why do things exactly in a different way as the standard way that all other people aunderstand and use?
Related
As part of a custom log in page, I'm trying to get the querystring part of a URL string that may represent an absolute or a relative URL. If it's an absolute URL, I can use use the Uri.Query property, but this is not supported for relative URLs.
Is it as simple as getting the substring starting at the first instance of a '?' or is it possible for a URL to contain a question mark before the query string? Or can any other text come after the query string?
returnUrl.Substring(returnUrl.IndexOf('?'))
Where returnUrl may be absolute: "http://www.example.com/anydir/any-page1?param=1" or relative: "/anydir/any-page?param=1"
Only the first question mark in the URL has significance to indicate the start of the query string. Any after it are treated as a literal question mark.
See RFC 3986 3.4 and 3.3
It does note however:
The characters slash ("/") and question mark ("?") are allowed to
represent data within the fragment identifier. Beware that some
older, erroneous implementations may not handle this data correctly
when it is used as the base URI for relative references
I'm using Grapevine and I want to route request traffic that has a query string, and I don't know regular expressions well enough to figure out why it's not working.
e.g.
http://localhost:1234/service/function?param1=1¶m2=2
And I defined my RESTRoute like this:
[RESTRoute(Method = HttpMethod.GET, PathInfo = #"^/service/function\?\D+$")]
public void HandleFooRequestString(HttpListenerContext context)
{
PrintRequest(context);
this.SendTextResponse(context, "foo is a success!");
}
But traffic sent to the URL isn't going to that method. What am I doing wrong?
This is a pretty common question, and has spawned some changes to the Grapevine road map.
Grapevine 3.1.0 +
In the most recent version of Grapevine, the query string is stripped off of the URL prior to regular expression pattern matching, so this is no longer a problem.
Additionally, routing is being updated in version 4 to match the Node/Express routing mechanism, so you can opt to not use regular expressions at all if you prefer. However, this version is still in the planning stages.
Grapevine 3.0.x
While it certainly isn't uncommon for GET requests to contain query string parameters, let's first make sure we are using proper URI design. To quote from O'Reilly's RESTful Web Services, page 233, under the header URI Design (emphasis mine):
When designing URIs, use path variables to separate elements of a hierarchy, or a path through a directed graph. Example: /weblogs/myweblog/entries/100 goes from the general to the specific. From a list of weblogs, to a particular weblog, to the entries in that weblog, to a particular entry. Each path variable is in some sense "inside" the previous one.
Use punctuation characters to separate multiple pieces of data at the same level of hierarchy. Use commas when the order of the items matters, as it does in latitude and longitude: /Earth/37.0,-95.2. Use semicolons when the order doesn't matter: /color-blends/red;blue.
Use query variables only to suggest arguments being plugged into an algorithm, or when the other two techniques fail. If two URIs differ only in their query variables, it implies that they're the different sets of inputs into the same underlying algorithm.
The big takeaway here is that our URIs, generally speaking, should only use the query string to pass arguments to an algorithm. If, indeed, we are expecting query string parameters in our methods, those should be validated by the method the request is routed to, and our PathInfo regular expressions should reflect the possibility of receiving such arguments.
Example: When not to use query string
Suppose you wanted to request user data given a specific numeric user id, let's say 632. This is a scenario where it might be tempting to use the query string, but it would be better not to.
Less Correct URI: /user?id=632
More Correct URI: /user/632
The RESTRoute for the more-correct URI would then look like this:
[RESTRoute(Method = HttpMethod.GET, PathInfo = #"^/user/\d+$")]
public void GetUser(HttpListenerContext context)
{
var userid = context.RawUrl.GrabFirst(#"^/user/(\d+)$");
// you got your user id, do something
}
Example: Correctly using the query string
If you wanted to create a REST route that multiplied two integers together, then - setting aside that URI are suppose to represent resources, not operations on a resource - using a query string might be more suitable.
Probably Less Correct URI: /2/product/3
Probably More Correct URI: /product?x=2&y=3
The RESTRoute for the probably-more-correct URI would then look like this:
[RESTRoute(Method = HttpMethod.GET, PathInfo = #"^/product")]
public void MultiplyTwoIntegers(HttpListenerContext context)
{
var x = context.Request.QueryString["x"];
var y = context.Request.QueryString["y"];
// Verify the inputs and do the math.
}
Note that the PathInfo regular expression omits the trailing $, which would normally indicate the end of the string, and we let the route method handle getting the parameters passed. If we really wanted to be a stickler, we could also write it like this:
[RESTRoute(Method = HttpMethod.GET, PathInfo = #"^/product\?.+$")]
Which would ensure that it at least looked like there might be some parameters coming in the query string, but isn't really necessary since we are going to do that checking anyway.
I know you can apply a wildcard in the route attribute to allow / such as date input for example:
[Route("orders/{*orderdate}")]
The problem with wildcard is only applicable to the last paramter in URI. How do I solve the issue if want to have the following URI:
[Route("orders/{orderdate}/customers")]
Update:
I know there are few options to solve the issue by refactoring the code so please do not offer a solution something like:
change the route template to [Route("orders/customers/{orderdate}")]
change the date to a different format (e.g. "dd-mm-yyyy")
#bet.. I think the genericUriParserOptions is no longer applicable to .net 4.5 or later..
Also as suggested by #JotaBe, you might need to correctly decode the url request. In most case the %2F will be automatically translated to a slash '/'. So if you need to escape it you will need to decode the '%' char in the first place.. so your URL: will look something like: www.domain.com/api/orders/23%252F06%252F2015/customers
Notice the characters '%252F' will be translated to the actual '%2F'
EDIT
Ok here is the complete solution (Tried it and working for me):
Assuming you have an API endpoint like so:
[Route("orders/{date}/customers")]
public HttpResponseMessage Get(string date)
{
}
In the web.config you will need to set the requestPathInvalidCharacters to empty which tells the asp.net to allow all request
<system.web>
<httpRuntime targetFramework="4.5" requestPathInvalidCharacters=""/>
</system.web>
<system.webServer>
<security>
<requestFiltering allowDoubleEscaping="true" />
</security>
</system.webServer>
When the client sending the request to the API you will need to make sure to escape the '%' like so:
www.domain.com/api/orders/23%252F06%252F2015/customers
You then need to decode the request
[Route("orders/{date}/customers")]
public HttpResponseMessage Get(string date)
{
DateTime actualDate = DateTime.Parse(System.Net.WebUtility.UrlDecode(date)); // date is 23/06/2015
}
As noted in the comment by #AlexeiLevenkov, this is wrong:
You cannot have a parameter in the URL which accepts forward slashes, because this is a special symbol which separates each URL fragment. So, whenever you include this symbol in your URL, there will be new fragments, and a single parameter can't include several fragments.
If you want more details, read this, but these are the most relevant excerpts:
the URL path finishes in the first ? or # found in the URL. So, the slashes only create fragments in the section of the URL path before the occurrence or one of those symbols.
From section 3.4: The query component is indicated by the first question mark ("?") character and terminated by a number sign ("#") character or by the end of the URI.
So, the query string can include forward slashes, /, if desired, and they will not define path segments at all.
These are some solutions for the question:
include fragments for day, month and year, like this: [Route("orders/{month}/{day}/{year}/customers")] and then create the date on the server side
require the user to use a different separator, like dash or dot, which won't create problems, receive it at string an parse it yourself (or use your own custom binder to support that format)
use the URL Rewrite extension to change the URL before it reaches the routing system, and parse it as explained in the previous solution (this requires hosting in IIS)
receive it as a query string, i.e. something like this: ´?date=02/03/2015´ (you'd better encode it)
NOTE: your original question said "query string", and my comment about encoding referred to the query string, which is the last segment of an URL after the question mark, if present, like &id=27. I corrected your question so that it doesn't mention "query string", which was not the right name for what you need
C# has its own method who skips the rules of escape sequences
the name of method is
Uri.UnescapeDataString(your querystring parameter)
you can use it while getting the parameters value
You can use the following URI [Route("orders/{DD:int}/{MM:int}/{YY:int}}/customers")]
and then use a custom model binder to take DD/MM/YY and turn them into a date that you can bind in your action method.
You can choose how you want to deal with constraints (go stricter with regex's) or use validation and return 400 if it doesn't match.
The simpler approach is, to take the Day/Month/Year and put it together in code.
Here is a link for dealing with modelbinding.
I have this route:
(Ignore the XML Format)
<route name="note" url="{noteId}-{title}">
<constraints>
<segment name="noteId" value="\d+" />
<segment name="title" value=".+" />
</constraints>
</route>
I want it to match urls like /1234-hello-kitty or /5578-a-b-c-ddd-f-g
MVC Routing Handler seems to be having some troubles with this.
I have been reading a lot about the subject, and I found out some interesting facts:
MVC first identifies the route segments and then it checks the constraints
MVC reads segments from right to left, meaning it first identifies title and then noteId
Taking the first example, I'm guessing MVC is identifying noteId as 1234-hello and title as kitty.
This fails when constraints are checked and therefore the route is not matched.
Is there another way to do this?
Please take in account that I want to keep both my segments noteId and title, and they should be both separated by a hyphen - (This is mandatory)
I can see couple of options for an approach to solving this issue:
URLRewriting
One possibility is to rewrite URLs (similar to mod_rewrite) to convert them from format that is imposed on you into a format that MVC can route natively. There is an IIS Module from Microsoft that does just that, and I believe (though not certain) would have the necessary functionality to accomplish the task in your case. The basic principle here is that if the format cannot be handled by MVC due to route template parsing rules, then convert the URL to something that it can manage before it even reaches the MVC route handling. URL Rewrite is an IIS Module that sits before MVC handler, examines the requests, and is able to rewrite the request from one form into another. Then, this altered form is what is seen by MVC and can be understood and parsed by it. E.g. the URL of /1234-hello-kitty can be rewritten by the module as /1234/hello-kitty and then MVC route template would be a simple {noteId}/{*title}. The downside caveat here is that generating links may not work here since generated links would look like /1234/hello-kitty rather than /1234-hello-kitty. However, mitigation may be to have a route specifically for link generation and not for routing defined as {noteId}-{title}. I believe (should be verified) that this will actually generate a link in form /1234-hello-kitty (albeit not being able to parse it on incoming request).
Custom MVC route handler
This one basically draws on the idea that if MVC doesn't do it for you, override its behavior to do what you wish it would do. The tactical aspect of this is described in SO post on how to provide your own handler. The way you would use it is you can provide your own interpretation of parsing of segments of url to route data, and provide the actual values as you parse them into requestContext.RouteData.Values["nodeId"] = /* your code that gets noteId out of URL. */. The rest of the application works as any other, knowing nothing about this surgical intervention in routing.
I have been reading a lot about the subject, and I found out some
interesting facts:
MVC first identifies the route segments and then it checks the constraints
MVC reads segments from right to left, meaning it first identifies title and then noteId
Taking the first example, I'm guessing MVC is identifying noteId
as 1234-hello and title as kitty.
This fails when constraints are checked and therefore the route is not
matched.
Those facts and the guess are completely correct. This is how ASP.NET routing works, unfortunately.
Why?
ASP.NET routing Simply works in two phases, first parse all routes and second try to match them for every request.
Considering your case, first parses:
Split a routeUrl by "/". Each segment is a path segment. You have just one: "{noteId}-{title}".
For each path segment, split them into sub-segments: parameters and literals. Parameters are enclosed by {} and literals are the rest. You have 3 sub-segments: {noteId}, - and {title}
Then, try to match (when having multiple sub-segments):
Find the last occurrence of last literal (-) and match the text after the literal to the last parameter (title).
Repeats 1st to finish all parameters and literals. If the URL or sub-segments are longer, the match fails.
Possible solutions
So in order to use a literal you have to make sure that your literal won't occur in parameters. Since you are stick with a dash, you may have some possible solutions.
You can use one parameter and no literal with a matching constraint (e.g. ^\d+-[\w-]+$), then try to parse the id inside controller action. This requires no changes in existing URL structure.
You can switch places of title and noteId, like /hello-kitty-1234.
You can try double dashes as literal, like /1234--hello-kitty.
If I have a series of "pattern" Urls of the form:
http://{username}.sitename.com/
http://{username}.othersite.net/
http://mysite.com/{username}
and I have an actual Url of the form:
http://joesmith.sitename.com/
Is there any way that I can match a pattern Url and in turn use it to extract the username portion out the actual Url? I've thought of nasty ways to do it, but it just seems like there should be a more intuitive way to accomplish this.
ASP.NET MVC uses a similar approach to extract the various segments of the URL when it is building its routes. Given the example:
{controller}/{action}
So given the Url of the form, Home/Index, it knows that it is the Home controller calling the Index action method.
Not sure I understand this question correctly but you can just use a regular expression to match anything between 'http://' and the first dot.
A very simple regex will do:
':https?://([a-z0-9\.-]*[a-z0-9])\.sitename\.com'
This will allow any subdomain that only contains valid subdomain characters. Example of allowed subdomains:
joesmith.sitename.com
joe.smith.sitename.com
joe-smith.sitename.com
a-very-long-subdomain.sitename.com
As you can see, you might want to complicate the regex slightly. For instance, you could limit it to only allow a certain amount of characters in the subdomain.
It seems the the quickest and easiest solution is going off of Machine's answer.
var givenUri = "http://joesmith.sitename.com/";
var patternUri = "http://{username}.sitename.com/";
patternUri = patternUri.Replace("{username}", #"([a-z0-9\.-]*[a-z0-9]");
var result = Regex.Match(givenUri, patternUri, RegexOptions.IgnoreCase).Groups;
if(!String.IsNullOrEmpty(result[1].Value))
return result[1].Value;
Seems to work great.
Well, this "pattern URL" is a format you've made up, right? You basically you'll just need to process it.
If the format of it is:
anything inside "{ }" is a thing to capture, everything else must be as is
Then you'd just find the start/end index of those brackets, and match everything else. Then when you get to a place where one is, make sure you only look for chars such that they don't match whatever 'token' comes after the next ending '}'.
There are definitely different ways - ultimately though your server must be configured to handle (and possibly route) these different subdomain requests.
What I would do would be to answer all subdomain requests (except maybe some reserved words, like 'www', 'mail', etc.) on sitename.com with a single handler or page (I'm assuming ASP.NET here based on your C# tag).
I'd use the request path, which is easy enough to get, with some simple string parsing/regex routines (remove the 'http://', grab the first token up until '.' or '/' or '\', etc.) and then use that in a session, making sure to observe URL changes.
Alternately, you could map certain virtual paths to request urls ('joesmith.sitename.com' => 'sitename.com/index.aspx?username=joesmith') via IIS but that's kind of nasty too.
Hope this helps!