Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@ -0,0 +1,64 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Net.Http;
using System.Web.Http.Description;
using System.Web.Http.Dispatcher;
using Xunit.Extensions;
namespace System.Web.Http.ApiExplorer
{
public class ApiExplorerSettingsTest
{
public static IEnumerable<object[]> HiddenController_DoesNotShowUpOnDescription_PropertyData
{
get
{
object controllerType = typeof(HiddenController);
object expectedApiDescriptions = new List<object>();
yield return new[] { controllerType, expectedApiDescriptions };
}
}
[Theory]
[PropertyData("HiddenController_DoesNotShowUpOnDescription_PropertyData")]
public void HiddenController_DoesNotShowUpOnDescription(Type controllerType, List<object> expectedResults)
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{id}", new { id = RouteParameter.Optional });
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, controllerType);
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
ApiExplorerHelper.VerifyApiDescriptions(explorer.ApiDescriptions, expectedResults);
}
public static IEnumerable<object[]> HiddenAction_DoesNotShowUpOnDescription_PropertyData
{
get
{
object controllerType = typeof(HiddenActionController);
object expectedApiDescriptions = new List<object>
{
new { HttpMethod = HttpMethod.Get, RelativePath = "HiddenAction/{id}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 1}
};
yield return new[] { controllerType, expectedApiDescriptions };
}
}
[Theory]
[PropertyData("HiddenAction_DoesNotShowUpOnDescription_PropertyData")]
public void HiddenAction_DoesNotShowUpOnDescription(Type controllerType, List<object> expectedResults)
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{id}", new { id = RouteParameter.Optional });
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, controllerType);
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
ApiExplorerHelper.VerifyApiDescriptions(explorer.ApiDescriptions, expectedResults);
}
}
}

View File

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.ApiExplorer
{
public class DocumentationController : ApiController
{
[ApiDocumentation("Get action")]
public string Get()
{
return string.Empty;
}
[ApiDocumentation("Post action")]
[ApiParameterDocumentation("value", "value parameter")]
public void Post(string value)
{
}
[ApiDocumentation("Put action")]
[ApiParameterDocumentation("id", "id parameter")]
[ApiParameterDocumentation("value", "value parameter")]
public void Put(int id, string value)
{
}
[ApiDocumentation("Delete action")]
[ApiParameterDocumentation("id", "id parameter")]
public void Delete(int id)
{
}
}
}

View File

