How to create a Uri instance parsed with GenericUriParserOptions.DontCompressPath - c#

When the .NET System.Uri class parses strings it performs some normalization on the input, such as lower-casing the scheme and hostname. It also trims trailing periods from each path segment. This latter feature is fatal to OpenID applications because some OpenIDs (like those issued from Yahoo) include base64 encoded path segments which may end with a period.
How can I disable this period-trimming behavior of the Uri class?
Registering my own scheme using UriParser.Register with a parser initialized with GenericUriParserOptions.DontCompressPath avoids the period trimming, and some other operations that are also undesirable for OpenID. But I cannot register a new parser for existing schemes like HTTP and HTTPS, which I must do for OpenIDs.
Another approach I tried was registering my own new scheme, and programming the custom parser to change the scheme back to the standard HTTP(s) schemes as part of parsing:
public class MyUriParser : GenericUriParser
{
private string actualScheme;
public MyUriParser(string actualScheme)
: base(GenericUriParserOptions.DontCompressPath)
{
this.actualScheme = actualScheme.ToLowerInvariant();
}
protected override string GetComponents(Uri uri, UriComponents components, UriFormat format)
{
string result = base.GetComponents(uri, components, format);
// Substitute our actual desired scheme in the string if it's in there.
if ((components & UriComponents.Scheme) != 0)
{
string registeredScheme = base.GetComponents(uri, UriComponents.Scheme, format);
result = this.actualScheme + result.Substring(registeredScheme.Length);
}
return result;
}
}
class Program
{
static void Main(string[] args)
{
UriParser.Register(new MyUriParser("http"), "httpx", 80);
UriParser.Register(new MyUriParser("https"), "httpsx", 443);
Uri z = new Uri("httpsx://me.yahoo.com/b./c.#adf");
var req = (HttpWebRequest)WebRequest.Create(z);
req.GetResponse();
}
}
This actually almost works. The Uri instance reports https instead of httpsx everywhere -- except the Uri.Scheme property itself. That's a problem when you pass this Uri instance to the HttpWebRequest to send a request to this address. Apparently it checks the Scheme property and doesn't recognize it as 'https' because it just sends plaintext to the 443 port instead of SSL.
I'm happy for any solution that:
Preserves trailing periods in path segments in Uri.Path
Includes these periods in outgoing HTTP requests.
Ideally works with under ASP.NET medium trust (but not absolutely necessary).

Microsoft says it will be fixed in .NET 4.0 (though it appears from the comments that it has not been fixed yet)
https://connect.microsoft.com/VisualStudio/feedback/details/386695/system-uri-incorrectly-strips-trailing-dots?wa=wsignin1.0#tabs
There is a workaround on that page, however. It involves using reflection to change the options though, so it may not meet the medium trust requirement. Just scroll to the bottom and click on the "Workarounds" tab.
Thanks to jxdavis and Google for this answer:
http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/5206beca-071f-485d-a2bd-657d635239c9

