/// <inheritdoc /> public string CreateUrl(ApiOperationLink link, object result = null) { // This duplicates the checks in CreateRelativeUrlFromLink for the purpose of not creating a new instance // of StringBuilder unnecessarily if (result == null) { return(this._baseUri + link.UrlFormat); } // We can short-circuit in the (relatively uncommon case) of no placeholders if (!link.HasPlaceholders()) { return(this._baseUri + link.UrlFormat); } var relativeUrl = CreateRelativeUrlFromLink(link, result); // We cannot create a full URL if the relative link is null if (relativeUrl == null) { return(null); } // baseUri always has / at end, relative never has at start return(this._baseUri + relativeUrl); }
/// <inheritdoc /> public string CreateUrl(ApiOperationLink link, object result = null) { // We can short-circuit in the (relatively uncommon case) of no placeholders if (!link.HasPlaceholders()) { return(this._baseUri + link.UrlFormat); } var relativeUrl = link.CreateRelativeUrl(result); // We cannot create a full URL if the relative link is null if (relativeUrl == null) { return(null); } // baseUri always has / at end, relative never has at start return(this._baseUri + relativeUrl); }
private static StringBuilder CreateRelativeUrlFromLink(ApiOperationLink link, object result) { if (result == null) { return(new StringBuilder(link.UrlFormat)); } // We can short-circuit in the (relatively uncommon case) of no placeholders if (!link.HasPlaceholders()) { return(new StringBuilder(link.UrlFormat)); } var builtUrl = new StringBuilder(); var currentIndex = 0; foreach (var placeholder in link.Placeholders) { // Grab the static bit of the URL _before_ this placeholder. builtUrl.Append(link.UrlFormat.Substring(currentIndex, placeholder.Index - currentIndex)); // Now skip over the actual placeholder for the next iteration currentIndex = placeholder.Index + placeholder.Length; object placeholderValue; if (link.OperationDescriptor.OperationType == result.GetType()) { // Do not have to deal with "alternate" names, we know the original name is correct placeholderValue = placeholder.Property.GetValue(result); } else { // We cannot use the existing PropertyInfo on placeholder because the type is different, even though they are the same name var property = result .GetType() .GetProperty(placeholder.AlternatePropertyName ?? placeholder.Property.Name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public); if (property == null) { if (placeholder.AlternatePropertyName != null) { throw new InvalidOperationException( $"Cannot find property '{placeholder.AlternatePropertyName}' (specified as alternate name) on type '{result.GetType()}'"); } throw new InvalidOperationException( $"Cannot find property '{placeholder.Property.Name}' on type '{result.GetType()}'"); } placeholderValue = property.GetValue(result); } // If we have a placeholder value then we must return null if it does not exist, otherwise we would build URLs // like /users/null if using a "safe" representation if (placeholderValue == null) { return(null); } if (placeholder.Format != null) { builtUrl.Append(Uri.EscapeDataString(string.Format(placeholder.FormatSpecifier, placeholderValue))); } else { // We do not have a format so just ToString the result. We pick a few common types to cast directly to avoid indirect // call to ToString when doing it as (object).ToString() switch (placeholderValue) { case string s: builtUrl.Append(Uri.EscapeDataString(s)); break; case Guid g: builtUrl.Append(Uri.EscapeDataString(g.ToString())); break; case int i: builtUrl.Append(Uri.EscapeDataString(i.ToString())); break; case long l: builtUrl.Append(Uri.EscapeDataString(l.ToString())); break; default: builtUrl.Append(Uri.EscapeDataString(placeholderValue.ToString())); break; } } } if (currentIndex < link.UrlFormat.Length) { builtUrl.Append(link.UrlFormat.Substring(currentIndex)); } return(builtUrl); }