@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Web.Http.Description;
namespace System.Web.Http.ApiExplorer
{
public class HiddenActionController : ApiController
{
public string GetVisibleAction(int id)
{
return "visible action";
}
[HttpPost]
[ApiExplorerSettings(IgnoreApi = true)]
public void AddData()
{
}
[ApiExplorerSettings(IgnoreApi = true)]
public int Get()
{
return 0;
}
[NonAction]
public string GetHiddenAction()
{
return "Hidden action";
}
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Web.Http.Description;
namespace System.Web.Http.ApiExplorer
{
[ApiExplorerSettings(IgnoreApi = true)]
public class HiddenController : ApiController
{
public string Get(int id)
{
return "visible action";
}
[HttpPost]
public void AddData()
{
}
public int Get()
{
return 0;
}
[NonAction]
public string GetHiddenAction()
{
return "Hidden action";
}
}
}

View File

@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.ApiExplorer
{
public class ItemController : ApiController
{
public Item GetItem(string name, int series)
{
return new Item()
{
Name = name,
Series = series
};
}
[HttpPost]
[HttpPut]
public Item PostItem(Item item)
{
return item;
}
[HttpDelete]
public void RemoveItem(int id)
{
}
public class Item
{
public int Series { get; set; }
public string Name { get; set; }
}
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace System.Web.Http.ApiExplorer
{
public class OverloadsController : ApiController
{
public Person Get(int id) { return null; }
public List<Person> Get() { return null; }
public List<Person> Get(string name) { return null; }
public Person GetPersonByNameAndId(string name, int id) { return null; }
public Person GetPersonByNameAndAge(string name, int age) { return null; }
public Person GetPersonByNameAgeAndSsn(string name, int age, int ssn) { return null; }
public Person GetPersonByNameIdAndSsn(string name, int id, int ssn) { return null; }
public Person GetPersonByNameAndSsn(string name, int ssn) { return null; }
public Person Post(Person Person) { return null; }
public Person ActionDefaultedToPost(string name, int age) { return null; }
public void Delete(int id, string name = "Default Name") { }
public void Delete(int id, string name, int age) { }
public class Person
{
public string Name { get; set; }
public int ID { get; set; }
public int SSN { get; set; }
public int Age { get; set; }
}
}
}

View File

@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Web.Http.ModelBinding;
using System.Web.Http.ValueProviders;
namespace System.Web.Http.ApiExplorer
{
public class ParameterSourceController : ApiController
{
public void GetCompleTypeFromUri([FromUri]ComplexType value, string name)
{
}
public void PostSimpleTypeFromBody([FromBody] string name)
{
}
public void GetCustomFromUriAttribute([MyFromUriAttribute] ComplexType value, ComplexType bodyValue)
{
}
public void GetFromHeaderAttribute([FromHeaderAttribute] string value)
{
}
public class ComplexType
{
public int Id { get; set; }
public string Name { get; set; }
}
private class MyFromUriAttribute : ModelBinderAttribute, IUriValueProviderFactory
{
public override IEnumerable<ValueProviderFactory> GetValueProviderFactories(HttpConfiguration configuration)
{
var factories = from f in base.GetValueProviderFactories(configuration) where f is IUriValueProviderFactory select f;
return factories;
}
}
private class FromHeaderAttribute : ModelBinderAttribute
{
public override IEnumerable<ValueProviderFactory> GetValueProviderFactories(HttpConfiguration configuration)
{
var factories = new ValueProviderFactory[] { new HeaderValueProvider() };
return factories;
}
}
private class HeaderValueProvider : ValueProviderFactory
{
public override IValueProvider GetValueProvider(Controllers.HttpActionContext actionContext)
{
throw new NotImplementedException();
}
}
}
}

View File

@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Linq;
using System.Web.Http.Controllers;
using System.Web.Http.Description;
namespace System.Web.Http.ApiExplorer
{
public class AttributeDocumentationProvider : IDocumentationProvider
{
public string GetDocumentation(HttpActionDescriptor actionDescriptor)
{
var apiDocumentation = actionDescriptor.GetCustomAttributes<ApiDocumentationAttribute>().FirstOrDefault();
if (apiDocumentation != null)
{
return apiDocumentation.Description;
}
return string.Empty;
}
public string GetDocumentation(HttpParameterDescriptor parameterDescriptor)
{
var parameterDocumentation = parameterDescriptor.ActionDescriptor.GetCustomAttributes<ApiParameterDocumentationAttribute>().FirstOrDefault(param => param.ParameterName == parameterDescriptor.ParameterName);
if (parameterDocumentation != null)
{
return parameterDocumentation.Description;
}
return string.Empty;
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class ApiDocumentationAttribute : Attribute
{
public ApiDocumentationAttribute(string description)
{
Description = description;
}
public string Description { get; private set; }
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class ApiParameterDocumentationAttribute : Attribute
{
public ApiParameterDocumentationAttribute(string parameterName, string description)
{
ParameterName = parameterName;
Description = description;
}
public string ParameterName { get; private set; }
public string Description { get; private set; }
}
}

View File

@ -0,0 +1,67 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Web.Http.Description;
using System.Web.Http.Dispatcher;
using System.Web.Http.Properties;
using Xunit;
namespace System.Web.Http.ApiExplorer
{
public class DocumentationTest
{
[Fact]
public void VerifyDefaultDocumentationMessage()
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{id}", new { id = RouteParameter.Optional });
ItemFormatter customFormatter = new ItemFormatter();
config.Formatters.Add(customFormatter);
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, typeof(ItemController));
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
foreach (ApiDescription description in explorer.ApiDescriptions)
{
Assert.Equal(
String.Format(SRResources.ApiExplorer_DefaultDocumentation, description.ActionDescriptor.ActionName),
description.Documentation);
foreach (ApiParameterDescription param in description.ParameterDescriptions)
{
Assert.Equal(
String.Format(SRResources.ApiExplorer_DefaultDocumentation, param.Name),
param.Documentation);
}
}
}
[Fact]
public void VerifyCustomDocumentationProviderMessage()
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{id}", new { id = RouteParameter.Optional });
ItemFormatter customFormatter = new ItemFormatter();
config.Formatters.Add(customFormatter);
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, typeof(DocumentationController));
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
AttributeDocumentationProvider documentationProvider = new AttributeDocumentationProvider();
config.Services.Replace(typeof(IDocumentationProvider), documentationProvider);
IApiExplorer explorer = config.Services.GetApiExplorer();
foreach (ApiDescription description in explorer.ApiDescriptions)
{
Assert.Equal(
String.Format("{0} action", description.ActionDescriptor.ActionName),
description.Documentation);
foreach (ApiParameterDescription param in description.ParameterDescriptions)
{
Assert.Equal(
String.Format("{0} parameter", param.Name),
param.Documentation);
}
}
}
}
}

View File

@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Net.Http.Formatting;
namespace System.Web.Http.ApiExplorer
{
public class ItemFormatter : BufferedMediaTypeFormatter
{
public override bool CanReadType(Type type)
{
return typeof(System.Web.Http.ApiExplorer.ItemController.Item).IsAssignableFrom(type);
}
public override bool CanWriteType(Type type)
{
return typeof(System.Web.Http.ApiExplorer.ItemController.Item).IsAssignableFrom(type);
}
public override object ReadFromStream(Type type, IO.Stream stream, Net.Http.Headers.HttpContentHeaders contentHeaders, IFormatterLogger formatterLogger)
{
return base.ReadFromStream(type, stream, contentHeaders, formatterLogger);
}
public override void WriteToStream(Type type, object value, IO.Stream stream, Net.Http.Headers.HttpContentHeaders contentHeaders)
{
base.WriteToStream(type, value, stream, contentHeaders);
}
}
}

View File

@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Linq;
using System.Web.Http.Description;
using System.Web.Http.Dispatcher;
using Xunit;
namespace System.Web.Http.ApiExplorer
{
public class FormattersTest
{
[Fact]
public void CustomRequestBodyFormatters_ShowUpOnDescription()
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{id}", new { id = RouteParameter.Optional });
ItemFormatter customFormatter = new ItemFormatter();
config.Formatters.Add(customFormatter);
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, typeof(ItemController));
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
ApiDescription description = explorer.ApiDescriptions.FirstOrDefault(desc => desc.ActionDescriptor.ActionName == "PostItem");
Assert.True(description.SupportedRequestBodyFormatters.Any(formatter => formatter == customFormatter), "Did not find the custom formatter on the SupportedRequestBodyFormatters.");
}
[Fact]
public void CustomResponseFormatters_ShowUpOnDescription()
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{id}", new { id = RouteParameter.Optional });
ItemFormatter customFormatter = new ItemFormatter();
config.Formatters.Add(customFormatter);
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, typeof(ItemController));
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
ApiDescription description = explorer.ApiDescriptions.FirstOrDefault(desc => desc.ActionDescriptor.ActionName == "PostItem");
Assert.True(description.SupportedResponseFormatters.Any(formatter => formatter == customFormatter), "Did not find the custom formatter on the SupportedResponseFormatters.");
}
}
}

