From 02aa1c50fffa386cb94112e353e9b0eb6e6a3d80 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Wed, 29 Oct 2014 14:28:40 -0700 Subject: [PATCH] #28 - Add Helper for building Uris. --- src/Microsoft.AspNet.Http/FragmentString.cs | 136 ++++++++++++++++++ src/Microsoft.AspNet.Http/HostString.cs | 5 + .../UriHelper.cs | 102 +++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 src/Microsoft.AspNet.Http/FragmentString.cs create mode 100644 src/Microsoft.AspNet.WebUtilities/UriHelper.cs diff --git a/src/Microsoft.AspNet.Http/FragmentString.cs b/src/Microsoft.AspNet.Http/FragmentString.cs new file mode 100644 index 0000000000..1d725e742c --- /dev/null +++ b/src/Microsoft.AspNet.Http/FragmentString.cs @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNet.Http +{ + /// + /// Provides correct handling for FragmentString value when needed to generate a URI string + /// + public struct FragmentString : IEquatable + { + /// + /// Represents the empty fragment string. This field is read-only. + /// + public static readonly FragmentString Empty = new FragmentString(string.Empty); + + private readonly string _value; + + /// + /// Initialize the fragment string with a given value. This value must be in escaped and delimited format with + /// a leading '#' character. + /// + /// The fragment string to be assigned to the Value property. + public FragmentString(string value) + { + if (!string.IsNullOrEmpty(value) && value[0] != '#') + { + throw new ArgumentException("The leading '#' must be included for a non-empty fragment.", "value"); + } + _value = value; + } + + /// + /// The escaped fragment string with the leading '#' character + /// + public string Value + { + get { return _value; } + } + + /// + /// True if the fragment string is not empty + /// + public bool HasValue + { + get { return !string.IsNullOrEmpty(_value); } + } + + /// + /// Provides the fragment string escaped in a way which is correct for combining into the URI representation. + /// A leading '#' character will be included unless the Value is null or empty. Characters which are potentially + /// dangerous are escaped. + /// + /// The fragment string value + public override string ToString() + { + return ToUriComponent(); + } + + /// + /// Provides the fragment string escaped in a way which is correct for combining into the URI representation. + /// A leading '#' character will be included unless the Value is null or empty. Characters which are potentially + /// dangerous are escaped. + /// + /// The fragment string value + public string ToUriComponent() + { + // Escape things properly so System.Uri doesn't mis-interpret the data. + return HasValue ? _value : string.Empty; + } + + /// + /// Returns an FragmentString given the fragment as it is escaped in the URI format. The string MUST NOT contain any + /// value that is not a fragment. + /// + /// The escaped fragment as it appears in the URI format. + /// The resulting FragmentString + public static FragmentString FromUriComponent(string uriComponent) + { + if (String.IsNullOrEmpty(uriComponent)) + { + return Empty; + } + return new FragmentString(uriComponent); + } + + /// + /// Returns an FragmentString given the fragment as from a Uri object. Relative Uri objects are not supported. + /// + /// The Uri object + /// The resulting FragmentString + public static FragmentString FromUriComponent(Uri uri) + { + if (uri == null) + { + throw new ArgumentNullException("uri"); + } + string fragmentValue = uri.GetComponents(UriComponents.Fragment, UriFormat.UriEscaped); + if (!string.IsNullOrEmpty(fragmentValue)) + { + fragmentValue = "#" + fragmentValue; + } + return new FragmentString(fragmentValue); + } + + public bool Equals(FragmentString other) + { + return string.Equals(_value, other._value); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + return obj is FragmentString && Equals((FragmentString)obj); + } + + public override int GetHashCode() + { + return (_value != null ? _value.GetHashCode() : 0); + } + + public static bool operator ==(FragmentString left, FragmentString right) + { + return left.Equals(right); + } + + public static bool operator !=(FragmentString left, FragmentString right) + { + return !left.Equals(right); + } + } +} diff --git a/src/Microsoft.AspNet.Http/HostString.cs b/src/Microsoft.AspNet.Http/HostString.cs index 67af893b19..ae72f819cc 100644 --- a/src/Microsoft.AspNet.Http/HostString.cs +++ b/src/Microsoft.AspNet.Http/HostString.cs @@ -33,6 +33,11 @@ namespace Microsoft.AspNet.Http get { return _value; } } + public bool HasValue + { + get { return !string.IsNullOrEmpty(_value); } + } + /// /// Returns the value as normalized by ToUriComponent(). /// diff --git a/src/Microsoft.AspNet.WebUtilities/UriHelper.cs b/src/Microsoft.AspNet.WebUtilities/UriHelper.cs new file mode 100644 index 0000000000..491c599d20 --- /dev/null +++ b/src/Microsoft.AspNet.WebUtilities/UriHelper.cs @@ -0,0 +1,102 @@ +using System; +using Microsoft.AspNet.Http; + +namespace Microsoft.AspNet.WebUtilities +{ + /// + /// A helper class for constructing encoded Uris for use in headers and other Uris. + /// + public class UriHelper + { + public UriHelper() + { + } + + public UriHelper(HttpRequest request) + { + Scheme = request.Scheme; + Host = request.Host; + PathBase = request.PathBase; + Path = request.Path; + Query = request.QueryString; + // Fragment is not a valid request field. + } + + public UriHelper(Uri uri) + { + Scheme = uri.Scheme; + Host = HostString.FromUriComponent(uri); + // Assume nothing is being put in PathBase + Path = PathString.FromUriComponent(uri); + Query = QueryString.FromUriComponent(uri); + Fragment = FragmentString.FromUriComponent(uri); + } + + public string Scheme { get; set; } + + public HostString Host { get; set; } + + public PathString PathBase { get; set; } + + public PathString Path { get; set; } + + public QueryString Query { get; set; } + + public FragmentString Fragment { get; set; } + + // Always returns at least '/' + public string GetPartialUri() + { + string path = (PathBase.HasValue || Path.HasValue) ? (PathBase + Path).ToString() : "/"; + return path + Query + Fragment; + } + + // Always returns at least 'scheme://host/' + public string GetFullUri() + { + if (string.IsNullOrEmpty(Scheme)) + { + throw new InvalidOperationException("Missing Scheme"); + } + if (!Host.HasValue) + { + throw new InvalidOperationException("Missing Host"); + } + + string path = (PathBase.HasValue || Path.HasValue) ? (PathBase + Path).ToString() : "/"; + return Scheme + "://" + Host + path + Query + Fragment; + } + + public static string Create(PathString pathBase, + PathString path = new PathString(), + QueryString query = new QueryString(), + FragmentString fragment = new FragmentString()) + { + return new UriHelper() + { + PathBase = pathBase, + Path = path, + Query = query, + Fragment = fragment + }.GetPartialUri(); + } + + public static string Create(string scheme, + HostString host, + PathString pathBase = new PathString(), + PathString path = new PathString(), + QueryString query = new QueryString(), + FragmentString fragment = new FragmentString()) + { + return new UriHelper() + { + Scheme = scheme, + Host = host, + PathBase = pathBase, + Path = path, + Query = query, + Fragment = fragment + }.GetFullUri(); + } + } +} \ No newline at end of file