I'm curious if part of the problem is that you are only accounting for "don't compress path", instead of all the defaults of the base HTTP parser: (including UnEscapeDotsAndSlashes)
private const UriSyntaxFlags HttpSyntaxFlags = (UriSyntaxFlags.AllowIriParsing | UriSyntaxFlags.AllowIdn | UriSyntaxFlags.UnEscapeDotsAndSlashes | UriSyntaxFlags.CanonicalizeAsFilePath | UriSyntaxFlags.CompressPath | UriSyntaxFlags.ConvertPathSlashes | UriSyntaxFlags.PathIsRooted | UriSyntaxFlags.AllowAnInternetHost | UriSyntaxFlags.AllowUncHost | UriSyntaxFlags.MayHaveFragment | UriSyntaxFlags.MayHaveQuery | UriSyntaxFlags.MayHavePath | UriSyntaxFlags.MayHavePort | UriSyntaxFlags.MayHaveUserInfo | UriSyntaxFlags.MustHaveAuthority);
That's as opposed to the news that has flags (for instance):
private const UriSyntaxFlags NewsSyntaxFlags = (UriSyntaxFlags.AllowIriParsing | UriSyntaxFlags.MayHaveFragment | UriSyntaxFlags.MayHavePath);
Dang, Brandon Black beat me to it while I was working on typing things up...
This may help with code readability:
namespace System
{
[Flags]
internal enum UriSyntaxFlags
{
AllowAnInternetHost = 0xe00,
AllowAnyOtherHost = 0x1000,
AllowDnsHost = 0x200,
AllowDOSPath = 0x100000,
AllowEmptyHost = 0x80,
AllowIdn = 0x4000000,
AllowIPv4Host = 0x400,
AllowIPv6Host = 0x800,
AllowIriParsing = 0x10000000,
AllowUncHost = 0x100,
BuiltInSyntax = 0x40000,
CanonicalizeAsFilePath = 0x1000000,
CompressPath = 0x800000,
ConvertPathSlashes = 0x400000,
FileLikeUri = 0x2000,
MailToLikeUri = 0x4000,
MayHaveFragment = 0x40,
MayHavePath = 0x10,
MayHavePort = 8,
MayHaveQuery = 0x20,
MayHaveUserInfo = 4,
MustHaveAuthority = 1,
OptionalAuthority = 2,
ParserSchemeOnly = 0x80000,
PathIsRooted = 0x200000,
SimpleUserSyntax = 0x20000,
UnEscapeDotsAndSlashes = 0x2000000,
V1_UnknownUri = 0x10000
}
}

You should be able to precent escape the '.' using '%2E', but that's the cheap and dirty way out.
You might try playing around with the dontEscape option a bit and it may change how Uri is treating those characters.
More info here:
http://msdn.microsoft.com/en-us/library/system.uri.aspx
Also check out the following (see DontUnescapePathDotsAndSlashes):
http:// msdn.microsoft.com/en-us/library/system.genericuriparseroptions.aspx

Does this work?
public class MyUriParser : UriParser
{
private string actualScheme;
public MyUriParser(string actualScheme)
{
Type type = this.GetType();
FieldInfo fInfo = type.BaseType.GetField("m_Flags", BindingFlags.Instance | BindingFlags.NonPublic);
fInfo.SetValue(this, GenericUriParserOptions.DontCompressPath);
this.actualScheme = actualScheme.ToLowerInvariant();
}
protected override string GetComponents(Uri uri, UriComponents components, UriFormat format)
{
string result = base.GetComponents(uri, components, format);
// Substitute our actual desired scheme in the string if it's in there.
if ((components & UriComponents.Scheme) != 0)
{
string registeredScheme = base.GetComponents(uri, UriComponents.Scheme, format);
result = this.actualScheme + result.Substring(registeredScheme.Length);
}
return result;
}}

Related

MVC Cache VaryByHeader, need to remove cache by header also