View File

@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Linq;
using System.Web.Http.Description;
using System.Web.Http.Dispatcher;
using Xunit;
namespace System.Web.Http.ApiExplorer
{
public class ParameterSourceTest
{
[Fact]
public void FromUriParameterSource_ShowUpCorrectlyOnDescription()
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{action}/{id}", new { id = RouteParameter.Optional });
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, typeof(ParameterSourceController));
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
ApiDescription description = explorer.ApiDescriptions.FirstOrDefault(desc => desc.ActionDescriptor.ActionName == "GetCompleTypeFromUri");
Assert.NotNull(description);
Assert.True(description.ParameterDescriptions.All(param => param.Source == ApiParameterSource.FromUri), "All parameters should come from URI.");
description = explorer.ApiDescriptions.FirstOrDefault(desc => desc.ActionDescriptor.ActionName == "GetCustomFromUriAttribute");
Assert.NotNull(description);
Assert.True(description.ParameterDescriptions.Any(param => param.Source == ApiParameterSource.FromUri && param.Name == "value"), "The 'value' parameter should come from URI.");
Assert.True(description.ParameterDescriptions.Any(param => param.Source == ApiParameterSource.FromBody && param.Name == "bodyValue"), "The 'bodyValue' parameter should come from body.");
}
[Fact]
public void FromBodyParameterSource_ShowUpCorrectlyOnDescription()
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{action}/{id}", new { id = RouteParameter.Optional });
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, typeof(ParameterSourceController));
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
ApiDescription description = explorer.ApiDescriptions.FirstOrDefault(desc => desc.ActionDescriptor.ActionName == "PostSimpleTypeFromBody");
Assert.NotNull(description);
Assert.True(description.ParameterDescriptions.All(param => param.Source == ApiParameterSource.FromBody), "The parameter should come from Body.");
}
[Fact]
public void UnknownParameterSource_ShowUpCorrectlyOnDescription()
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{action}/{id}", new { id = RouteParameter.Optional });
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, typeof(ParameterSourceController));
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
ApiDescription description = explorer.ApiDescriptions.FirstOrDefault(desc => desc.ActionDescriptor.ActionName == "GetFromHeaderAttribute");
Assert.NotNull(description);
Assert.True(description.ParameterDescriptions.All(param => param.Source == ApiParameterSource.Unknown), "The parameter source should be Unknown.");
}
}
}

