/WCFWebApi/src/Microsoft.ApplicationServer.Http/Microsoft/ApplicationServer/Http/Dispatcher/HelpPage.cs
C# | 1645 lines | 1483 code | 138 blank | 24 comment | 241 complexity | 9f683ae33460c35c409f396d04142923 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, Apache-2.0
Large files files are truncated, but you can click here to view the full file
- // <copyright>
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
-
- namespace Microsoft.ApplicationServer.Http.Dispatcher
- {
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Net.Http;
- using System.Net.Http.Formatting;
- using System.Runtime.Serialization;
- using System.Runtime.Serialization.Json;
- using System.ServiceModel.Channels;
- using System.ServiceModel.Description;
- using System.ServiceModel.Dispatcher;
- using System.ServiceModel.Syndication;
- using System.ServiceModel.Web;
- using System.Text;
- using System.Web;
- using System.Xml;
- using System.Xml.Linq;
- using System.Xml.Schema;
- using System.Xml.Serialization;
- using Microsoft.Server.Common;
- using Microsoft.ApplicationServer.Http.Description;
- using Microsoft.Runtime.Serialization;
- using Microsoft.Runtime.Serialization.Json;
-
- internal class HelpPage
- {
- public const string OperationListHelpPageUriTemplate = "help";
- public const string OperationHelpPageUriTemplateBase = "help/operations/";
- public const string OperationHelpPageUriTemplate = "help/operations/{operation}";
- public const string HelpMethodName = "GetHelpPage";
- public const string HelpOperationMethodName = "GetOperationHelpPage";
-
- private static readonly Dictionary<string, string> ConstantValues = new Dictionary<string, string>()
- {
- { "anyURI", "http://www.example.com/" },
- { "base64Binary", "QmFzZSA2NCBTdHJlYW0=" },
- { "boolean", "true" },
- { "byte", "127" },
- { "date", "1999-05-31" },
- { "decimal", "12678967.543233" },
- { "double", "1.26743233E+15" },
- { "duration", "P428DT10H30M12.3S" },
- { "ENTITY", "NCNameString" },
- { "float", "1.26743237E+15" },
- { "gDay", "---31" },
- { "gMonth", "--05" },
- { "gMonthDay", "--05-31" },
- { "guid", "1627aea5-8e0a-4371-9022-9b504344e724" },
- { "gYear", "1999" },
- { "gYearMonth", "1999-02" },
- { "hexBinary", "GpM7" },
- { "ID", "NCNameString" },
- { "IDREF", "NCNameString" },
- { "IDREFS", "NCNameString" },
- { "int", "2147483647" },
- { "integer", "2147483647" },
- { "language", "Http.SR.HelpExampleGeneratorLanguage" },
- { "long", "9223372036854775807" },
- { "Name", "Name" },
- { "NCName", "NCNameString" },
- { "negativeInteger", "-12678967543233" },
- { "NMTOKEN", Http.SR.HelpExampleGeneratorStringContent },
- { "NMTOKENS", Http.SR.HelpExampleGeneratorStringContent },
- { "nonNegativeInteger", "+2147483647" },
- { "nonPositiveInteger", "-12678967543233" },
- { "normalizedString", Http.SR.HelpExampleGeneratorStringContent },
- { "NOTATION", "namespace:Name" },
- { "positiveInteger", "+2147483647" },
- { "QName", "namespace:Name" },
- { "short", "32767" },
- { "string", Http.SR.HelpExampleGeneratorStringContent },
- { "time", "13:20:00.000, 13:20:00.000-05:00" },
- { "token", Http.SR.HelpExampleGeneratorStringContent },
- { "unsignedByte", "255" },
- { "unsignedInt", "4294967295" },
- { "unsignedLong", "18446744073709551615" },
- { "unsignedShort", "65535" },
- };
-
- private static readonly Encoding utf8Encoding = new UTF8Encoding(true);
- private DateTime startupTime = DateTime.UtcNow;
- private Uri baseAddress;
- private Dictionary<string, OperationHelpInformation> operationInfoDictionary;
- private NameValueCache<string> operationPageCache;
- private NameValueCache<string> helpPageCache;
-
- public HelpPage(Uri baseAddress, string javascriptCallbackParameterName, ContractDescription description)
- {
- Fx.Assert(baseAddress != null, "The 'baseAddress' parameter should not be null.");
-
- this.baseAddress = baseAddress;
-
- this.operationInfoDictionary = new Dictionary<string, OperationHelpInformation>();
- this.operationPageCache = new NameValueCache<string>();
- this.helpPageCache = new NameValueCache<string>();
-
- foreach (OperationDescription od in description.Operations)
- {
- HttpOperationDescription httpOperationDescription = od.ToHttpOperationDescription();
- operationInfoDictionary.Add(
- httpOperationDescription.Name,
- new OperationHelpInformation(javascriptCallbackParameterName, WebMessageBodyStyle.Bare, httpOperationDescription));
- }
- }
-
- public static OperationDescription[] AddHelpOperations(ContractDescription contractDescription, DispatchRuntime dispatchRuntime)
- {
- Fx.Assert(contractDescription != null, "The 'contractDescription' parameter should not be null.");
- Fx.Assert(dispatchRuntime != null, "The 'dispatchRuntime' parameter should not be null.");
-
- Uri baseAddress = dispatchRuntime.EndpointDispatcher.EndpointAddress.Uri;
- HelpPage helpPage = new HelpPage(baseAddress, null, contractDescription);
-
- HttpOperationDescription helpPageOperation = new HttpOperationDescription(HelpPage.HelpMethodName, contractDescription);
- helpPageOperation.Behaviors.Add(new WebGetAttribute() { UriTemplate = HelpPage.OperationListHelpPageUriTemplate });
- helpPageOperation.InputParameters.Add(HttpParameter.RequestMessage);
- helpPageOperation.ReturnValue = HttpParameter.ResponseMessage;
-
- HttpOperationDescription operationhelpPageOperation = new HttpOperationDescription(HelpPage.HelpOperationMethodName, contractDescription);
- operationhelpPageOperation.Behaviors.Add(new WebGetAttribute() { UriTemplate = HelpPage.OperationHelpPageUriTemplate });
- operationhelpPageOperation.InputParameters.Add(HttpParameter.RequestMessage);
- operationhelpPageOperation.InputParameters.Add(new HttpParameter("operation", TypeHelper.StringType));
- operationhelpPageOperation.ReturnValue = HttpParameter.ResponseMessage;
-
- dispatchRuntime.Operations.Add(
- new DispatchOperation(dispatchRuntime, HelpPage.HelpMethodName, null, null) { Invoker = helpPage.GetHelpPageInvoker(true) });
- dispatchRuntime.Operations.Add(
- new DispatchOperation(dispatchRuntime, HelpPage.HelpOperationMethodName, null, null) { Invoker = helpPage.GetHelpPageInvoker(false) });
-
- return new OperationDescription[] { helpPageOperation.ToOperationDescription(), operationhelpPageOperation.ToOperationDescription() };
- }
-
- public static IEnumerable<KeyValuePair<UriTemplate, object>> GetOperationTemplatePairs()
- {
- return new KeyValuePair<UriTemplate, object>[]
- {
- new KeyValuePair<UriTemplate, object>(new UriTemplate(OperationListHelpPageUriTemplate), HelpMethodName),
- new KeyValuePair<UriTemplate, object>(new UriTemplate(OperationHelpPageUriTemplate), HelpOperationMethodName)
- };
- }
-
- public IOperationInvoker GetHelpPageInvoker(bool invokerForOperationListHelpPage)
- {
- return new HttpHelpOperationInvoker(this, invokerForOperationListHelpPage);
- }
-
- public void InvokeHelpPage(HttpResponseMessage response)
- {
- ApplyCaching();
-
- Uri baseUri = this.baseAddress.GetHostNormalizedUri(response.RequestMessage);
-
- string helpPage = this.helpPageCache.Lookup(baseUri.Authority);
-
- if (string.IsNullOrEmpty(helpPage))
- {
- helpPage = HelpHtmlPageBuilder.CreateHelpPage(baseUri, operationInfoDictionary.Values).ToString();
- if (HttpContext.Current == null)
- {
- this.helpPageCache.AddOrUpdate(baseUri.Authority, helpPage);
- }
- }
-
- response.Content = new ActionOfStreamContent(
- stream =>
- {
- byte[] preamble = utf8Encoding.GetPreamble();
- stream.Write(preamble, 0, preamble.Length);
- byte[] bytes = utf8Encoding.GetBytes(helpPage);
- stream.Write(bytes, 0, bytes.Length);
- });
-
- response.Content.Headers.ContentType = MediaTypeConstants.HtmlMediaType;
- }
-
- public void InvokeOperationHelpPage(string operationName, HttpResponseMessage response)
- {
- ApplyCaching();
-
- Uri requestUri = response.RequestMessage.RequestUri.GetHostNormalizedUri(response.RequestMessage);
- string helpPage = this.operationPageCache.Lookup(requestUri.AbsoluteUri);
-
- if (string.IsNullOrEmpty(helpPage))
- {
- OperationHelpInformation operationInfo;
- if (this.operationInfoDictionary.TryGetValue(operationName, out operationInfo))
- {
- Uri baseUri = this.baseAddress.GetHostNormalizedUri(response.RequestMessage);
- helpPage = HelpHtmlPageBuilder.CreateOperationHelpPage(baseUri, operationInfo).ToString();
- if (HttpContext.Current == null)
- {
- this.operationPageCache.AddOrUpdate(requestUri.AbsoluteUri, helpPage);
- }
- }
- else
- {
- throw Fx.Exception.AsError(new WebFaultException(HttpStatusCode.NotFound));
- }
- }
-
- response.Content = new ActionOfStreamContent(
- stream =>
- {
- byte[] preamble = utf8Encoding.GetPreamble();
- stream.Write(preamble, 0, preamble.Length);
- byte[] bytes = utf8Encoding.GetBytes(helpPage);
- stream.Write(bytes, 0, bytes.Length);
- });
- response.Content.Headers.ContentType = MediaTypeConstants.HtmlMediaType;
- }
-
- private void ApplyCaching()
- {
- if (HttpContext.Current != null)
- {
- HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Public);
- HttpContext.Current.Response.Cache.SetMaxAge(TimeSpan.MaxValue);
- HttpContext.Current.Response.Cache.AddValidationCallback(new HttpCacheValidateHandler(this.CacheValidationCallback), this.startupTime);
- HttpContext.Current.Response.Cache.SetValidUntilExpires(true);
- }
- }
-
- private void CacheValidationCallback(HttpContext context, object state, ref HttpValidationStatus result)
- {
- if (((DateTime)state) == this.startupTime)
- {
- result = HttpValidationStatus.Valid;
- }
- else
- {
- result = HttpValidationStatus.Invalid;
- }
- }
-
- private class NameValueCache<T>
- {
- // The NameValueCache implements a structure that uses a dictionary to map objects to
- // indices of an array of cache entries. This allows us to store the cache entries in
- // the order in which they were added to the cache, and yet still lookup any cache entry.
- // The eviction policy of the cache is to evict the least-recently-added cache entry.
- // Using a pointer to the next available cache entry in the array, we can always be sure
- // that the given entry is the oldest entry.
- private Hashtable cache;
- private string[] currentKeys;
- private int nextAvailableCacheIndex;
- private object cachelock;
- internal const int maxNumberofEntriesInCache = 16;
-
- public NameValueCache()
- : this(maxNumberofEntriesInCache)
- {
- }
-
- public NameValueCache(int maxCacheEntries)
- {
- cache = new Hashtable();
- currentKeys = new string[maxCacheEntries];
- cachelock = new object();
- }
-
- public T Lookup(string key)
- {
- return (T)cache[key];
- }
-
- public void AddOrUpdate(string key, T value)
- {
- lock (this.cachelock)
- {
- if (!cache.ContainsKey(key))
- {
- if (!String.IsNullOrEmpty(currentKeys[nextAvailableCacheIndex]))
- {
- cache.Remove(currentKeys[nextAvailableCacheIndex]);
- }
- currentKeys[nextAvailableCacheIndex] = key;
- nextAvailableCacheIndex = ++nextAvailableCacheIndex % currentKeys.Length;
- }
- cache[key] = value;
- }
- }
- }
-
- private class OperationHelpInformation
- {
- HttpOperationDescription httpOperationDescription;
- MessageHelpInformation request;
- MessageHelpInformation response;
- string javascriptCallbackParameterName;
- WebMessageBodyStyle bodyStyle;
-
- internal OperationHelpInformation(string javascriptCallbackParameterName, WebMessageBodyStyle bodyStyle, HttpOperationDescription httpOperationDescription)
- {
- this.httpOperationDescription = httpOperationDescription;
- this.javascriptCallbackParameterName = javascriptCallbackParameterName;
- this.bodyStyle = bodyStyle;
- }
-
- public string Name
- {
- get
- {
- return httpOperationDescription.Name;
- }
- }
-
- public string UriTemplate
- {
- get
- {
- return httpOperationDescription.GetUriTemplate().ToString();
- }
- }
-
- public string Method
- {
- get
- {
- return httpOperationDescription.GetHttpMethod().Method;
- }
- }
-
- public string Description
- {
- get
- {
- return httpOperationDescription.GetDescription();
- }
- }
-
- public string JavascriptCallbackParameterName
- {
- get
- {
- if (this.Response.SupportsJson && string.Equals(this.Method, HttpMethod.Get.ToString()))
- {
- return this.javascriptCallbackParameterName;
- }
- return null;
- }
- }
-
- public WebMessageBodyStyle BodyStyle
- {
- get
- {
- return this.bodyStyle;
- }
- }
-
- public MessageHelpInformation Request
- {
- get
- {
- if (this.request == null)
- {
- this.request = new MessageHelpInformation(httpOperationDescription, true, GetRequestBodyType(httpOperationDescription, this.UriTemplate),
- this.BodyStyle == WebMessageBodyStyle.WrappedRequest || this.BodyStyle == WebMessageBodyStyle.Wrapped);
- }
- return this.request;
- }
- }
-
- public MessageHelpInformation Response
- {
- get
- {
- if (this.response == null)
- {
- this.response = new MessageHelpInformation(httpOperationDescription, false, GetResponseBodyType(httpOperationDescription),
- this.BodyStyle == WebMessageBodyStyle.WrappedResponse || this.BodyStyle == WebMessageBodyStyle.Wrapped);
- }
- return this.response;
- }
- }
-
- public static Type GetResponseBodyType(HttpOperationDescription description)
- {
- if (description.OutputParameters.Count > 0)
- {
- // If it is more than 0 the response is wrapped and not supported
- return null;
- }
-
- HttpParameter returnDescription = description.ReturnValue;
-
- return returnDescription == null ? null : returnDescription.ParameterType;
- }
-
- public static Type GetRequestBodyType(HttpOperationDescription description, string uriTemplate)
- {
- if (description.Behaviors.Contains(typeof(WebGetAttribute)))
- {
- return typeof(void);
- }
-
- UriTemplate template = new UriTemplate(uriTemplate);
- IEnumerable<HttpParameter> parameterDescriptions =
- from parameterDescription in description.InputParameters
- where !template.PathSegmentVariableNames.Contains(parameterDescription.Name.ToUpperInvariant()) && !template.QueryValueVariableNames.Contains(parameterDescription.Name.ToUpperInvariant())
- select parameterDescription;
-
- HttpParameter[] matchingParameters = parameterDescriptions.ToArray();
-
- // No match == void
- // One match == the body type
- // Multiple matches == the request is wrapped and not supported
- return matchingParameters.Length == 0
- ? typeof(void)
- : (matchingParameters.Length == 1)
- ? matchingParameters[0].ParameterType
- : null;
- }
- }
-
- private class MessageHelpInformation
- {
- private static readonly Type typeOfIQueryable = typeof(IQueryable);
- private static readonly Type typeOfIQueryableGeneric = typeof(IQueryable<>);
- private static readonly Type typeOfIEnumerable = typeof(IEnumerable);
- private static readonly Type typeOfIEnumerableGeneric = typeof(IEnumerable<>);
-
- public string BodyDescription { get; private set; }
- public string FormatString { get; private set; }
- public Type Type { get; private set; }
- public bool SupportsJson { get; private set; }
- public XmlSchemaSet SchemaSet { get; private set; }
- public XmlSchema Schema { get; private set; }
- public XElement XmlExample { get; private set; }
- public XElement JsonExample { get; private set; }
-
- [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "This class requires the evaluation of many types.")]
- [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "This class requires the evaluation of many types.")]
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is handled.")]
- internal MessageHelpInformation(HttpOperationDescription description, bool isRequest, Type type, bool wrapped)
- {
- this.Type = type;
- this.SupportsJson = true;
- string direction = isRequest ? Http.SR.HelpPageRequest : Http.SR.HelpPageResponse;
-
- if (wrapped && !typeof(void).Equals(type))
- {
- this.BodyDescription = Http.SR.HelpPageBodyIsWrapped(direction);
- this.FormatString = Http.SR.HelpPageUnknown;
- }
- else if (typeof(void).Equals(type))
- {
- this.BodyDescription = Http.SR.HelpPageBodyIsEmpty(direction);
- this.FormatString = Http.SR.HelpPageNA;
- }
- else if (typeof(Message).IsAssignableFrom(type))
- {
- this.BodyDescription = Http.SR.HelpPageIsMessage(direction);
- this.FormatString = Http.SR.HelpPageUnknown;
- }
- else if (typeof(Stream).IsAssignableFrom(type))
- {
- this.BodyDescription = Http.SR.HelpPageIsStream(direction);
- this.FormatString = Http.SR.HelpPageUnknown;
- }
- else if (typeof(Atom10FeedFormatter).IsAssignableFrom(type))
- {
- this.BodyDescription = Http.SR.HelpPageIsAtom10Feed(direction);
- this.FormatString = WebMessageFormat.Xml.ToString();
- }
- else if (typeof(Atom10ItemFormatter).IsAssignableFrom(type))
- {
- this.BodyDescription = Http.SR.HelpPageIsAtom10Entry(direction);
- this.FormatString = WebMessageFormat.Xml.ToString();
- }
- else if (typeof(AtomPub10ServiceDocumentFormatter).IsAssignableFrom(type))
- {
- this.BodyDescription = Http.SR.HelpPageIsAtomPubServiceDocument(direction);
- this.FormatString = WebMessageFormat.Xml.ToString();
- }
- else if (typeof(AtomPub10CategoriesDocumentFormatter).IsAssignableFrom(type))
- {
- this.BodyDescription = Http.SR.HelpPageIsAtomPubCategoriesDocument(direction);
- this.FormatString = WebMessageFormat.Xml.ToString();
- }
- else if (typeof(Rss20FeedFormatter).IsAssignableFrom(type))
- {
- this.BodyDescription = Http.SR.HelpPageIsRSS20Feed(direction);
- this.FormatString = WebMessageFormat.Xml.ToString();
- }
- else if (typeof(SyndicationFeedFormatter).IsAssignableFrom(type))
- {
- this.BodyDescription = Http.SR.HelpPageIsSyndication(direction);
- this.FormatString = WebMessageFormat.Xml.ToString();
- }
- else if (typeof(XElement).IsAssignableFrom(type) || typeof(XmlElement).IsAssignableFrom(type))
- {
- this.BodyDescription = Http.SR.HelpPageIsXML(direction);
- this.FormatString = WebMessageFormat.Xml.ToString();
- }
- else
- {
- try
- {
- bool usesXmlSerializer = HttpOperationHandlerFactory.DetermineSerializerFormat(description) != XmlMediaTypeSerializerFormat.DataContractSerializer;
-
- XmlQualifiedName name;
- this.SchemaSet = new XmlSchemaSet();
- IDictionary<XmlQualifiedName, Type> knownTypes = new Dictionary<XmlQualifiedName, Type>();
- if (usesXmlSerializer)
- {
- XmlReflectionImporter importer = new XmlReflectionImporter();
- XmlTypeMapping typeMapping = importer.ImportTypeMapping(this.Type);
- name = new XmlQualifiedName(typeMapping.ElementName, typeMapping.Namespace);
- XmlSchemas schemas = new XmlSchemas();
- XmlSchemaExporter exporter = new XmlSchemaExporter(schemas);
- exporter.ExportTypeMapping(typeMapping);
- foreach (XmlSchema schema in schemas)
- {
- this.SchemaSet.Add(schema);
- }
- }
- else
- {
- XsdDataContractExporter exporter = new XsdDataContractExporter();
- List<Type> listTypes = new List<Type>(description.KnownTypes);
- bool isQueryable;
- Type dataContractType = GetSubstituteDataContractType(this.Type, out isQueryable);
-
- listTypes.Add(dataContractType);
- exporter.Export(listTypes);
- if (!exporter.CanExport(dataContractType))
- {
- this.BodyDescription = Http.SR.HelpPageCouldNotGenerateSchema;
- this.FormatString = Http.SR.HelpPageUnknown;
- return;
- }
- name = exporter.GetRootElementName(dataContractType);
- DataContract typeDataContract = DataContract.GetDataContract(dataContractType);
- if (typeDataContract.KnownDataContracts != null)
- {
- foreach (XmlQualifiedName dataContractName in typeDataContract.KnownDataContracts.Keys)
- {
- knownTypes.Add(dataContractName, typeDataContract.KnownDataContracts[dataContractName].UnderlyingType);
- }
- }
- foreach (Type knownType in description.KnownTypes)
- {
- XmlQualifiedName knownTypeName = exporter.GetSchemaTypeName(knownType);
- if (!knownTypes.ContainsKey(knownTypeName))
- {
- knownTypes.Add(knownTypeName, knownType);
- }
- }
-
- foreach (XmlSchema schema in exporter.Schemas.Schemas())
- {
- this.SchemaSet.Add(schema);
- }
- }
- this.SchemaSet.Compile();
-
- XmlWriterSettings settings = new XmlWriterSettings
- {
- CloseOutput = false,
- Indent = true,
- };
-
- if (this.SupportsJson)
- {
- XDocument exampleDocument = new XDocument();
- using (XmlWriter writer = XmlWriter.Create(exampleDocument.CreateWriter(), settings))
- {
- HelpExampleGenerator.GenerateJsonSample(this.SchemaSet, name, writer, knownTypes);
- }
- this.JsonExample = exampleDocument.Root;
- }
-
- if (name.Namespace != "http://schemas.microsoft.com/2003/10/Serialization/")
- {
- foreach (XmlSchema schema in this.SchemaSet.Schemas(name.Namespace))
- {
- this.Schema = schema;
-
- }
- }
-
- XDocument XmlExampleDocument = new XDocument();
- using (XmlWriter writer = XmlWriter.Create(XmlExampleDocument.CreateWriter(), settings))
- {
- HelpExampleGenerator.GenerateXmlSample(this.SchemaSet, name, writer);
- }
- this.XmlExample = XmlExampleDocument.Root;
-
- }
- catch (Exception)
- {
- this.BodyDescription = Http.SR.HelpPageCouldNotGenerateSchema;
- this.FormatString = Http.SR.HelpPageUnknown;
- this.Schema = null;
- this.JsonExample = null;
- this.XmlExample = null;
- }
- }
- }
-
- private static Type GetSubstituteDataContractType(Type type, out bool isQueryable)
- {
- if (type == typeOfIQueryable)
- {
- isQueryable = true;
- return typeOfIEnumerable;
- }
-
- if (type.IsGenericType &&
- type.GetGenericTypeDefinition() == typeOfIQueryableGeneric)
- {
- isQueryable = true;
- return typeOfIEnumerableGeneric.MakeGenericType(type.GetGenericArguments());
- }
-
- isQueryable = false;
- return type;
- }
- }
-
- private class HelpHtmlPageBuilder : HtmlPageBuilder
- {
- internal const string HelpOperationPageUrl = "help/operations/{0}";
-
- private HelpHtmlPageBuilder()
- {
- }
-
- public static XDocument CreateHelpPage(Uri baseUri, IEnumerable<OperationHelpInformation> operations)
- {
- XDocument document = CreateBaseDocument(Http.SR.HelpPageOperationsAt(baseUri));
- XElement table = new XElement(HtmlTableElementName,
- new XElement(HtmlTrElementName,
- new XElement(HtmlThElementName, Http.SR.HelpPageUri),
- new XElement(HtmlThElementName, Http.SR.HelpPageMethod),
- new XElement(HtmlThElementName, Http.SR.HelpPageDescription)));
-
- string lastOperation = null;
- XElement firstTr = null;
- int rowspan = 0;
- foreach (OperationHelpInformation operationHelpInfo in operations.OrderBy(o => FilterQueryVariables(o.UriTemplate)))
- {
- string operationUri = FilterQueryVariables(operationHelpInfo.UriTemplate);
- string description = operationHelpInfo.Description;
- if (String.IsNullOrEmpty(description))
- {
- description = Http.SR.HelpPageDefaultDescription(BuildFullUriTemplate(baseUri, operationHelpInfo.UriTemplate));
- }
- XElement tr = new XElement(HtmlTrElementName,
- new XElement(HtmlTdElementName, new XAttribute(HtmlTitleAttributeName, BuildFullUriTemplate(baseUri, operationHelpInfo.UriTemplate)),
- new XElement(HtmlAElementName,
- new XAttribute(HtmlRelAttributeName, HtmlOperationClass),
- new XAttribute(HtmlHrefAttributeName, String.Format(CultureInfo.InvariantCulture, HelpOperationPageUrl, operationHelpInfo.Name)), operationHelpInfo.Method)),
- new XElement(HtmlTdElementName, description));
- table.Add(tr);
- if (operationUri != lastOperation)
- {
- XElement td = new XElement(HtmlTdElementName, operationUri == lastOperation ? String.Empty : operationUri);
- tr.AddFirst(td);
- if (firstTr != null && rowspan > 1)
- {
- firstTr.Descendants(HtmlTdElementName).First().Add(new XAttribute(HtmlRowspanAttributeName, rowspan));
- }
- firstTr = tr;
- rowspan = 0;
- lastOperation = operationUri;
- }
- ++rowspan;
- }
- if (firstTr != null && rowspan > 1)
- {
- firstTr.Descendants(HtmlTdElementName).First().Add(new XAttribute(HtmlRowspanAttributeName, rowspan));
- }
- document.Descendants(HtmlBodyElementName).First().Add(new XElement(HtmlDivElementName, new XAttribute(HtmlIdAttributeName, HtmlContentClass),
- new XElement(HtmlPElementName, new XAttribute(HtmlClassAttributeName, HtmlHeading1Class), Http.SR.HelpPageOperationsAt(baseUri)),
- new XElement(HtmlPElementName, Http.SR.HelpPageStaticText),
- table));
- return document;
- }
-
- public static XDocument CreateOperationHelpPage(Uri baseUri, OperationHelpInformation operationInfo)
- {
- XDocument document = CreateBaseDocument(Http.SR.HelpPageReferenceFor(BuildFullUriTemplate(baseUri, operationInfo.UriTemplate)));
- XElement table = new XElement(HtmlTableElementName,
- new XElement(HtmlTrElementName,
- new XElement(HtmlThElementName, Http.SR.HelpPageMessageDirection),
- new XElement(HtmlThElementName, Http.SR.HelpPageFormat),
- new XElement(HtmlThElementName, Http.SR.HelpPageBody)));
-
- RenderMessageInformation(table, operationInfo, true);
- RenderMessageInformation(table, operationInfo, false);
-
- XElement div = new XElement(HtmlDivElementName, new XAttribute(HtmlIdAttributeName, HtmlContentClass),
- new XElement(HtmlPElementName, new XAttribute(HtmlClassAttributeName, HtmlHeading1Class), Http.SR.HelpPageReferenceFor(BuildFullUriTemplate(baseUri, operationInfo.UriTemplate))),
- new XElement(HtmlPElementName, operationInfo.Description),
- XElement.Parse(Http.SR.HelpPageOperationUri(HttpUtility.HtmlEncode(BuildFullUriTemplate(baseUri, operationInfo.UriTemplate)))),
- XElement.Parse(Http.SR.HelpPageOperationMethod(HttpUtility.HtmlEncode(operationInfo.Method))));
- if (!String.IsNullOrEmpty(operationInfo.JavascriptCallbackParameterName))
- {
- div.Add(XElement.Parse(Http.SR.HelpPageCallbackText(HttpUtility.HtmlEncode(operationInfo.JavascriptCallbackParameterName))), table);
- }
- else
- {
- div.Add(table);
- }
- document.Descendants(HtmlBodyElementName).First().Add(div);
-
- CreateOperationSamples(document.Descendants(HtmlDivElementName).First(), operationInfo);
-
- return document;
- }
-
- private static string FilterQueryVariables(string uriTemplate)
- {
- int variablesIndex = uriTemplate.IndexOf('?');
- if (variablesIndex > 0)
- {
- return uriTemplate.Substring(0, variablesIndex);
- }
- return uriTemplate;
- }
-
- private static void RenderMessageInformation(XElement table, OperationHelpInformation operationInfo, bool isRequest)
- {
- MessageHelpInformation info = isRequest ? operationInfo.Request : operationInfo.Response;
- string direction = isRequest ? Http.SR.HelpPageRequest : Http.SR.HelpPageResponse;
-
- if (info.BodyDescription != null)
- {
- table.Add(new XElement(HtmlTrElementName,
- new XElement(HtmlTdElementName, direction),
- new XElement(HtmlTdElementName, info.FormatString),
- new XElement(HtmlTdElementName, info.BodyDescription)));
- }
- else
- {
- if (info.XmlExample != null || info.Schema != null)
- {
- XElement contentTd;
- table.Add(new XElement(HtmlTrElementName,
- new XElement(HtmlTdElementName, direction),
- new XElement(HtmlTdElementName, "Xml"),
- contentTd = new XElement(HtmlTdElementName)));
-
- if (info.XmlExample != null)
- {
- contentTd.Add(new XElement(HtmlAElementName, new XAttribute(HtmlHrefAttributeName, "#" + (isRequest ? HtmlRequestXmlId : HtmlResponseXmlId)), Http.SR.HelpPageExample));
- if (info.Schema != null)
- {
- contentTd.Add(",");
- }
- }
- if (info.Schema != null)
- {
- contentTd.Add(new XElement(HtmlAElementName, new XAttribute(HtmlHrefAttributeName, "#" + (isRequest ? HtmlRequestSchemaId : HtmlResponseSchemaId)), Http.SR.HelpPageSchema));
- }
- }
- if (info.JsonExample != null)
- {
- table.Add(new XElement(HtmlTrElementName,
- new XElement(HtmlTdElementName, direction),
- new XElement(HtmlTdElementName, "Json"),
- new XElement(HtmlTdElementName,
- new XElement(HtmlAElementName, new XAttribute(HtmlHrefAttributeName, "#" + (isRequest ? HtmlRequestJsonId : HtmlResponseJsonId)), Http.SR.HelpPageExample))));
- }
- }
- }
-
- private static void CreateOperationSamples(XElement element, OperationHelpInformation operationInfo)
- {
- if (operationInfo.Request.XmlExample != null)
- {
- element.Add(GenerateSampleXml(operationInfo.Request.XmlExample, Http.SR.HelpPageXmlRequest, HtmlRequestXmlId));
- }
- if (operationInfo.Request.JsonExample != null)
- {
- element.Add(AddSampleJson(operationInfo.Request.JsonExample, Http.SR.HelpPageJsonRequest, HtmlRequestJsonId));
- }
- if (operationInfo.Response.XmlExample != null)
- {
- element.Add(GenerateSampleXml(operationInfo.Response.XmlExample, Http.SR.HelpPageXmlResponse, HtmlResponseXmlId));
- }
- if (operationInfo.Response.JsonExample != null)
- {
- element.Add(AddSampleJson(operationInfo.Response.JsonExample, Http.SR.HelpPageJsonResponse, HtmlResponseJsonId));
- }
-
- if (operationInfo.Request.Schema != null)
- {
- element.Add(GenerateSampleXml(XmlSchemaToXElement(operationInfo.Request.Schema), Http.SR.HelpPageRequestSchema, HtmlRequestSchemaId));
- int count = 0;
- foreach (XmlSchema schema in operationInfo.Request.SchemaSet.Schemas())
- {
- if (schema.TargetNamespace != operationInfo.Request.Schema.TargetNamespace)
- {
- element.Add(GenerateSampleXml(XmlSchemaToXElement(schema), ++count == 1 ? Http.SR.HelpPageAdditionalRequestSchema : null, HtmlRequestSchemaId));
- }
- }
- }
- if (operationInfo.Response.Schema != null)
- {
- element.Add(GenerateSampleXml(XmlSchemaToXElement(operationInfo.Response.Schema), Http.SR.HelpPageResponseSchema, HtmlResponseSchemaId));
- int count = 0;
- foreach (XmlSchema schema in operationInfo.Response.SchemaSet.Schemas())
- {
- if (schema.TargetNamespace != operationInfo.Response.Schema.TargetNamespace)
- {
- element.Add(GenerateSampleXml(XmlSchemaToXElement(schema), ++count == 1 ? Http.SR.HelpPageAdditionalResponseSchema : null, HtmlResponseSchemaId));
- }
- }
- }
- }
-
- private static XElement XmlSchemaToXElement(XmlSchema schema)
- {
- XmlWriterSettings settings = new XmlWriterSettings
- {
- CloseOutput = false,
- Indent = true,
- };
-
- XDocument schemaDocument = new XDocument();
-
- using (XmlWriter writer = XmlWriter.Create(schemaDocument.CreateWriter(), settings))
- {
- schema.Write(writer);
- }
- return schemaDocument.Root;
- }
-
- private static XElement AddSample(object content, string title, string label)
- {
- if (String.IsNullOrEmpty(title))
- {
- return new XElement(HtmlPElementName,
- new XElement(HtmlPreElementName, new XAttribute(HtmlClassAttributeName, label), content));
- }
- else
- {
- return new XElement(HtmlPElementName,
- new XElement(HtmlAElementName, new XAttribute(HtmlNameAttributeName, "#" + label), title),
- new XElement(HtmlPreElementName, new XAttribute(HtmlClassAttributeName, label), content));
- }
- }
-
- private static XElement GenerateSampleXml(XElement content, string title, string label)
- {
- StringBuilder sample = new StringBuilder();
- using (XmlWriter writer = XmlTextWriter.Create(sample, new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
- {
- content.WriteTo(writer);
- }
- return AddSample(sample.ToString(), title, label);
- }
-
- [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "dispose is idempotent.")]
- private static XElement AddSampleJson(XElement content, string title, string label)
- {
- StringBuilder sample = new StringBuilder();
- using (MemoryStream stream = new MemoryStream())
- {
- using (XmlDictionaryWriter writer =
- JsonReaderWriterFactory.CreateJsonWriter(stream, Encoding.Unicode, false))
- {
- content.WriteTo(writer);
- }
-
- stream.Position = 0;
- sample.Append(new StreamReader(stream, Encoding.Unicode).ReadToEnd());
- }
- int depth = 0;
- bool inString = false;
- for (int i = 0; i < sample.Length; ++i)
- {
- if (sample[i] == '"')
- {
- inString = !inString;
- }
- else if (sample[i] == '{')
- {
- sample.Insert(i + 1, "\n" + new String('\t', ++depth));
- i += depth + 1;
- }
- else if (sample[i] == ',' && !inString)
- {
- sample.Insert(i + 1, "\n" + new String('\t', depth));
- }
- else if (sample[i] == '}' && depth > 0)
- {
- sample.Insert(i, "\n" + new String('\t', --depth));
- i += depth + 1;
- }
- }
- return AddSample(sample.ToString(), title, label);
- }
-
- private static string BuildFullUriTemplate(Uri baseUri, string uriTemplate)
- {
- UriTemplate template = new UriTemplate(uriTemplate);
- Uri result = template.BindByPosition(baseUri, template.PathSegmentVariableNames.Concat(template.QueryValueVariableNames).Select(name => "{" + name + "}").ToArray());
- return result.ToString();
- }
- }
-
- [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "This type requires the evaluation of many types.")]
- private static class HelpExampleGenerator
- {
- private const int MaxDepthLevel = 256;
- private const string XmlSchemaNamespace = "http://www.w3.org/2001/XMLSchema";
- private const string XmlNamespacePrefix = "xmlns";
- private const string XmlSchemaInstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance";
- private const string XmlSchemaInstanceNil = "nil";
- private const string XmlSchemaInstanceType = "type";
-
- private static Dictionary<Type, Action<XmlSchemaObject, HelpExampleGeneratorContext>> XmlObjectHandler = new Dictionary<Type, Action<XmlSchemaObject, HelpExampleGeneratorContext>>
- {
- { typeof(XmlSchemaComplexContent), ContentHandler },
- { typeof(XmlSchemaSimpleContent), ContentHandler },
- { typeof(XmlSchemaSimpleTypeRestriction), SimpleTypeRestrictionHandler },
- { typeof(XmlSchemaChoice), ChoiceHandler },
- // Nothing to do, inheritance is resolved by Schema compilation process
- { typeof(XmlSchemaComplexContentExtension), EmptyHandler },
- { typeof(XmlSchemaSimpleContentExtension), EmptyHandler },
- // No need to generate XML for these objects
- { typeof(XmlSchemaAny), EmptyHandler },
- { typeof(XmlSchemaAnyAttribute), EmptyHandler },
- { typeof(XmlSchemaAnnotated), EmptyHandler },
- { typeof(XmlSchema), EmptyHandler },
- // The following schema objects are not handled
- { typeof(XmlSchemaAttributeGroup), ErrorHandler },
- { typeof(XmlSchemaAttributeGroupRef), ErrorHandler },
- { typeof(XmlSchemaComplexContentRestriction), ErrorHandler },
- { typeof(XmlSchemaSimpleContentRestriction), ErrorHandler },
- // Enumerations are supported by the GenerateContentForSimpleType
- { typeof(XmlSchemaEnumerationFacet), EmptyHandler },
- { typeof(XmlSchemaMaxExclusiveFacet), ErrorHandler },
- { typeof(XmlSchemaMaxInclusiveFacet), ErrorHandler },
- { typeof(XmlSchemaMinExclusiveFacet), ErrorHandler },
- { typeof(XmlSchemaMinInclusiveFacet), ErrorHandler },
- { typeof(XmlSchemaNumericFacet), ErrorHandler },
- { typeof(XmlSchemaFractionDigitsFacet), ErrorHandler },
- { typeof(XmlSchemaLengthFacet), ErrorHandler },
- { typeof(XmlSchemaMaxLengthFacet), ErrorHandler },
- { typeof(XmlSchemaMinLengthFacet), ErrorHandler },
- { typeof(XmlSchemaTotalDigitsFacet), ErrorHandler },
- { typeof(XmlSchemaPatternFacet), ErrorHandler },
- { typeof(XmlSchemaWhiteSpaceFacet), ErrorHandler },
- { typeof(XmlSchemaGroup), ErrorHandler },
- { typeof(XmlSchemaIdentityConstraint), ErrorHandler },
- { typeof(XmlSchemaKey), ErrorHandler },
- { typeof(XmlSchemaKeyref), ErrorHandler },
- { typeof(XmlSchemaUnique), ErrorHandler },
- { typeof(XmlSchemaNotation), ErrorHandler },
- { typeof(XmlSchemaAll), ErrorHandler },
- { typeof(XmlSchemaGroupRef), ErrorHandler },
- { typeof(XmlSchemaSimpleTypeUnion), ErrorHandler },
- { typeof(XmlSchemaSimpleTypeList), XmlSimpleTypeListHandler },
- { typeof(XmlSchemaXPath), ErrorHandler },
- { typeof(XmlSchemaAttribute), XmlAttributeHandler },
- { typeof(XmlSchemaElement), XmlElementHandler },
- { typeof(XmlSchemaComplexType), XmlComplexTypeHandler },
- { typeof(XmlSchemaSequence), XmlSequenceHandler },
- { typeof(XmlSchemaSimpleType), XmlSimpleTypeHandler },
- };
-
- private static Dictionary<Type, Action<XmlSchemaObject, HelpExampleGeneratorContext>> JsonObjectHandler = new Dictionary<Type, Action<XmlSchemaObject, HelpExampleGeneratorContext>>
- {
- { typeof(XmlSchemaComplexContent), ContentHandler },
- { typeof(XmlSchemaSimpleContent), ContentHandler },
- { typeof(XmlSchemaSimpleTypeRestriction), SimpleTypeRestrictionHandler },
- { typeof(XmlSchemaChoice), ChoiceHandler },
- // Nothing to do, inheritance is resolved by Schema compilation process
- { typeof(XmlSchemaComplexContentExtension), EmptyHandler },
- { typeof(XmlSchemaSimpleContentExtension), EmptyHandler },
- // No need to generate XML for these objects
- …
Large files files are truncated, but you can click here to view the full file