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);
}
}
}

View File

@ -0,0 +1,76 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Net;
using System.Net.Http;
using System.Web.Http.SelfHost;
using Xunit;
namespace System.Web.Http
{
public class BasicOverHttpTest
{
private static readonly string BaseAddress = "http://localhost:8080";
[Fact]
public void AuthenticateWithUsernameTokenSucceed()
{
RunBasicAuthTest("Sample", "", new NetworkCredential("username", "password"),
(response) => Assert.Equal(HttpStatusCode.OK, response.StatusCode)
);
}
[Fact]
public void AuthenticateWithWrongPasswordFail()
{
RunBasicAuthTest("Sample", "", new NetworkCredential("username", "wrong password"),
(response) => Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode)
);
}
[Fact]
public void AuthenticateWithNoCredentialFail()
{
RunBasicAuthTest("Sample", "", null,
(response) => Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode)
);
}
private static void RunBasicAuthTest(string controllerName, string routeSuffix, NetworkCredential credential, Action<HttpResponseMessage> assert)
{
// Arrange
HttpSelfHostConfiguration config = new HttpSelfHostConfiguration(BaseAddress);
config.Routes.MapHttpRoute("Default", "{controller}" + routeSuffix, new { controller = controllerName });
config.UserNamePasswordValidator = new CustomUsernamePasswordValidator();
config.MessageHandlers.Add(new CustomMessageHandler());
HttpSelfHostServer server = new HttpSelfHostServer(config);
server.OpenAsync().Wait();
// Create a GET request with correct username and password
HttpClientHandler handler = new HttpClientHandler();
handler.Credentials = credential;
HttpClient client = new HttpClient(handler);
HttpResponseMessage response = null;
try
{
// Act
response = client.GetAsync(BaseAddress).Result;
// Assert
assert(response);
}
finally
{
if (response != null)
{
response.Dispose();
}
client.Dispose();
}
server.CloseAsync().Wait();
}
}
}

View File

@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Net.Http;
using System.Security.Principal;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.SelfHost;
namespace System.Web.Http
{
public class CustomMessageHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
IPrincipal originalPrincipal = Thread.CurrentPrincipal;
// here you can see the requestor's identity via the request message
// convert the Generic Identity to some IPrincipal object, and set it in the request's property
// later the authorization filter will use the role information to authorize request.
SecurityMessageProperty property = request.GetSecurityMessageProperty();
if (property != null)
{
ServiceSecurityContext context = property.ServiceSecurityContext;
if (context.PrimaryIdentity.Name == "username")
{
Thread.CurrentPrincipal = new GenericPrincipal(context.PrimaryIdentity, new string[] { "Administrators" });
}
}
return base.SendAsync(request, cancellationToken)
.Finally(() => Thread.CurrentPrincipal = originalPrincipal);
}
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.IdentityModel.Selectors;
namespace System.Web.Http
{
public class CustomUsernamePasswordValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (userName == "username" && password == "password")
{
return;
}
else
{
throw new InvalidOperationException();
}
}
}
}

View File

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Net;
using System.Net.Http;
using System.Security.Principal;
using System.Threading;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace System.Web.Http
{
public class RequireAdminAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext context)
{
// do authorization based on the principle.
IPrincipal principal = Thread.CurrentPrincipal;
if (principal == null || !principal.IsInRole("Administrators"))
{
context.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
}
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http
{
/// <summary>
/// Sample ApiControler
/// </summary>
public class SampleController : ApiController
{
[RequireAdmin]
public string Get()
{
return "hello";
}
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Net.Http;
using System.Net.Http.Headers;
using Xunit;
using Xunit.Extensions;
namespace System.Web.Http.ContentNegotiation
{
public class AcceptHeaderTests : ContentNegotiationTestBase
{
[Theory]
[InlineData("application/json")]
[InlineData("application/xml")]
public void Response_Contains_ContentType(string contentType)
{
// Arrange
MediaTypeWithQualityHeaderValue requestContentType = new MediaTypeWithQualityHeaderValue(contentType);
MediaTypeHeaderValue responseContentType = null;
// Act
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, baseUri);
request.Headers.Accept.Add(requestContentType);
HttpResponseMessage response = httpClient.SendAsync(request).Result;
response.EnsureSuccessStatusCode();
responseContentType = response.Content.Headers.ContentType;
// Assert
Assert.Equal(requestContentType.MediaType, responseContentType.MediaType);
}
}
}

Some files were not shown because too many files have changed in this diff Show More