View File

@ -0,0 +1,118 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Net.Http;
using System.Web.Http.Description;
using System.Web.Http.Dispatcher;
using System.Web.Http.Routing;
using Xunit.Extensions;
namespace System.Web.Http.ApiExplorer
{
public class RouteConstraintsTest
{
public static IEnumerable<object[]> HttpMethodConstraints_LimitsTheDescriptions_PropertyData
{
get
{
object controllerType = typeof(ItemController);
object expectedApiDescriptions = new List<object>
{
new { HttpMethod = HttpMethod.Get, RelativePath = "Item?name={name}&series={series}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Put, RelativePath = "Item", HasRequestFormatters = true, HasResponseFormatters = true, NumberOfParameters = 1}
};
yield return new[] { controllerType, expectedApiDescriptions };
controllerType = typeof(OverloadsController);
expectedApiDescriptions = new List<object>
{
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/{id}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 0},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads?name={name}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/{id}?name={name}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads?name={name}&age={age}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads?name={name}&age={age}&ssn={ssn}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 3},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/{id}?name={name}&ssn={ssn}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 3},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads?name={name}&ssn={ssn}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2}
};
yield return new[] { controllerType, expectedApiDescriptions };
}
}
[Theory]
[PropertyData("HttpMethodConstraints_LimitsTheDescriptions_PropertyData")]
public void HttpMethodConstraints_LimitsTheDescriptions(Type controllerType, List<object> expectedResults)
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{id}", new { id = RouteParameter.Optional }, new { routeConstraint = new HttpMethodConstraint(HttpMethod.Get, HttpMethod.Put) });
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, controllerType);
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
ApiExplorerHelper.VerifyApiDescriptions(explorer.ApiDescriptions, expectedResults);
}
public static IEnumerable<object[]> RegexConstraint_LimitsTheController_PropertyData
{
get
{
object[] controllerTypes = new Type[] { typeof(OverloadsController), typeof(ItemController) };
object expectedApiDescriptions = new List<object>
{
new { HttpMethod = HttpMethod.Get, RelativePath = "Item?name={name}&series={series}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Post, RelativePath = "Item", HasRequestFormatters = true, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Put, RelativePath = "Item", HasRequestFormatters = true, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Delete, RelativePath = "Item/{id}", HasRequestFormatters = false, HasResponseFormatters = false, NumberOfParameters = 1}
};
yield return new[] { controllerTypes, expectedApiDescriptions };
}
}
[Theory]
[PropertyData("RegexConstraint_LimitsTheController_PropertyData")]
public void RegexConstraint_LimitsTheController(Type[] controllerTypes, List<object> expectedResults)
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{id}", new { id = RouteParameter.Optional }, new { controller = "It.*" }); // controllers that start with "It"
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, controllerTypes);
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
ApiExplorerHelper.VerifyApiDescriptions(explorer.ApiDescriptions, expectedResults);
}
public static IEnumerable<object[]> RegexConstraint_LimitsTheAction_PropertyData
{
get
{
object[] controllerTypes = new Type[] { typeof(OverloadsController), typeof(ItemController) };
object expectedApiDescriptions = new List<object>
{
new { HttpMethod = HttpMethod.Get, RelativePath = "Item/GetItem?name={name}&series={series}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/GetPersonByNameAndId/{id}?name={name}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/GetPersonByNameAndAge?name={name}&age={age}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/GetPersonByNameAgeAndSsn?name={name}&age={age}&ssn={ssn}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 3},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/GetPersonByNameIdAndSsn/{id}?name={name}&ssn={ssn}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 3},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/GetPersonByNameAndSsn?name={name}&ssn={ssn}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2}
};
yield return new[] { controllerTypes, expectedApiDescriptions };
}
}
[Theory]
[PropertyData("RegexConstraint_LimitsTheAction_PropertyData")]
public void RegexConstraint_LimitsTheAction(Type[] controllerTypes, List<object> expectedResults)
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{action}/{id}", new { id = RouteParameter.Optional }, new { action = "Get.+" }); // actions that start with "Get" and at least one extra character
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, controllerTypes);
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
ApiExplorerHelper.VerifyApiDescriptions(explorer.ApiDescriptions, expectedResults);
}
}
}

