I have five different HttpPost methods. Each one gets a client from the same end point. For this reason, I am repeating the code where I get the section, uri and endpoint values for the configuration.
I thought of getting these values with a method to simplify the code. But I can't decide on the return type of the method. Is there a special return type for this case?
public ? GetReportConfig(string sectionName, string host, string endpoint)
{
var section = this._configuration.GetSection(sectionName);
string uriString = section.GetValue<string>(host);
string endpointString = section.GetValue<string>(endpoint);
}
I have the following code:
if (Request.Url.AbsolutePath == "/Guidance.aspx")
{
if (Request.IsSecureConnection)
{
Reponse.Redirect("http://www.example.com/Guidance.aspx");
}
return;
}
The thing is that Guidance can have a querystring with it. I like to then Redirect to the same page name and append the querystring. Haven't found a way to do this.
if (Request.Url.AbsolutePath == "/Guidance.aspx?id='vid09'")
{
if (Request.IsSecureConnection)
{
Reponse.Redirect("http://www.example.com/Guidance.aspx?id='vid09'");
}
return;
}
How can I simplify the code above to do it with any querystring that comes its way.
Use UriBuilder and replace parts you need. Something like:
var builder = new UriBuilder(Request.Url);
builder.Scheme = "http";
Reponse.Redirect(builder.ToString);
string myUrl = Request.RawUrl.toString();
if (myUrl.Contains("/Guidance.aspx")
{
if (Request.IsSecureConnection)
{
var queryString = myUrl.Substring(myUrl.IndexOf("?"));
Reponse.Redirect("http://www.example.com/Guidance.aspx" + queryString);
}
return;
}
Don't get fancy, the URI is already parsed for you (don't do it yourself with unreliable regular expressions). The Url property you're using is a System.Uri object. You may simply compare the scheme, host, and any HTTP segment you may need, then construct your redirection URI by adding only the query string component from the original URI. All you need is in the Uri class.
Is there a better/more accurate/stricter method/way to find out if a URL is properly formatted?
Using:
bool IsGoodUrl = Uri.IsWellFormedUriString(url, UriKind.Absolute);
Doesn't catch everything. If I type htttp://www.google.com and run that filter, it passes. Then I get a NotSupportedExceptionlater when calling WebRequest.Create.
This bad url will also make it past the following code (which is the only other filter I could find):
Uri nUrl = null;
if (Uri.TryCreate(url, UriKind.Absolute, out nUrl))
{
url = nUrl.ToString();
}
The reason Uri.IsWellFormedUriString("htttp://www.google.com", UriKind.Absolute) returns true is because it is in a form that could be a valid Uri. URI and URL are not the same.
See: What's the difference between a URI and a URL?
In your case, I would check that new Uri("htttp://www.google.com").Scheme was equal to http or https.
Technically, htttp://www.google.com is a properly formatted URL, according the URL specification. The NotSupportedException was thrown because htttp isn't a registered scheme. If it was a poorly-formatted URL, you would have gotten a UriFormatException. If you just care about HTTP(S) URLs, then just check the scheme as well.
#Greg's solution is correct. However you can steel using URI and validate all protocols (scheme) that you want as valid.
public static bool Url(string p_strValue)
{
if (Uri.IsWellFormedUriString(p_strValue, UriKind.RelativeOrAbsolute))
{
Uri l_strUri = new Uri(p_strValue);
return (l_strUri.Scheme == Uri.UriSchemeHttp || l_strUri.Scheme == Uri.UriSchemeHttps);
}
else
{
return false;
}
}
This Code works fine for me to check a Textbox have valid URL format
if((!string.IsNullOrEmpty(TXBProductionURL.Text)) && (Uri.IsWellFormedUriString(TXBProductionURL.Text, UriKind.Absolute)))
{
// assign as valid URL
isValidProductionURL = true;
}
I am really surprised that there is no native .NET method to get an absolute url from a relative url. I know this has been discussed many times, but never have come across a satisfactory method that handles this well. Can you help fine tune the method below?
I think all I need left is to auto choose the protocol instead of hard coding it (http/https). Anything else I am missing (caveats, performance, etc)?
public static string GetAbsoluteUrl(string url)
{
//VALIDATE INPUT FOR ALREADY ABSOLUTE URL
if (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase)
|| url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
{
return url;
}
//GET PAGE REFERENCE FOR CONTEXT PROCESSING
Page page = HttpContext.Current.Handler as Page;
//RESOLVE PATH FOR APPLICATION BEFORE PROCESSING
if (url.StartsWith("~/"))
{
url = page.ResolveUrl(url);
}
//BUILD AND RETURN ABSOLUTE URL
return "http://" + page.Request.ServerVariables["SERVER_NAME"] + "/"
+ url.TrimStart('/');
}
This has always been my approach to this little nuisance. Note the use of VirtualPathUtility.ToAbsolute(relativeUrl) allows the method to be declared as an extension in a static class.
/// <summary>
/// Converts the provided app-relative path into an absolute Url containing the
/// full host name
/// </summary>
/// <param name="relativeUrl">App-Relative path</param>
/// <returns>Provided relativeUrl parameter as fully qualified Url</returns>
/// <example>~/path/to/foo to http://www.web.com/path/to/foo</example>
public static string ToAbsoluteUrl(this string relativeUrl) {
if (string.IsNullOrEmpty(relativeUrl))
return relativeUrl;
if (HttpContext.Current == null)
return relativeUrl;
if (relativeUrl.StartsWith("/"))
relativeUrl = relativeUrl.Insert(0, "~");
if (!relativeUrl.StartsWith("~/"))
relativeUrl = relativeUrl.Insert(0, "~/");
var url = HttpContext.Current.Request.Url;
var port = url.Port != 80 ? (":" + url.Port) : String.Empty;
return String.Format("{0}://{1}{2}{3}",
url.Scheme, url.Host, port, VirtualPathUtility.ToAbsolute(relativeUrl));
}
new System.Uri(Page.Request.Url, "/myRelativeUrl.aspx").AbsoluteUri
This one works for me...
new System.Uri(Page.Request.Url, ResolveClientUrl("~/mypage.aspx")).AbsoluteUri
With ASP.NET, you need to consider the reference point for a "relative URL" - is it relative to the page request, a user control, or if it is "relative" simply by virtue of using "~/"?
The Uri class contains a simple way to convert a relative URL to an absolute URL (given an absolute URL as the reference point for the relative URL):
var uri = new Uri(absoluteUrl, relativeUrl);
If relativeUrl is in fact an abolute URL, then the absoluteUrl is ignored.
The only question then remains what the reference point is, and whether "~/" URLs are allowed (the Uri constructor does not translate these).
Here is my own version that handles many validations and relative pathing from user's current location option. Feel free to refactor from here :)
/// <summary>
/// Converts the provided app-relative path into an absolute Url containing
/// the full host name
/// </summary>
/// <param name="relativeUrl">App-Relative path</param>
/// <returns>Provided relativeUrl parameter as fully qualified Url</returns>
/// <example>~/path/to/foo to http://www.web.com/path/to/foo</example>
public static string GetAbsoluteUrl(string relativeUrl)
{
//VALIDATE INPUT
if (String.IsNullOrEmpty(relativeUrl))
return String.Empty;
//VALIDATE INPUT FOR ALREADY ABSOLUTE URL
if (relativeUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase)
|| relativeUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
return relativeUrl;
//VALIDATE CONTEXT
if (HttpContext.Current == null)
return relativeUrl;
//GET CONTEXT OF CURRENT USER
HttpContext context = HttpContext.Current;
//FIX ROOT PATH TO APP ROOT PATH
if (relativeUrl.StartsWith("/"))
relativeUrl = relativeUrl.Insert(0, "~");
//GET RELATIVE PATH
Page page = context.Handler as Page;
if (page != null)
{
//USE PAGE IN CASE RELATIVE TO USER'S CURRENT LOCATION IS NEEDED
relativeUrl = page.ResolveUrl(relativeUrl);
}
else //OTHERWISE ASSUME WE WANT ROOT PATH
{
//PREPARE TO USE IN VIRTUAL PATH UTILITY
if (!relativeUrl.StartsWith("~/"))
relativeUrl = relativeUrl.Insert(0, "~/");
relativeUrl = VirtualPathUtility.ToAbsolute(relativeUrl);
}
var url = context.Request.Url;
var port = url.Port != 80 ? (":" + url.Port) : String.Empty;
//BUILD AND RETURN ABSOLUTE URL
return String.Format("{0}://{1}{2}{3}",
url.Scheme, url.Host, port, relativeUrl);
}
If you're in the context of an MVC Controller or View you can use the UrlHelper which should be accessible via just Url
Url.Content("~/content/images/myimage.jpg")
Which will be fully expanded to /virtual_directoryname/content/images/myimage.jpg
This can be used in a controller or .cshtml file
Yes it is a little odd that it's called Content but it's meant to be used to get an absolute path to a resource so it makes sense
Still nothing good enough using native stuff. Here is what I ended up with:
public static string GetAbsoluteUrl(string url)
{
//VALIDATE INPUT
if (String.IsNullOrEmpty(url))
{
return String.Empty;
}
//VALIDATE INPUT FOR ALREADY ABSOLUTE URL
if (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
{
return url;
}
//GET CONTEXT OF CURRENT USER
HttpContext context = HttpContext.Current;
//RESOLVE PATH FOR APPLICATION BEFORE PROCESSING
if (url.StartsWith("~/"))
{
url = (context.Handler as Page).ResolveUrl(url);
}
//BUILD AND RETURN ABSOLUTE URL
string port = (context.Request.Url.Port != 80 && context.Request.Url.Port != 443) ? ":" + context.Request.Url.Port : String.Empty;
return context.Request.Url.Scheme + Uri.SchemeDelimiter + context.Request.Url.Host + port + "/" + url.TrimStart('/');
}
When you want to generate URL from your Business Logic layer, you do not have the flexibility of using ASP.NET Web Form's Page class/ Control's ResolveUrl(..) etc. Moreover, you may need to generate URL from ASP.NET MVC controller too where you not only miss the Web Form's ResolveUrl(..) method, but also you cannot get the Url.Action(..) even though Url.Action takes only Controller name and Action name, not the relative url.
I tried using
var uri = new Uri(absoluteUrl, relativeUrl)
approach, but there is a problem too. If the web application is hosted in IIS virtual directory, where the url of the app is like this : http://localhost/MyWebApplication1/, and the relative url is "/myPage" then the relative url is resolved as "http://localhost/MyPage" which is another problem.
Therefore, in order to overcome such problems, I have written a UrlUtils class which can work from a class library. So, it wont depend on Page class but it depends on ASP.NET MVC. So, if you dont mind adding reference to MVC dll to your class library project then my class will work smoothly. I have tested in IIS virtual directory scenario where the web application url is like this : http://localhost/MyWebApplication/MyPage. I realized that, sometimes we need to make sure that the Absolute url is SSL url or non SSL url. So, I wrote my class library supporting this option. I have restricted this class library so that the relative url can be absolute url or a relative url that starts with '~/'.
Using this library, I can call
string absoluteUrl = UrlUtils.MapUrl("~/Contact");
Returns : http://localhost/Contact
when the page url is : http://localhost/Home/About
Returns : http://localhost/MyWebApplication/Contact
when the page url is : http://localhost/MyWebApplication/Home/About
string absoluteUrl = UrlUtils.MapUrl("~/Contact", UrlUtils.UrlMapOptions.AlwaysSSL);
Returns : **https**://localhost/MyWebApplication/Contact
when the page url is : http://localhost/MyWebApplication/Home/About
Here is my class Library :
public class UrlUtils
{
public enum UrlMapOptions
{
AlwaysNonSSL,
AlwaysSSL,
BasedOnCurrentScheme
}
public static string MapUrl(string relativeUrl, UrlMapOptions option = UrlMapOptions.BasedOnCurrentScheme)
{
if (relativeUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
relativeUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
return relativeUrl;
if (!relativeUrl.StartsWith("~/"))
throw new Exception("The relative url must start with ~/");
UrlHelper theHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
string theAbsoluteUrl = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority) +
theHelper.Content(relativeUrl);
switch (option)
{
case UrlMapOptions.AlwaysNonSSL:
{
return theAbsoluteUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase)
? string.Format("http://{0}", theAbsoluteUrl.Remove(0, 8))
: theAbsoluteUrl;
}
case UrlMapOptions.AlwaysSSL:
{
return theAbsoluteUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase)
? theAbsoluteUrl
: string.Format("https://{0}", theAbsoluteUrl.Remove(0, 7));
}
}
return theAbsoluteUrl;
}
}
The final version taking care of all previous complaints (ports, logical url, relative url, existing absolute url...etc.) considering the current handler is the page:
public static string ConvertToAbsoluteUrl(string url)
{
if (!IsAbsoluteUrl(url))
{
if (HttpContext.Current != null && HttpContext.Current.Request != null && HttpContext.Current.Handler is System.Web.UI.Page)
{
var originalUrl = HttpContext.Current.Request.Url;
return string.Format("{0}://{1}{2}{3}", originalUrl.Scheme, originalUrl.Host, !originalUrl.IsDefaultPort ? (":" + originalUrl.Port) : string.Empty, ((System.Web.UI.Page)HttpContext.Current.Handler).ResolveUrl(url));
}
throw new Exception("Invalid context!");
}
else
return url;
}
private static bool IsAbsoluteUrl(string url)
{
Uri result;
return Uri.TryCreate(url, UriKind.Absolute, out result);
}
check the following code to retrieve absolute Url :
Page.Request.Url.AbsoluteUri
I hope to be useful.
This works fine too:
HttpContext.Current.Server.MapPath(relativePath)
Where relative path is something like "~/foo/file.jpg"
foreach (var node in root.Find("a[href]"))
{
var href = node.Attributes["href"].Value;
Uri uri;
try
{
uri = new Uri(item.Value.Uri, href);
}
catch(UriFormatException)
{
continue;
}
// *snip*
try
{
if (_imageHosts.IsMatch(uri.Host)) // <--- problematic line
priority--;
}catch(UriFormatException)
{
MessageBox.Show(uri.OriginalString); // <--- gets displayed when I expected it wouldn't
continue;
}
// *snip*
}
The message box shows up with an address like
mailto: webmaster [ # ] somehost ?webmaster
Which is obviously malformed, but what I don't get is why it wasn't caught by the first catch block?
MSDN says it can only throw an InvalidOperationException. This is quite problematic, because it means my app can explode at any time then!
[[snip]]
First of all, I want to say its no so good idea to use Exception for checking validity because you can use Uri.TryCreate method. So you can rewrite your code and not rely it on which exception can be thrown and catched.
So better change your
Uri uri;
try
{
uri = new Uri(item.Value.Uri, href);
}
catch(UriFormatException)
{
continue;
}
to
Uri uri;
if (!Uri.TryCreate(item.Value.Uri, href, out uri)) continue;
But this is not full check anyway.
As for your question, answer is relatively simple. You are wrong assuming malformed:
mailto: webmaster [ # ] somehost ?webmaster
URI is Uniform Resource Identifier so its basic syntax
{scheme name} : {hierarchical part} [ ? {query} ] [ # {fragment} ]
obviously valid for your input. You are end with resource's URI with "mailto:" scheme.
When you try to access Host property you assume resource was Http, but "mailto"-scheme parser used by default can't parse original string for host component and hence raised exception.
So to write your check correctly you have to modify your code a bit:
Uri uri;
if (!Uri.TryCreate(item.Value.Uri, href, out uri)) continue;
if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps) continue;
Read some info about UriParser
Here update based on #Mark comments.
I'm pretty sure it threw an exception when I tried to get the AbsoluteUri property too..why should that fail?
You can't pass Scheme check since it will be "mailto". So here quick test:
var baseUri = new Uri("http://localhost");
const string href = "mailto: webmaster [ # ] somehost ?webmaster";
Uri uri;
if (!Uri.TryCreate(baseUri,href, out uri))
{
Console.WriteLine("Can't create");
return;
}
if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)
{
Console.WriteLine("Wrong scheme");
return;
}
Console.WriteLine("Testing uri: {0}", uri);
It ends with "Wrong scheme". Maybe I don't understand you correctly?
When you change href to:
const string href = "http: webmaster [ # ] somehost ?webmaster";
It passed correctly, automatically escaping uri to:
http://localhost/%20webmaster%20%5B%20#%20%5D%20somehost%20?webmaster
also all uri's components will be available to you.
The main problem I try to explain in first part following:
It seems to me you incorrectly treats any Uniform Resource Identifier as http(s) based url, but this is wrong. mailto:webmaster#somehost.tst or gopher://gopher.hprc.utoronto.ca/ or myreshandler://something#somewhere also valid URI which can be succesfully parsed. Take a look on Official IANA-registered schemes
So
Uri constructor behaviour is expected and correct.
it tries validate incoming URI for known schemes:
UriSchemeFile - Specifies that the URI is a pointer to a file.
UriSchemeFtp - Specifies that the URI is accessed through the File Transfer Protocol (FTP).
UriSchemeGopher - Specifies that the URI is accessed through the Gopher protocol.
UriSchemeHttp - Specifies that the URI is accessed through the Hypertext Transfer Protocol (HTTP)
UriSchemeHttps - Specifies that the URI is accessed through the Secure Hypertext Transfer Protocol (HTTPS).
UriSchemeMailto - Specifies that the URI is an email address and is accessed through the Simple Network Mail Protocol (SNMP).
UriSchemeNews - Specifes that the URI is an Internet news group and is accessed through the Network News Transport Protocol (NNTP).
UriSchemeNntp - Specifies that the URI is an Internet news group and is accessed through the Network News Transport Protocol (NNTP)
Basic URI parser is used when scheme is not known (see URI scheme generic syntax) .
Basicly Uri.TryCreate() and scheme checks enough to get links which can be passed to .NET HttpWebRequest for example. You don't reallyneed check whether they well-formed or no. If links are bad (not well-formed or don't exists) you just get corresponded HttpError when try to request them.
As for your example:
http://www.google.com/search?q=cheesy poof
it passes my check and becomes:
http://www.google.com/search?q=cheesy%20poof
You don't need to check is it well-formed or no. Just do base checks and try request. Hope it helps.
Also, the string mailto: webmaster [ # ] somehost ?webmaster is malformed. I literally mean, that string, with the stupid []s and everything in it
This string is malformed by meaning is not well-formed (since contains excluded characters according RFC 2396) but it still can be considered as valid due to conformance generic syntax of URI scheme (check also how it escaped when created with http:).
If you dig deep into the Uri.Host property (real deep), it can eventually call a static function GetException which returns UriFormatException objects for different conditions of invalid URIs. Print out the full UriFormatException you are getting and compare it to the ones generated by Uri.GetException. You might get more details out of it.
Based on Nick's answer:
private static readonly string[] SupportedSchmes = { Uri.UriSchemeHttp, Uri.UriSchemeHttps, Uri.UriSchemeFtp, Uri.UriSchemeFile };
private static bool TryCreateUri(string uriString, out Uri result)
{
return Uri.TryCreate(uriString, UriKind.Absolute, out result) && SupportedSchmes.Contains(result.Scheme);
}
private static bool TryCreateUri(Uri baseAddress, string relativeAddress, out Uri result)
{
return Uri.TryCreate(baseAddress, relativeAddress, out result) && SupportedSchmes.Contains(result.Scheme);
}