123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- using System;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using System.Globalization;
- using System.Linq;
- using System.Net.Http;
- using System.Net.Http.Headers;
- using System.Web.Http;
- using System.Web.Http.Controllers;
- using System.Web.Http.Description;
- using CacheControl_test.Areas.HelpPage.ModelDescriptions;
- using CacheControl_test.Areas.HelpPage.Models;
-
- namespace CacheControl_test.Areas.HelpPage
- {
- public static class HelpPageConfigurationExtensions
- {
- private const string ApiModelPrefix = "MS_HelpPageApiModel_";
-
- /// <summary>
- /// Sets the documentation provider for help page.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <param name="documentationProvider">The documentation provider.</param>
- public static void SetDocumentationProvider(this HttpConfiguration config, IDocumentationProvider documentationProvider)
- {
- config.Services.Replace(typeof(IDocumentationProvider), documentationProvider);
- }
-
- /// <summary>
- /// Sets the objects that will be used by the formatters to produce sample requests/responses.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <param name="sampleObjects">The sample objects.</param>
- public static void SetSampleObjects(this HttpConfiguration config, IDictionary<Type, object> sampleObjects)
- {
- config.GetHelpPageSampleGenerator().SampleObjects = sampleObjects;
- }
-
- /// <summary>
- /// Sets the sample request directly for the specified media type and action.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <param name="sample">The sample request.</param>
- /// <param name="mediaType">The media type.</param>
- /// <param name="controllerName">Name of the controller.</param>
- /// <param name="actionName">Name of the action.</param>
- public static void SetSampleRequest(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName)
- {
- config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, new[] { "*" }), sample);
- }
-
- /// <summary>
- /// Sets the sample request directly for the specified media type and action with parameters.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <param name="sample">The sample request.</param>
- /// <param name="mediaType">The media type.</param>
- /// <param name="controllerName">Name of the controller.</param>
- /// <param name="actionName">Name of the action.</param>
- /// <param name="parameterNames">The parameter names.</param>
- public static void SetSampleRequest(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName, params string[] parameterNames)
- {
- config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, parameterNames), sample);
- }
-
- /// <summary>
- /// Sets the sample request directly for the specified media type of the action.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <param name="sample">The sample response.</param>
- /// <param name="mediaType">The media type.</param>
- /// <param name="controllerName">Name of the controller.</param>
- /// <param name="actionName">Name of the action.</param>
- public static void SetSampleResponse(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName)
- {
- config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, new[] { "*" }), sample);
- }
-
- /// <summary>
- /// Sets the sample response directly for the specified media type of the action with specific parameters.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <param name="sample">The sample response.</param>
- /// <param name="mediaType">The media type.</param>
- /// <param name="controllerName">Name of the controller.</param>
- /// <param name="actionName">Name of the action.</param>
- /// <param name="parameterNames">The parameter names.</param>
- public static void SetSampleResponse(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName, params string[] parameterNames)
- {
- config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, parameterNames), sample);
- }
-
- /// <summary>
- /// Sets the sample directly for all actions with the specified media type.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <param name="sample">The sample.</param>
- /// <param name="mediaType">The media type.</param>
- public static void SetSampleForMediaType(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType)
- {
- config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType), sample);
- }
-
- /// <summary>
- /// Sets the sample directly for all actions with the specified type and media type.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <param name="sample">The sample.</param>
- /// <param name="mediaType">The media type.</param>
- /// <param name="type">The parameter type or return type of an action.</param>
- public static void SetSampleForType(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, Type type)
- {
- config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, type), sample);
- }
-
- /// <summary>
- /// Specifies the actual type of <see cref="System.Net.Http.ObjectContent{T}"/> passed to the <see cref="System.Net.Http.HttpRequestMessage"/> in an action.
- /// The help page will use this information to produce more accurate request samples.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <param name="type">The type.</param>
- /// <param name="controllerName">Name of the controller.</param>
- /// <param name="actionName">Name of the action.</param>
- public static void SetActualRequestType(this HttpConfiguration config, Type type, string controllerName, string actionName)
- {
- config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, new[] { "*" }), type);
- }
-
- /// <summary>
- /// Specifies the actual type of <see cref="System.Net.Http.ObjectContent{T}"/> passed to the <see cref="System.Net.Http.HttpRequestMessage"/> in an action.
- /// The help page will use this information to produce more accurate request samples.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <param name="type">The type.</param>
- /// <param name="controllerName">Name of the controller.</param>
- /// <param name="actionName">Name of the action.</param>
- /// <param name="parameterNames">The parameter names.</param>
- public static void SetActualRequestType(this HttpConfiguration config, Type type, string controllerName, string actionName, params string[] parameterNames)
- {
- config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, parameterNames), type);
- }
-
- /// <summary>
- /// Specifies the actual type of <see cref="System.Net.Http.ObjectContent{T}"/> returned as part of the <see cref="System.Net.Http.HttpRequestMessage"/> in an action.
- /// The help page will use this information to produce more accurate response samples.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <param name="type">The type.</param>
- /// <param name="controllerName">Name of the controller.</param>
- /// <param name="actionName">Name of the action.</param>
- public static void SetActualResponseType(this HttpConfiguration config, Type type, string controllerName, string actionName)
- {
- config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, new[] { "*" }), type);
- }
-
- /// <summary>
- /// Specifies the actual type of <see cref="System.Net.Http.ObjectContent{T}"/> returned as part of the <see cref="System.Net.Http.HttpRequestMessage"/> in an action.
- /// The help page will use this information to produce more accurate response samples.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <param name="type">The type.</param>
- /// <param name="controllerName">Name of the controller.</param>
- /// <param name="actionName">Name of the action.</param>
- /// <param name="parameterNames">The parameter names.</param>
- public static void SetActualResponseType(this HttpConfiguration config, Type type, string controllerName, string actionName, params string[] parameterNames)
- {
- config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, parameterNames), type);
- }
-
- /// <summary>
- /// Gets the help page sample generator.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <returns>The help page sample generator.</returns>
- public static HelpPageSampleGenerator GetHelpPageSampleGenerator(this HttpConfiguration config)
- {
- return (HelpPageSampleGenerator)config.Properties.GetOrAdd(
- typeof(HelpPageSampleGenerator),
- k => new HelpPageSampleGenerator());
- }
-
- /// <summary>
- /// Sets the help page sample generator.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <param name="sampleGenerator">The help page sample generator.</param>
- public static void SetHelpPageSampleGenerator(this HttpConfiguration config, HelpPageSampleGenerator sampleGenerator)
- {
- config.Properties.AddOrUpdate(
- typeof(HelpPageSampleGenerator),
- k => sampleGenerator,
- (k, o) => sampleGenerator);
- }
-
- /// <summary>
- /// Gets the model description generator.
- /// </summary>
- /// <param name="config">The configuration.</param>
- /// <returns>The <see cref="ModelDescriptionGenerator"/></returns>
- public static ModelDescriptionGenerator GetModelDescriptionGenerator(this HttpConfiguration config)
- {
- return (ModelDescriptionGenerator)config.Properties.GetOrAdd(
- typeof(ModelDescriptionGenerator),
- k => InitializeModelDescriptionGenerator(config));
- }
-
- /// <summary>
- /// Gets the model that represents an API displayed on the help page. The model is initialized on the first call and cached for subsequent calls.
- /// </summary>
- /// <param name="config">The <see cref="HttpConfiguration"/>.</param>
- /// <param name="apiDescriptionId">The <see cref="ApiDescription"/> ID.</param>
- /// <returns>
- /// An <see cref="HelpPageApiModel"/>
- /// </returns>
- public static HelpPageApiModel GetHelpPageApiModel(this HttpConfiguration config, string apiDescriptionId)
- {
- object model;
- string modelId = ApiModelPrefix + apiDescriptionId;
- if (!config.Properties.TryGetValue(modelId, out model))
- {
- Collection<ApiDescription> apiDescriptions = config.Services.GetApiExplorer().ApiDescriptions;
- ApiDescription apiDescription = apiDescriptions.FirstOrDefault(api => String.Equals(api.GetFriendlyId(), apiDescriptionId, StringComparison.OrdinalIgnoreCase));
- if (apiDescription != null)
- {
- model = GenerateApiModel(apiDescription, config);
- config.Properties.TryAdd(modelId, model);
- }
- }
-
- return (HelpPageApiModel)model;
- }
-
- private static HelpPageApiModel GenerateApiModel(ApiDescription apiDescription, HttpConfiguration config)
- {
- HelpPageApiModel apiModel = new HelpPageApiModel()
- {
- ApiDescription = apiDescription,
- };
-
- ModelDescriptionGenerator modelGenerator = config.GetModelDescriptionGenerator();
- HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator();
- GenerateUriParameters(apiModel, modelGenerator);
- GenerateRequestModelDescription(apiModel, modelGenerator, sampleGenerator);
- GenerateResourceDescription(apiModel, modelGenerator);
- GenerateSamples(apiModel, sampleGenerator);
-
- return apiModel;
- }
-
- private static void GenerateUriParameters(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator)
- {
- ApiDescription apiDescription = apiModel.ApiDescription;
- foreach (ApiParameterDescription apiParameter in apiDescription.ParameterDescriptions)
- {
- if (apiParameter.Source == ApiParameterSource.FromUri)
- {
- HttpParameterDescriptor parameterDescriptor = apiParameter.ParameterDescriptor;
- Type parameterType = null;
- ModelDescription typeDescription = null;
- ComplexTypeModelDescription complexTypeDescription = null;
- if (parameterDescriptor != null)
- {
- parameterType = parameterDescriptor.ParameterType;
- typeDescription = modelGenerator.GetOrCreateModelDescription(parameterType);
- complexTypeDescription = typeDescription as ComplexTypeModelDescription;
- }
-
- // Example:
- // [TypeConverter(typeof(PointConverter))]
- // public class Point
- // {
- // public Point(int x, int y)
- // {
- // X = x;
- // Y = y;
- // }
- // public int X { get; set; }
- // public int Y { get; set; }
- // }
- // Class Point is bindable with a TypeConverter, so Point will be added to UriParameters collection.
- //
- // public class Point
- // {
- // public int X { get; set; }
- // public int Y { get; set; }
- // }
- // Regular complex class Point will have properties X and Y added to UriParameters collection.
- if (complexTypeDescription != null
- && !IsBindableWithTypeConverter(parameterType))
- {
- foreach (ParameterDescription uriParameter in complexTypeDescription.Properties)
- {
- apiModel.UriParameters.Add(uriParameter);
- }
- }
- else if (parameterDescriptor != null)
- {
- ParameterDescription uriParameter =
- AddParameterDescription(apiModel, apiParameter, typeDescription);
-
- if (!parameterDescriptor.IsOptional)
- {
- uriParameter.Annotations.Add(new ParameterAnnotation() { Documentation = "Required" });
- }
-
- object defaultValue = parameterDescriptor.DefaultValue;
- if (defaultValue != null)
- {
- uriParameter.Annotations.Add(new ParameterAnnotation() { Documentation = "Default value is " + Convert.ToString(defaultValue, CultureInfo.InvariantCulture) });
- }
- }
- else
- {
- Debug.Assert(parameterDescriptor == null);
-
- // If parameterDescriptor is null, this is an undeclared route parameter which only occurs
- // when source is FromUri. Ignored in request model and among resource parameters but listed
- // as a simple string here.
- ModelDescription modelDescription = modelGenerator.GetOrCreateModelDescription(typeof(string));
- AddParameterDescription(apiModel, apiParameter, modelDescription);
- }
- }
- }
- }
-
- private static bool IsBindableWithTypeConverter(Type parameterType)
- {
- if (parameterType == null)
- {
- return false;
- }
-
- return TypeDescriptor.GetConverter(parameterType).CanConvertFrom(typeof(string));
- }
-
- private static ParameterDescription AddParameterDescription(HelpPageApiModel apiModel,
- ApiParameterDescription apiParameter, ModelDescription typeDescription)
- {
- ParameterDescription parameterDescription = new ParameterDescription
- {
- Name = apiParameter.Name,
- Documentation = apiParameter.Documentation,
- TypeDescription = typeDescription,
- };
-
- apiModel.UriParameters.Add(parameterDescription);
- return parameterDescription;
- }
-
- private static void GenerateRequestModelDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator, HelpPageSampleGenerator sampleGenerator)
- {
- ApiDescription apiDescription = apiModel.ApiDescription;
- foreach (ApiParameterDescription apiParameter in apiDescription.ParameterDescriptions)
- {
- if (apiParameter.Source == ApiParameterSource.FromBody)
- {
- Type parameterType = apiParameter.ParameterDescriptor.ParameterType;
- apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType);
- apiModel.RequestDocumentation = apiParameter.Documentation;
- }
- else if (apiParameter.ParameterDescriptor != null &&
- apiParameter.ParameterDescriptor.ParameterType == typeof(HttpRequestMessage))
- {
- Type parameterType = sampleGenerator.ResolveHttpRequestMessageType(apiDescription);
-
- if (parameterType != null)
- {
- apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType);
- }
- }
- }
- }
-
- private static void GenerateResourceDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator)
- {
- ResponseDescription response = apiModel.ApiDescription.ResponseDescription;
- Type responseType = response.ResponseType ?? response.DeclaredType;
- if (responseType != null && responseType != typeof(void))
- {
- apiModel.ResourceDescription = modelGenerator.GetOrCreateModelDescription(responseType);
- }
- }
-
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The exception is recorded as ErrorMessages.")]
- private static void GenerateSamples(HelpPageApiModel apiModel, HelpPageSampleGenerator sampleGenerator)
- {
- try
- {
- foreach (var item in sampleGenerator.GetSampleRequests(apiModel.ApiDescription))
- {
- apiModel.SampleRequests.Add(item.Key, item.Value);
- LogInvalidSampleAsError(apiModel, item.Value);
- }
-
- foreach (var item in sampleGenerator.GetSampleResponses(apiModel.ApiDescription))
- {
- apiModel.SampleResponses.Add(item.Key, item.Value);
- LogInvalidSampleAsError(apiModel, item.Value);
- }
- }
- catch (Exception e)
- {
- apiModel.ErrorMessages.Add(String.Format(CultureInfo.CurrentCulture,
- "An exception has occurred while generating the sample. Exception message: {0}",
- HelpPageSampleGenerator.UnwrapException(e).Message));
- }
- }
-
- private static bool TryGetResourceParameter(ApiDescription apiDescription, HttpConfiguration config, out ApiParameterDescription parameterDescription, out Type resourceType)
- {
- parameterDescription = apiDescription.ParameterDescriptions.FirstOrDefault(
- p => p.Source == ApiParameterSource.FromBody ||
- (p.ParameterDescriptor != null && p.ParameterDescriptor.ParameterType == typeof(HttpRequestMessage)));
-
- if (parameterDescription == null)
- {
- resourceType = null;
- return false;
- }
-
- resourceType = parameterDescription.ParameterDescriptor.ParameterType;
-
- if (resourceType == typeof(HttpRequestMessage))
- {
- HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator();
- resourceType = sampleGenerator.ResolveHttpRequestMessageType(apiDescription);
- }
-
- if (resourceType == null)
- {
- parameterDescription = null;
- return false;
- }
-
- return true;
- }
-
- private static ModelDescriptionGenerator InitializeModelDescriptionGenerator(HttpConfiguration config)
- {
- ModelDescriptionGenerator modelGenerator = new ModelDescriptionGenerator(config);
- Collection<ApiDescription> apis = config.Services.GetApiExplorer().ApiDescriptions;
- foreach (ApiDescription api in apis)
- {
- ApiParameterDescription parameterDescription;
- Type parameterType;
- if (TryGetResourceParameter(api, config, out parameterDescription, out parameterType))
- {
- modelGenerator.GetOrCreateModelDescription(parameterType);
- }
- }
- return modelGenerator;
- }
-
- private static void LogInvalidSampleAsError(HelpPageApiModel apiModel, object sample)
- {
- InvalidSample invalidSample = sample as InvalidSample;
- if (invalidSample != null)
- {
- apiModel.ErrorMessages.Add(invalidSample.ErrorMessage);
- }
- }
- }
- }
|