View File

@ -0,0 +1,216 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Net.Http;
using System.Web.Http.Description;
using System.Web.Http.Dispatcher;
using Xunit;
using Xunit.Extensions;
namespace System.Web.Http.ApiExplorer
{
public class RoutesTest
{
[Fact]
public void VerifyDescription_OnEmptyRoute()
{
HttpConfiguration config = new HttpConfiguration();
IApiExplorer explorer = config.Services.GetApiExplorer();
Assert.NotNull(explorer);
Assert.Equal(0, explorer.ApiDescriptions.Count);
}
[Fact]
public void VerifyDescription_OnInvalidRoute()
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Invalid", "badRouteWithNoControler");
IApiExplorer explorer = config.Services.GetApiExplorer();
Assert.NotNull(explorer);
Assert.Equal(0, explorer.ApiDescriptions.Count);
}
public static IEnumerable<object[]> VerifyDescription_OnDefaultRoute_PropertyData
{
get
{
object controllerType = typeof(ItemController);
object expectedApiDescriptions = new List<object>
{
new { HttpMethod = HttpMethod.Get, RelativePath = "Item?name={name}&series={series}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Post, RelativePath = "Item", HasRequestFormatters = true, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Put, RelativePath = "Item", HasRequestFormatters = true, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Delete, RelativePath = "Item/{id}", HasRequestFormatters = false, HasResponseFormatters = false, NumberOfParameters = 1}
};
yield return new[] { controllerType, expectedApiDescriptions };
controllerType = typeof(OverloadsController);
expectedApiDescriptions = new List<object>
{
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/{id}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 0},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads?name={name}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/{id}?name={name}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads?name={name}&age={age}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads?name={name}&age={age}&ssn={ssn}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 3},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/{id}?name={name}&ssn={ssn}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 3},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads?name={name}&ssn={ssn}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Post, RelativePath = "Overloads", HasRequestFormatters = true, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Post, RelativePath = "Overloads?name={name}&age={age}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Delete, RelativePath = "Overloads/{id}?name={name}", HasRequestFormatters = false, HasResponseFormatters = false, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Delete, RelativePath = "Overloads/{id}?name={name}&age={age}", HasRequestFormatters = false, HasResponseFormatters = false, NumberOfParameters = 3}
};
yield return new[] { controllerType, expectedApiDescriptions };
}
}
[Theory]
[PropertyData("VerifyDescription_OnDefaultRoute_PropertyData")]
public void VerifyDescription_OnDefaultRoute(Type controllerType, List<object> expectedResults)
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{id}", new { id = RouteParameter.Optional });
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, controllerType);
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
ApiExplorerHelper.VerifyApiDescriptions(explorer.ApiDescriptions, expectedResults);
}
public static IEnumerable<object[]> VerifyDescription_OnRouteWithControllerOnDefaults_PropertyData
{
get
{
object controllerType = typeof(ItemController);
object expectedApiDescriptions = new List<object>
{
new { HttpMethod = HttpMethod.Get, RelativePath = "myitem?name={name}&series={series}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Post, RelativePath = "myitem", HasRequestFormatters = true, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Put, RelativePath = "myitem", HasRequestFormatters = true, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Delete, RelativePath = "myitem/{id}", HasRequestFormatters = false, HasResponseFormatters = false, NumberOfParameters = 1}
};
yield return new[] { controllerType, expectedApiDescriptions };
}
}
[Theory]
[PropertyData("VerifyDescription_OnRouteWithControllerOnDefaults_PropertyData")]
public void VerifyDescription_OnRouteWithControllerOnDefaults(Type controllerType, List<object> expectedResults)
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "myitem/{id}", new { controller = "Item", id = RouteParameter.Optional });
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, controllerType);
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
ApiExplorerHelper.VerifyApiDescriptions(explorer.ApiDescriptions, expectedResults);
}
public static IEnumerable<object[]> VerifyDescription_OnRouteWithActionVariable_PropertyData
{
get
{
object controllerType = typeof(ItemController);
object expectedApiDescriptions = new List<object>
{
new { HttpMethod = HttpMethod.Get, RelativePath = "Item/GetItem?name={name}&series={series}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Post, RelativePath = "Item/PostItem", HasRequestFormatters = true, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Put, RelativePath = "Item/PostItem", HasRequestFormatters = true, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Delete, RelativePath = "Item/RemoveItem/{id}", HasRequestFormatters = false, HasResponseFormatters = false, NumberOfParameters = 1}
};
yield return new[] { controllerType, expectedApiDescriptions };
controllerType = typeof(OverloadsController);
expectedApiDescriptions = new List<object>
{
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/Get/{id}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/Get", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 0},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/Get?name={name}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/GetPersonByNameAndId/{id}?name={name}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/GetPersonByNameAndAge?name={name}&age={age}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/GetPersonByNameAgeAndSsn?name={name}&age={age}&ssn={ssn}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 3},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/GetPersonByNameIdAndSsn/{id}?name={name}&ssn={ssn}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 3},
new { HttpMethod = HttpMethod.Get, RelativePath = "Overloads/GetPersonByNameAndSsn?name={name}&ssn={ssn}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Post, RelativePath = "Overloads/Post", HasRequestFormatters = true, HasResponseFormatters = true, NumberOfParameters = 1},
new { HttpMethod = HttpMethod.Post, RelativePath = "Overloads/ActionDefaultedToPost?name={name}&age={age}", HasRequestFormatters = false, HasResponseFormatters = true, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Delete, RelativePath = "Overloads/Delete/{id}?name={name}", HasRequestFormatters = false, HasResponseFormatters = false, NumberOfParameters = 2},
new { HttpMethod = HttpMethod.Delete, RelativePath = "Overloads/Delete/{id}?name={name}&age={age}", HasRequestFormatters = false, HasResponseFormatters = false, NumberOfParameters = 3}
};
yield return new[] { controllerType, expectedApiDescriptions };
}
}
[Theory]
[PropertyData("VerifyDescription_OnRouteWithActionVariable_PropertyData")]
public void VerifyDescription_OnRouteWithActionVariable(Type controllerType, List<object> expectedResults)
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{action}/{id}", new { id = RouteParameter.Optional });
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, controllerType);
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
ApiExplorerHelper.VerifyApiDescriptions(explorer.ApiDescriptions, expectedResults);
}
public static IEnumerable<object[]> VerifyDescription_On_RouteWithActionOnDefaults_PropertyData
{
get
{
object controllerType = typeof(ItemController);
object expectedApiDescriptions = new List<object>
{
new { HttpMethod = HttpMethod.Delete, RelativePath = "Item/{id}", HasRequestFormatters = false, HasResponseFormatters = false, NumberOfParameters = 1}
};
yield return new[] { controllerType, expectedApiDescriptions };
}
}
[Theory]
[PropertyData("VerifyDescription_On_RouteWithActionOnDefaults_PropertyData")]
public void VerifyDescription_On_RouteWithActionOnDefaults(Type controllerType, List<object> expectedResults)
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{id}", new { action = "RemoveItem", id = RouteParameter.Optional });
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, controllerType);
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
ApiExplorerHelper.VerifyApiDescriptions(explorer.ApiDescriptions, expectedResults);
}
[Fact]
public void InvalidActionNameOnRoute_DoesNotThrow()
{
Type controllerType = typeof(OverloadsController);
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "{controller}/{id}", new { action = "ActionThatDoesNotExist", id = RouteParameter.Optional });
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, controllerType);
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
Assert.Empty(explorer.ApiDescriptions);
}
[Fact]
public void InvalidControllerNameOnRoute_DoesNotThrow()
{
Type controllerType = typeof(OverloadsController);
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Default", "mycontroller/{id}", new { controller = "ControllerThatDoesNotExist", id = RouteParameter.Optional });
DefaultHttpControllerSelector controllerSelector = ApiExplorerHelper.GetStrictControllerSelector(config, controllerType);
config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector);
IApiExplorer explorer = config.Services.GetApiExplorer();
Assert.Empty(explorer.ApiDescriptions);
}
}
}