I have a MVC site that I am developing that is multi-tenant application. I have set up the cache to varybyheader="host". Now I'd like to invalidate the cache only by hostname.
The RemoveOutputCacheItem only takes absolute virtual paths and isn't allowing a custom host name (there by being a non-virtual path).
Any help on how to achieve this?
Thanks.
Update
Here is how I can get the internal cache keys
var runtimeType = typeof(HttpRuntime);
var ci = runtimeType.GetProperty(
"CacheInternal",
BindingFlags.NonPublic | BindingFlags.Static);
var cache = ci.GetValue(ci, new object[0]);
var cachesInfo = cache.GetType().GetField(
"_caches",
BindingFlags.NonPublic | BindingFlags.Instance);
var cacheEntries = cachesInfo.GetValue(cache);
var outputCacheEntries = new List<object>();
foreach (Object singleCache in cacheEntries as Array)
{
var singleCacheInfo =
singleCache.GetType().GetField("_entries",
BindingFlags.NonPublic | BindingFlags.Instance);
var entries = singleCacheInfo.GetValue(singleCache);
foreach (DictionaryEntry cacheEntry in entries as Hashtable)
{
var cacheEntryInfo = cacheEntry.Value.GetType().GetField("_value",
BindingFlags.NonPublic | BindingFlags.Instance);
var value = cacheEntryInfo.GetValue(cacheEntry.Value);
if (value.GetType().Name == "CachedRawResponse")
{
var key = (string)cacheEntry.Value.GetType().BaseType.GetField("_key", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(cacheEntry.Value);
key = key.Substring(key.IndexOf("/"));
outputCacheEntries.Add(key);
}
}
}
var keys = new StringBuilder();
foreach (string key in outputCacheEntries)
{
if (key.Contains(Request.Url.Host))
{
keys.Append(key + " ");
HttpResponse.RemoveOutputCacheItem(key);
}
}
That RemoveOutputCacheItem doesn't work with this key.
They key is generated like this: /HQNnoneV+n+FCNhostVHOSTNAME.comDE
Even a direct call RemoveOutputCache("/HOSTNAME.com") doesn't work either (with vary by custom).
Update #2
So read through the reference source code (http://referencesource.microsoft.com/#System.Web/HttpResponse.cs,3222f830c91ccb06) and it appears that it should attempt to create the custom key. So I should be able to RemoveOutputCache("/") and it should create the custom key for me, but this also appears to not be working as expected, it still appears to clear all keys.
You can try the VaryByCustom
[OutputCache(Duration = 3600, VaryByCustom = "hostname")]
And define the VaryByCustom like:
public override string GetVaryByCustomString(HttpContext Context, string Custom)
{
//Here you set any cache invalidation policy
if (Custom == "hostname")
{
return Context.Request.Url.Host;
}
return base.GetVaryByCustomString(Context, Custom);
}
The answer is that this cannot be done in this manner. Digging into the actual code for RemoveOutputCache where it generates the key doesn't pass in the varyby parameter and therefore I am unable to get the specific key needed.
Perhaps if I dug a bit more into the key generation, perhaps I could write a key gen method, but I've gone a different route. This is just to give everyone else an answer to my issue.
Again I only wanted to vary by header, no params and be able to invalidate just that one cached item.
I'm a little late to the party, but I had the exact same need, and ended up being inspired by the answers in this SO question: Clearing Page Cache in ASP.NET
I setup the following below test, and was able to successfully clear the cache per host with no additional code...
[OutputCache(VaryByHeader = "host", Duration = 600, Location = OutputCacheLocation.Server)]
public ActionResult TestPage()
{
var key = GetCacheKey("TestPage");
HttpContext.Cache[key] = new object();
Response.AddCacheItemDependency(key);
return Content(DateTime.Now.ToString());
}
public ActionResult TestClearCache()
{
var key = GetCacheKey("TestPage");
HttpContext.Cache.Remove(key);
return Content("Cache cleared");
}
private string GetCacheKey(string page)
{
return string.Concat(page, Request.Url.Host.ToLower());
}

Getting a Uri with escaped slashes on mono

Update: A fix has now made it's way into mono. This is good news!
Updated: Added logic to fix fragment handling.
I am trying to send a request with an encoded slash on Mono using the Uri class. This is basically the Mono equivalent of this question: GETting a URL with an url-encoded slash
The issue is that Mono similar to .NET will unescape any slashes it finds in the Uri when it is constructed. This logic was originally put in in place in order to remove vulnerabilities that could occur if paths were escape encoded and not detected.
In the previous post there is a hack which shows setting flags on the underlying Uri class via reflection which force the escaped slashes to be left alone. This behavior has been fixed in .NET 4.5 and by default the escaped slashes are allowed (as I mentioned in the comments).
I tried to do the same on Mono, but it fails as the internals of the Uri class are different. I came up with this approach to achieve what I want, which works but it is TERRIBLY hacky.
class Program
{
static void Main(string[] args)
{
var uri = new Uri("http://www.yahoo.com/%2F?Foo=Bar%2F#frag");
UriHelper.ForceCanonicalPathAndQuery(uri);
Console.WriteLine ("uri.ToString() - " + uri.ToString ());
Console.WriteLine ("uri.AbsoluteUri - " + uri.AbsoluteUri);
Console.WriteLine ("uri.Host - " + uri.Host);
Console.WriteLine ("uri.Query - " + uri.Query);
Console.WriteLine ("uri.PathAndQuery - " + uri.PathAndQuery);
Console.WriteLine ("uri.AbsolutePath - " + uri.AbsolutePath);
Console.WriteLine ("uri.Fragment - " + uri.Fragment);
}
public class UriHelper {
private static Type uriType = typeof(Uri);
private static FieldInfo sourceField;
private static FieldInfo queryField;
private static FieldInfo pathField;
private static FieldInfo cachedToStringField;
private static FieldInfo cachedAbsoluteUriField;
static UriHelper ()
{
sourceField = uriType.GetField ("source", BindingFlags.NonPublic | BindingFlags.Instance);
queryField = uriType.GetField ("query", BindingFlags.NonPublic | BindingFlags.Instance);
pathField = uriType.GetField ("path", BindingFlags.NonPublic | BindingFlags.Instance);
cachedToStringField = uriType.GetField ("cachedToString", BindingFlags.NonPublic | BindingFlags.Instance);
cachedAbsoluteUriField = uriType.GetField ("cachedAbsoluteUri", BindingFlags.NonPublic | BindingFlags.Instance);
}
public static void ForceCanonicalPathAndQuery(Uri uri)
{
var source = (string) sourceField.GetValue (uri);
cachedToStringField.SetValue (uri, source);
cachedAbsoluteUriField.SetValue (uri, source);
var fragPos = source.IndexOf ("#");
var queryPos = source.IndexOf ("?");
var start = source.IndexOf (uri.Host) + uri.Host.Length;
var pathEnd = queryPos == -1 ? fragPos : queryPos;
if (pathEnd == -1)
pathEnd = source.Length+1;
var path = queryPos > -1 ? source.Substring (start, pathEnd - start) : source.Substring (start);
pathField.SetValue (uri, path);
queryField.SetValue(uri, fragPos > -1 ? source.Substring(queryPos, fragPos - queryPos) : source.Substring(queryPos));
}
}
}
When you run this, it outputs the following:
uri.ToString() - http://www.yahoo.com/%2F?Foo=Bar%2F#frag
uri.AbsoluteUri - http://www.yahoo.com/%2F?Foo=Bar%2F#frag
uri.Host - www.yahoo.com
uri.Query - ?Foo=Bar%2F
uri.PathAndQuery - /%2F?Foo=Bar%2F
uri.AbsolutePath - /%2F
uri.Fragment - #frag
I don't at all feel good about it, but it does work, at least for the basic scenario of taking a Uri and issuing a query.
I might be missing something in the Uri class, so if you have a better / less hacky way to do what I am doing here, I'd really appreciate it.
From the original question, it looks like the behaviour of MS.NET changed in .NET 4.5 to fix the bug.
Indeed, then, it is a bug in mono for not following the behaviour change in the .NET 4.5 profile. And it seems someone already fixed the bug and proposed a pull request, the problem is that nobody in the Mono team seems to have found the time to review it: https://github.com/mono/mono/pull/619

Can anyone explain the major features of a VDPROJ file?

I'm sure there must be some documentation on MSDN somewhere, but I couldn't find it. It looks like some subset/variation of JSON. Really, this question grew out of something that has always bugged me: what do all the 8:s and 3:s mean? Is this some a version number of some kind? Maybe a typing scheme? Every VDPROJ excerpt I've ever seen is filled with these "eight-colon" and "three-colon" prefixes, but this is not the sort of question search engines are really good for.
"DeployProject"
{
"VSVersion" = "3:800"
"ProjectType" = "8:{978C614F-708E-4E1A-B201-565925725DBA}"
"IsWebType" = "8:FALSE"
"ProjectName" = "8:ProjectNameRedacted"
"LanguageId" = "3:1033"
"CodePage" = "3:1252"
"UILanguageId" = "3:1033"
"SccProjectName" = "8:"
"SccLocalPath" = "8:"
"SccAuxPath" = "8:"
"SccProvider" = "8:"
"Hierarchy"
{
"Entry"
{
"MsmKey" = "8:_02F97BB7BD104F1AAA1C97C854D5DC99"
"OwnerKey" = "8:_UNDEFINED"
"MsmSig" = "8:_UNDEFINED"
}
...
If anyone just wants to berate my pitiful Google-fu, that's fine too.
As #R. Matveev pointed out, the prefix numbers likely indicate the type of data stored in the property. This would be useful when deserializing the file into an object structure.
I doubt the source code which Visual Studio used to read/write the files was ever made open source, so it's no wonder that web searches returned nothing.
The best I could find was this page on OLE Automation data types, which may not have been the actual constants, but the data types seem to match the values in the *.vdproj file.
2.2.7 VARIANT Type Constants
typedef enum tagVARENUM
{
VT_EMPTY = 0x0000,
VT_NULL = 0x0001,
VT_I2 = 0x0002,
VT_I4 = 0x0003, // 4-byte signed integer
VT_R4 = 0x0004,
VT_R8 = 0x0005,
VT_CY = 0x0006,
VT_DATE = 0x0007,
VT_BSTR = 0x0008, // BSTR (string data)
VT_DISPATCH = 0x0009,
VT_ERROR = 0x000A,
VT_BOOL = 0x000B, // Boolean value
VT_VARIANT = 0x000C,
VT_UNKNOWN = 0x000D
...
} VARENUM;

Gstreamer: Link a bin with two sinks to playbin2

I want to read in an SDP file and encode the video stream into h.264 and the audio stream into aac. Then I want to multiplex those streams into an avi stream and then to a file. I don't know the contents of the SDP file ahead of time, so it seems easiest to use playbin2.
So, I thought I could do something like this:
RawToAviMux Bin
______________________________________
----- |ghostpad----x264enc
/ | \
playbin2------ | avimux--filesink
\ | /
-------| ghostpad----ffenc_aac
|_______________________________________
setting playbin2's videosink to an instance of RawToAviMux
and playbin2's audiosink to the same instance of RawToAviMux
However, I cannot get the pipeline into the playing state.
Here is the code:
recorder = new Gst.BasePlugins.PlayBin2();
recorder.PlayFlags &= ~((Gst.BasePlugins.PlayBin2.PlayFlagsType)(1 << 2));
recorder.Bus.AddSignalWatch();
recorder.Bus.EnableSyncMessageEmission();
RawToAviMuxer aviMuxer = new RawToAviMuxer(fileName);
recorder.VideoSink = aviMuxer;
recorder.AudioSink = aviMuxer;
recorder.SetState(Gst.State.Ready);
recorder.Uri = #"file:///" + filePath.Replace('\\', '/');
recorder.SetState(Gst.State.Paused);
recorder.SetState(Gst.State.Playing);
Gst.State currentState;
Gst.State playingState = Gst.State.Playing;
Gst.StateChangeReturn stateReturn = recorder.GetState(out currentState, out playingState, Gst.Clock.Second);
if (stateReturn != Gst.StateChangeReturn.Failure)
return true;
return false;
With RawToAviMuxer as
public class RawToAviMuxer : Gst.Bin
{
bool test = false;
public RawToAviMuxer(string outputFileName)
: base("rawToAviMux")
{
Gst.Element x264Enc = Gst.ElementFactory.Make("x264enc");
Gst.Element ffenc_aac = Gst.ElementFactory.Make("ffenc_aac");
x264Enc["bframes"] = (uint)0;
x264Enc["b-adapt"] = false;
x264Enc["bitrate"] = (uint)1024;
x264Enc["tune"] = 0x4;
x264Enc["speed-preset"] = 3;
x264Enc["sliced-threads"] = false;
x264Enc["profile"] = 0;
x264Enc["key-int-max"] = (uint)30;
Gst.GhostPad videoToX264Pad = new Gst.GhostPad("video_sink", x264Enc.GetStaticPad("sink"));
Gst.GhostPad audioToAACPad = new Gst.GhostPad("audio_sink", ffenc_aac.GetStaticPad("sink"));
test = this.AddPad(videoToX264Pad);
test = this.AddPad(audioToAACPad);
Gst.Element aviMux = Gst.ElementFactory.Make("avimux");
Gst.Element fileSink = Gst.ElementFactory.Make("filesink");
test = this.Add(new Gst.Element[]{x264Enc, ffenc_aac, aviMux, fileSink});
test = x264Enc.Link(aviMux);
test = ffenc_aac.Link(aviMux);
test = aviMux.Link(fileSink);
fileSink["location"] = outputFileName;
}
}
I have stepped through in the debugger and all of the links are successful.
update
Ok, so I tried the following pipeline with Gst.Parse.Launch:
uridecodebin uri=file:///C:/Users/Jonathan/AppData/Local/Temp/192.168.0.215_5000.sdp !
x264enc byte-stream=true bframes=0 b-adapt=0 tune=0x4 speed-preset=3 sliced-threads=false
profile=0 ! mux. ffenc_aac ! mux. avimux name=mux ! filesink location=C:\Users\Jonathan\Desktop\test.avi
I still can't get it out of paused.
I am using the windows build, so I am worried maybe there is something wrong with that?
I also can't attach a message handler to the bus so that I can figure out what is going on, which is really starting to get annoying.
I did just find this however,
If I directly grab the streams via an udpsrc element, knowing what the formats are ahead of time, it does not work with just an rtph264depay element. There must be an h264parse element in the pipeline. This may be the reason that uridecodebin isn't working for me?
solved
I ended up doing the following:
if (!gst_is_init)
{
Gst.Application.Init();
gst_is_init = true;
}
if(recorder != null)
{
recorder.SetState(Gst.State.Null);
recorder.Dispose();
}
string videoDepay, audioDepay, strExtension, strMuxer;
GetGstElements(stream.VideoCaps, out videoDepay, out strMuxer, out strExtension);
GetGstElements(stream.AudioCaps, out audioDepay, out strMuxer, out strExtension);
fileName = Path.ChangeExtension(fileName, strExtension);
//recorder = new Gst.Pipeline("recordingPipe");
string pipeString = String.Format("udpsrc port={0} ! {1} {2} ! {3} ! queue ! mux. udpsrc port={4} ! {1} {5} ! {6} ! mux. {7} name=mux ! filesink location={8}",
portToUse, "application/x-rtp,", stream.VideoCaps, videoDepay, (portToUse + 2), stream.AudioCaps, audioDepay, strMuxer, fileName.Replace("\\", "\\\\"));
recorder = (Gst.Pipeline)Gst.Parse.Launch(pipeString);
recordingFileName = fileName;
recorder.SetState(Gst.State.Ready);
recorder.SetState(Gst.State.Paused);
recorder.SetState(Gst.State.Playing);
Gst.State currentState;
Gst.StateChangeReturn stateReturn = recorder.GetState(out currentState, Gst.Clock.Second);
if (stateReturn != Gst.StateChangeReturn.Failure)
return true;
return false;
You have to have a parser for all streams, for the pipeline to go to playing. So, in the case of an incomming h264 stream, I would need to use rtph264depay ! h264parse. In addition the NALU and byte-stream must match for the timing to be right.
Also, in order for the file to be usable, you have to send an EOS downstream before disposing of the pipeline.
recorder.SendEvent(Gst.Event.NewEos());
System.Threading.Thread.Sleep(100);
recorder.SetState(Gst.State.Paused);
recorder.SetState(Gst.State.Ready);
recorder.SetState(Gst.State.Null);
recorder.Dispose();
Yes, this won't work. Here is how you can do it. Playbin2 is a modular component, it consists of an uridecodebin and a playsinkbin. You can just use and uridecodebin, set your media file uri, add signal handlers for pad-added and connect the newly created pads to the sink-pads of your rawtoavimux component.
One alternative for rawtoavimux would be to use encodebin. Using uridecodebin ! encodebin can potentially to smart transcoding which would avoid the decoding and re-encoding if the format of one or more stream is already in the correct format.

Getting the parent name of a URI/URL from absolute name C#

Given an absolute URI/URL, I want to get a URI/URL which doesn't contain the leaf portion. For example: given http://foo.com/bar/baz.html, I should get http://foo.com/bar/.
The code which I could come up with seems a bit lengthy, so I'm wondering if there is a better way.
static string GetParentUriString(Uri uri)
{
StringBuilder parentName = new StringBuilder();
// Append the scheme: http, ftp etc.
parentName.Append(uri.Scheme);
// Appned the '://' after the http, ftp etc.
parentName.Append("://");
// Append the host name www.foo.com
parentName.Append(uri.Host);
// Append each segment except the last one. The last one is the
// leaf and we will ignore it.
for (int i = 0; i < uri.Segments.Length - 1; i++)
{
parentName.Append(uri.Segments[i]);
}
return parentName.ToString();
}
One would use the function something like this:
static void Main(string[] args)
{
Uri uri = new Uri("http://foo.com/bar/baz.html");
// Should return http://foo.com/bar/
string parentName = GetParentUriString(uri);
}
Thanks,
Rohit
Did you try this? Seems simple enough.
Uri parent = new Uri(uri, "..");
This is the shortest I can come up with:
static string GetParentUriString(Uri uri)
{
return uri.AbsoluteUri.Remove(uri.AbsoluteUri.Length - uri.Segments.Last().Length);
}
If you want to use the Last() method, you will have to include System.Linq.
There must be an easier way to do this with the built in uri methods but here is my twist on #unknown (yahoo)'s suggestion.
In this version you don't need System.Linq and it also handles URIs with query strings:
private static string GetParentUriString(Uri uri)
{
return uri.AbsoluteUri.Remove(uri.AbsoluteUri.Length - uri.Segments[uri.Segments.Length -1].Length - uri.Query.Length);
}
Quick and dirty
int pos = uriString.LastIndexOf('/');
if (pos > 0) { uriString = uriString.Substring(0, pos); }
Shortest way I found:
static Uri GetParent(Uri uri) {
return new Uri(uri, Path.GetDirectoryName(uri.LocalPath) + "/");
}
PapyRef's answer is incorrect, UriPartial.Path includes the filename.
new Uri(uri, ".").ToString()
seems to be cleanest/simplest implementation of the function requested.
I read many answers here but didn't find one that I liked because they break in some cases.
So, I am using this:
public Uri GetParentUri(Uri uri) {
var withoutQuery = new Uri(uri.GetComponents(UriComponents.Scheme |
UriComponents.UserInfo |
UriComponents.Host |
UriComponents.Port |
UriComponents.Path, UriFormat.UriEscaped));
var trimmed = new Uri(withoutQuery.AbsoluteUri.TrimEnd('/'));
var result = new Uri(trimmed, ".");
return result;
}
Note: It removes the Query and the Fragment intentionally.
new Uri(uri.AbsoluteUri + "/../")
Get segmenation of url
url="http://localhost:9572/School/Common/Admin/Default.aspx"
Dim name() As String = HttpContext.Current.Request.Url.Segments
now simply using for loop or by index, get parent directory name
code = name(2).Remove(name(2).IndexOf("/"))
This returns me, "Common"
Thought I'd chime in; despite it being almost 10 years, with the advent of the cloud, getting the parent Uri is a fairly common (and IMO more valuable) scenario, so combining some of the answers here you would simply use (extended) Uri semantics:
public static Uri Parent(this Uri uri)
{
return new Uri(uri.AbsoluteUri.Remove(uri.AbsoluteUri.Length - uri.Segments.Last().Length - uri.Query.Length).TrimEnd('/'));
}
var source = new Uri("https://foo.azure.com/bar/source/baz.html?q=1");
var parent = source.Parent(); // https://foo.azure.com/bar/source
var folder = parent.Segments.Last(); // source
I can't say I've tested every scenario, so caution advised.

Categories