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,141 @@
// 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.Controllers;
using Xunit.Extensions;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Web.Http
{
[CLSCompliant(false)]
public class ActionAttributesTest
{
[Theory]
[InlineData("GET", "ActionAttributeTest/RetriveUsers", "RetriveUsers")]
[InlineData("POST", "ActionAttributeTest/AddUsers", "AddUsers")]
[InlineData("PUT", "ActionAttributeTest/UpdateUsers", "UpdateUsers")]
[InlineData("DELETE", "ActionAttributeTest/DeleteUsers", "DeleteUsers")]
[InlineData("PATCH", "ActionAttributeTest/Users", "Users")]
[InlineData("HEAD", "ActionAttributeTest/Users", "Users")]
public void SelectAction_OnRouteWithActionParameter(string httpMethod, string requestUrl, string expectedActionName)
{
string routeUrl = "{controller}/{action}/{id}";
object routeDefault = new { id = RouteParameter.Optional };
HttpControllerContext controllerContext = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(ActionAttributeTestController));
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(controllerContext);
Assert.Equal<string>(expectedActionName, descriptor.ActionName);
}
[Theory]
[InlineData("POST", "ActionAttributeTest/RetriveUsers")]
[InlineData("DELETE", "ActionAttributeTest/RetriveUsers")]
[InlineData("WHATEVER", "ActionAttributeTest/RetriveUsers")]
[InlineData("GET", "ActionAttributeTest/AddUsers")]
[InlineData("PUT", "ActionAttributeTest/AddUsers")]
[InlineData("WHATEVER", "ActionAttributeTest/AddUsers")]
[InlineData("GET", "ActionAttributeTest/UpdateUsers")]
[InlineData("WHATEVER", "ActionAttributeTest/UpdateUsers")]
[InlineData("POST", "ActionAttributeTest/DeleteUsers")]
[InlineData("DELETEME", "ActionAttributeTest/DeleteUsers")]
[InlineData("GET", "ActionAttributeTest/Users")]
[InlineData("POST", "ActionAttributeTest/Users")]
[InlineData("PATCHING", "ActionAttributeTest/Users")]
[InlineData("NonAction", "ActionAttributeTest/NonAction")]
[InlineData("GET", "ActionAttributeTest/NonAction")]
public void SelectAction_ThrowsMethodNotSupported_OnRouteWithActionParameter(string httpMethod, string requestUrl)
{
string routeUrl = "{controller}/{action}/{id}";
object routeDefault = new { id = RouteParameter.Optional };
HttpControllerContext controllerContext = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
Type controllerType = typeof(ActionAttributeTestController);
controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, controllerType.Name, controllerType);
var exception = Assert.Throws<HttpResponseException>(() =>
{
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(controllerContext);
});
Assert.Equal(HttpStatusCode.MethodNotAllowed, exception.Response.StatusCode);
var content = Assert.IsType<ObjectContent<string>>(exception.Response.Content);
Assert.Equal("The requested resource does not support http method '" + httpMethod + "'.", content.Value);
}
[Theory]
[InlineData("POST", "ActionAttributeTest/NonAction")]
[InlineData("ACTION", "ActionAttributeTest/NonActionWitHttpMethod")]
public void SelectAction_ThrowsNotFound_OnRouteWithActionParameter(string httpMethod, string requestUrl)
{
string routeUrl = "{controller}/{action}/{id}";
object routeDefault = new { id = RouteParameter.Optional };
HttpControllerContext controllerContext = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
Type controllerType = typeof(ActionAttributeTestController);
controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, controllerType.Name, controllerType);
var exception = Assert.Throws<HttpResponseException>(() =>
{
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(controllerContext);
});
Assert.Equal(HttpStatusCode.NotFound, exception.Response.StatusCode);
var content = Assert.IsType<ObjectContent<string>>(exception.Response.Content);
Assert.Equal("No action was found on the controller 'ActionAttributeTestController' that matches the request.", content.Value);
}
[Theory]
[InlineData("GET", "ActionAttributeTest/", "RetriveUsers")]
[InlineData("GET", "ActionAttributeTest/3", "RetriveUsers")]
[InlineData("GET", "ActionAttributeTest/4?ssn=12345", "RetriveUsers")]
[InlineData("POST", "ActionAttributeTest/1", "AddUsers")]
[InlineData("PUT", "ActionAttributeTest", "UpdateUsers")]
[InlineData("PUT", "ActionAttributeTest/4", "UpdateUsers")]
[InlineData("PUT", "ActionAttributeTest/4?extra=thing", "UpdateUsers")]
[InlineData("DELETE", "ActionAttributeTest", "DeleteUsers")]
[InlineData("PATCH", "ActionAttributeTest", "PatchUsers")]
[InlineData("HEAD", "ActionAttributeTest/", "Head")]
[InlineData("PATCH", "ActionAttributeTest?key=2", "Users")]
[InlineData("HEAD", "ActionAttributeTest?key=2", "Users")]
[InlineData("OPTIONS", "ActionAttributeTest", "Options")]
[InlineData("PATCH", "ActionAttributeTest/2", "Update")]
[InlineData("HEAD", "ActionAttributeTest/2", "Ping")]
[InlineData("OPTIONS", "ActionAttributeTest/2", "Help")]
public void SelectAction_OnDefaultRoute(string httpMethod, string requestUrl, string expectedActionName)
{
string routeUrl = "{controller}/{id}";
object routeDefault = new { id = RouteParameter.Optional };
HttpControllerContext controllerContext = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "test", typeof(ActionAttributeTestController));
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(controllerContext);
Assert.Equal<string>(expectedActionName, descriptor.ActionName);
}
[Theory]
[InlineData("CONNECT", "ActionAttributeTest")]
[InlineData("TRACE", "ActionAttributeTest")]
[InlineData("NonAction", "ActionAttributeTest/")]
[InlineData("DENY", "ActionAttributeTest")]
[InlineData("APP", "ActionAttributeTest")]
public void SelectAction_ThrowsMethodNotSupported_OnDefaultRoute(string httpMethod, string requestUrl)
{
string routeUrl = "{controller}/{id}";
object routeDefault = new { id = RouteParameter.Optional };
HttpControllerContext controllerContext = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
Type controllerType = typeof(ActionAttributeTestController);
controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, controllerType.Name, controllerType);
var exception = Assert.Throws<HttpResponseException>(() =>
{
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(controllerContext);
});
Assert.Equal(HttpStatusCode.MethodNotAllowed, exception.Response.StatusCode);
var content = Assert.IsType<ObjectContent<string>>(exception.Response.Content);
Assert.Equal("The requested resource does not support http method '" + httpMethod + "'.", content.Value);
}
}
}

View File

@ -0,0 +1,111 @@
// 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 Xunit.Extensions;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Web.Http
{
public class ActionReachabilityTest
{
[Theory]
[InlineData("GET", "Users", HttpStatusCode.OK, "GetUser")]
[InlineData("UPDATE", "Users", HttpStatusCode.OK, "PutUser")]
[InlineData("DELETE", "Users", HttpStatusCode.OK, "Remove")]
[InlineData("OPTIONS", "Users", HttpStatusCode.OK, "Assist")]
[InlineData("PUT", "Users", HttpStatusCode.OK, "PutUserWithEmptyName")]
[InlineData("POST", "Users", HttpStatusCode.InternalServerError, "")] // InternalServerError because of ambiguous match, there're multiple POST actions given that every action is POST by default
[InlineData("POST", "Users/Approve", HttpStatusCode.NotFound, "")] // NotFound because it doesn't match the route
[InlineData("DELETE", "Users/Remove", HttpStatusCode.NotFound, "")] // NotFound because it doesn't match the route
[InlineData("POST", "Users/DefaultActionWithEmptyActionName", HttpStatusCode.NotFound, "")] // NotFound because it doesn't match the route
public void ActionReachability_UsingResourceOrientedRoute(string httpMethod, string requestUrl, HttpStatusCode expectedStatusCode, string expectedActionName)
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("REST", "{controller}");
HttpServer server = new HttpServer(config);
HttpClient client = new HttpClient(server);
HttpRequestMessage request = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/" + requestUrl);
HttpResponseMessage response = client.SendAsync(request).Result;
Assert.Equal(expectedStatusCode, response.StatusCode);
if (response.StatusCode == HttpStatusCode.OK)
{
Assert.Equal(expectedActionName, response.Content.ReadAsAsync<string>().Result);
}
}
[Theory]
[InlineData("GET", "Users/GetUser", HttpStatusCode.OK, "GetUser")]
[InlineData("POST", "Users/Approve", HttpStatusCode.OK, "Approve")]
[InlineData("UPDATE", "Users/PutUser", HttpStatusCode.OK, "PutUser")]
[InlineData("POST", "Users/UpdateUser", HttpStatusCode.OK, "PostUser")]
[InlineData("PATCH", "Users/ReplaceUser", HttpStatusCode.OK, "DeleteUser")]
[InlineData("DELETE", "Users/Remove", HttpStatusCode.OK, "Remove")]
[InlineData("POST", "Users/Reject", HttpStatusCode.OK, "Deny")]
[InlineData("OPTIONS", "Users/Help", HttpStatusCode.OK, "Assist")]
[InlineData("POST", "Users/GetUser", HttpStatusCode.MethodNotAllowed, "")] // MethodNotAllowed because the convention implies it's a GET
[InlineData("GET", "Users", HttpStatusCode.NotFound, "")] // NotFound because it doesn't match the route
[InlineData("PUT", "Users", HttpStatusCode.NotFound, "")] // NotFound because it doesn't match the route
[InlineData("PUT", "Users/PutUserWithEmptyName", HttpStatusCode.NotFound, "")] // NotFound because the action has an empty name and it's not reachable through {action}
[InlineData("POST", "Users/PostUser", HttpStatusCode.NotFound, "")] // NotFound because the action name is renamed to UpdateUser
[InlineData("GET", "Users/Approve", HttpStatusCode.MethodNotAllowed, "")] // MethodNotAllowed because only POST is allowed by default for action that has no HttpMethd declared or implied
[InlineData("POST", "Users/Remove", HttpStatusCode.MethodNotAllowed, "")] // MethodNotAllowed because the action has the attribute HttpDelete
[InlineData("POST", "Users/DefaultActionWithEmptyActionName", HttpStatusCode.NotFound, "")] // NotFound because the action has an empty name and it's not reachable through {action}
public void ActionReachability_UsingRpcStyleRoute(string httpMethod, string requestUrl, HttpStatusCode expectedStatusCode, string expectedActionName)
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("RPC", "{controller}/{action}");
HttpServer server = new HttpServer(config);
HttpClient client = new HttpClient(server);
HttpRequestMessage request = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/" + requestUrl);
HttpResponseMessage response = client.SendAsync(request).Result;
Assert.Equal(expectedStatusCode, response.StatusCode);
if (response.StatusCode == HttpStatusCode.OK)
{
Assert.Equal(expectedActionName, response.Content.ReadAsAsync<string>().Result);
}
}
[Theory]
[InlineData("GET", "Users/GetUser", HttpStatusCode.OK, "GetUser")]
[InlineData("POST", "Users/Approve", HttpStatusCode.OK, "Approve")]
[InlineData("UPDATE", "Users/PutUser", HttpStatusCode.OK, "PutUser")]
[InlineData("POST", "Users/UpdateUser", HttpStatusCode.OK, "PostUser")]
[InlineData("PATCH", "Users/ReplaceUser", HttpStatusCode.OK, "DeleteUser")]
[InlineData("DELETE", "Users/Remove", HttpStatusCode.OK, "Remove")]
[InlineData("POST", "Users/Reject", HttpStatusCode.OK, "Deny")]
[InlineData("OPTIONS", "Users/Help", HttpStatusCode.OK, "Assist")]
[InlineData("POST", "Users", HttpStatusCode.OK, "DefaultActionWithEmptyActionName")]
[InlineData("PUT", "Users", HttpStatusCode.OK, "PutUserWithEmptyName")]
[InlineData("GET", "Users", HttpStatusCode.MethodNotAllowed, "")] // MethodNotAllowed because GetUser doesn't have the ActionName="" so it's not reachable
[InlineData("UPDATE", "Users", HttpStatusCode.MethodNotAllowed, "")] // MethodNotAllowed because GetUser doesn't have the ActionName="" so it's not reachable
[InlineData("DELETE", "Users", HttpStatusCode.MethodNotAllowed, "Remove")] // MethodNotAllowed because GetUser doesn't have the ActionName="" so it's not reachable
[InlineData("OPTIONS", "Users", HttpStatusCode.MethodNotAllowed, "")] // MethodNotAllowed because GetUser doesn't have the ActionName="" so it's not reachable
[InlineData("PUT", "Users/PutUserWithEmptyName", HttpStatusCode.NotFound, "")] // NotFound because the action has an empty name and it's not reachable through {action}
[InlineData("POST", "Users/GetUser", HttpStatusCode.MethodNotAllowed, "")] // MethodNotAllowed because the convention implies it's a GET
[InlineData("POST", "Users/PostUser", HttpStatusCode.NotFound, "")] // NotFound because the action name is renamed to UpdateUser
[InlineData("GET", "Users/Approve", HttpStatusCode.MethodNotAllowed, "")] // MethodNotAllowed because only POST is allowed by default for action that has no HttpMethd declared or implied
[InlineData("POST", "Users/Remove", HttpStatusCode.MethodNotAllowed, "")] // MethodNotAllowed because the action has the attribute HttpDelete
[InlineData("POST", "Users/DefaultActionWithEmptyActionName", HttpStatusCode.NotFound, "")] // NotFound because the ActionName="" and no HttpMethd is declared or implied
public void ActionReachability_UsingResourceAndRpcStyleRoutes(string httpMethod, string requestUrl, HttpStatusCode expectedStatusCode, string expectedActionName)
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("Hybrid", "{controller}/{action}", new { action = "" });
HttpServer server = new HttpServer(config);
HttpClient client = new HttpClient(server);
HttpRequestMessage request = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/" + requestUrl);
HttpResponseMessage response = client.SendAsync(request).Result;
Assert.Equal(expectedStatusCode, response.StatusCode);
if (response.StatusCode == HttpStatusCode.OK)
{
Assert.Equal(expectedActionName, response.Content.ReadAsAsync<string>().Result);
}
}
}
}

View File

@ -0,0 +1,221 @@
// 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.Controllers;
using Xunit;
using Xunit.Extensions;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Web.Http
{
[CLSCompliant(false)]
public class ApiControllerActionSelectorTest
{
[Theory]
[InlineData("GET", "Test", "GetUsers")]
[InlineData("GET", "Test/2", "GetUser")]
[InlineData("GET", "Test/3?name=mario", "GetUserByNameAndId")]
[InlineData("GET", "Test/3?name=mario&ssn=123456", "GetUserByNameIdAndSsn")]
[InlineData("GET", "Test?name=mario&ssn=123456", "GetUserByNameAndSsn")]
[InlineData("GET", "Test?name=mario&ssn=123456&age=3", "GetUserByNameAgeAndSsn")]
[InlineData("GET", "Test/4?name=mario&age=20", "GetUserByNameAndId")]
[InlineData("GET", "Test/5?random=9", "GetUser")]
[InlineData("Post", "Test", "PostUser")]
[InlineData("Post", "Test?name=mario&age=10", "PostUserByNameAndAge")]
/// Note: Normally the following would not match DeleteUserByIdAndOptName because it has 'id' and 'age' as parameters while the DeleteUserByIdAndOptName action has 'id' and 'name'.
/// However, because the default value is provided on action parameter 'name', having the 'id' in the request was enough to match the action.
[InlineData("Delete", "Test/6?age=10", "DeleteUserByIdAndOptName")]
public void Route_Parameters_Default(string httpMethod, string requestUrl, string expectedActionName)
{
string routeUrl = "{controller}/{id}";
object routeDefault = new { id = RouteParameter.Optional };
HttpControllerContext context = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
context.ControllerDescriptor = new HttpControllerDescriptor(context.Configuration, "test", typeof(TestController));
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(context);
Assert.Equal(expectedActionName, descriptor.ActionName);
}
[Theory]
[InlineData("GET", "Test", "GetUsers")]
[InlineData("GET", "Test/2", "GetUsersByName")]
[InlineData("GET", "Test/luigi?ssn=123456", "GetUserByNameAndSsn")]
[InlineData("GET", "Test/luigi?ssn=123456&id=2&ssn=12345", "GetUserByNameIdAndSsn")]
[InlineData("GET", "Test?age=10&ssn=123456", "GetUsers")]
[InlineData("GET", "Test?id=3&ssn=123456&name=luigi", "GetUserByNameIdAndSsn")]
[InlineData("POST", "Test/luigi?age=20", "PostUserByNameAndAge")]
public void Route_Parameters_Non_Id(string httpMethod, string requestUrl, string expectedActionName)
{
string routeUrl = "{controller}/{name}";
object routeDefault = new { name = RouteParameter.Optional };
HttpControllerContext context = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
context.ControllerDescriptor = new HttpControllerDescriptor(context.Configuration, "test", typeof(TestController));
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(context);
Assert.Equal(expectedActionName, descriptor.ActionName);
}
[Theory]
[InlineData("GET", "Test/3?NAME=mario", "GetUserByNameAndId")]
[InlineData("GET", "Test/3?name=mario&SSN=123456", "GetUserByNameIdAndSsn")]
[InlineData("GET", "Test?nAmE=mario&ssn=123456&AgE=3", "GetUserByNameAgeAndSsn")]
[InlineData("Delete", "Test/6?AGe=10", "DeleteUserByIdAndOptName")]
public void Route_Parameters_Casing(string httpMethod, string requestUrl, string expectedActionName)
{
string routeUrl = "{controller}/{ID}";
object routeDefault = new { id = RouteParameter.Optional };
HttpControllerContext context = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
context.ControllerDescriptor = new HttpControllerDescriptor(context.Configuration, "test", typeof(TestController));
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(context);
Assert.Equal(expectedActionName, descriptor.ActionName);
}
[Theory]
[InlineData("GET", "Test/GetUsers", "GetUsers")]
[InlineData("GET", "Test/GetUser", "GetUser")]
[InlineData("GET", "Test/GetUser?id=3", "GetUser")]
[InlineData("GET", "Test/GetUser/4?id=3", "GetUser")]
[InlineData("GET", "Test/GetUserByNameAgeAndSsn", "GetUserByNameAgeAndSsn")]
[InlineData("GET", "Test/GetUserByNameAndSsn", "GetUserByNameAndSsn")]
[InlineData("POST", "Test/PostUserByNameAndAddress", "PostUserByNameAndAddress")]
public void Route_Action(string httpMethod, string requestUrl, string expectedActionName)
{
string routeUrl = "{controller}/{action}/{id}";
object routeDefault = new { id = RouteParameter.Optional };
HttpControllerContext context = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
context.ControllerDescriptor = new HttpControllerDescriptor(context.Configuration, "test", typeof(TestController));
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(context);
Assert.Equal(expectedActionName, descriptor.ActionName);
}
[Theory]
[InlineData("GET", "Test/getusers", "GetUsers")]
[InlineData("GET", "Test/getuseR", "GetUser")]
[InlineData("GET", "Test/Getuser?iD=3", "GetUser")]
[InlineData("GET", "Test/GetUser/4?Id=3", "GetUser")]
[InlineData("GET", "Test/GetUserByNameAgeandSsn", "GetUserByNameAgeAndSsn")]
[InlineData("GET", "Test/getUserByNameAndSsn", "GetUserByNameAndSsn")]
[InlineData("POST", "Test/PostUserByNameAndAddress", "PostUserByNameAndAddress")]
public void Route_Action_Name_Casing(string httpMethod, string requestUrl, string expectedActionName)
{
string routeUrl = "{controller}/{action}/{id}";
object routeDefault = new { id = RouteParameter.Optional };
HttpControllerContext context = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
context.ControllerDescriptor = new HttpControllerDescriptor(context.Configuration, "test", typeof(TestController));
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(context);
Assert.Equal(expectedActionName, descriptor.ActionName);
}
[Theory]
[InlineData("GET", "Test", "GetUsers")]
[InlineData("GET", "Test/?name=peach", "GetUsersByName")]
[InlineData("GET", "Test?name=peach", "GetUsersByName")]
[InlineData("GET", "Test?name=peach&ssn=123456", "GetUserByNameAndSsn")]
[InlineData("GET", "Test?name=peach&ssn=123456&age=3", "GetUserByNameAgeAndSsn")]
public void Route_No_Action(string httpMethod, string requestUrl, string expectedActionName)
{
string routeUrl = "{controller}";
HttpControllerContext context = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl);
context.ControllerDescriptor = new HttpControllerDescriptor(context.Configuration, "test", typeof(TestController));
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(context);
Assert.Equal(expectedActionName, descriptor.ActionName);
}
[Theory]
[InlineData("GET", "ParameterAttribute/2", "GetUser")]
[InlineData("GET", "ParameterAttribute?id=2", "GetUser")]
[InlineData("GET", "ParameterAttribute?myId=2", "GetUserByMyId")]
[InlineData("POST", "ParameterAttribute/3?name=user", "PostUserNameFromUri")]
[InlineData("POST", "ParameterAttribute/3", "PostUserNameFromBody")]
[InlineData("DELETE", "ParameterAttribute/3?name=user", "DeleteUserWithNullableIdAndName")]
[InlineData("DELETE", "ParameterAttribute?address=userStreet", "DeleteUser")]
public void ModelBindingParameterAttribute_AreAppliedWhenSelectingActions(string httpMethod, string requestUrl, string expectedActionName)
{
string routeUrl = "{controller}/{id}";
object routeDefault = new { id = RouteParameter.Optional };
HttpControllerContext context = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
context.ControllerDescriptor = new HttpControllerDescriptor(context.Configuration, "ParameterAttribute", typeof(ParameterAttributeController));
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(context);
Assert.Equal(expectedActionName, descriptor.ActionName);
}
[Fact]
public void RequestToAmbiguousAction_OnDefaultRoute()
{
string routeUrl = "{controller}/{id}";
object routeDefault = new { id = RouteParameter.Optional };
string httpMethod = "Post";
string requestUrl = "Test?name=mario";
// This would result in ambiguous match because complex parameter is not considered for matching.
// Therefore, PostUserByNameAndAddress(string name, Address address) would conflicts with PostUserByName(string name)
Assert.Throws<HttpResponseException>(() =>
{
HttpControllerContext context = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
context.ControllerDescriptor = new HttpControllerDescriptor(context.Configuration, "test", typeof(TestController));
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(context);
});
}
[Fact]
public void RequestToActionWithNotSupportedHttpMethod_OnRouteWithAction()
{
string routeUrl = "{controller}/{action}/{id}";
object routeDefault = new { id = RouteParameter.Optional };
string requestUrl = "Test/GetUsers";
string httpMethod = "POST";
var exception = Assert.Throws<HttpResponseException>(() =>
{
HttpControllerContext context = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
context.ControllerDescriptor = new HttpControllerDescriptor(context.Configuration, "test", typeof(TestController));
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(context);
});
Assert.Equal(HttpStatusCode.MethodNotAllowed, exception.Response.StatusCode);
var content = Assert.IsType<ObjectContent<string>>(exception.Response.Content);
Assert.Equal("The requested resource does not support http method 'POST'.", content.Value);
}
[Fact]
public void RequestToActionWith_HttpMethodDefinedByAttributeAndActionName()
{
string routeUrl = "{controller}/{id}";
object routeDefault = new { id = RouteParameter.Optional };
string requestUrl = "Test";
string httpMethod = "PATCH";
HttpControllerContext context = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
context.ControllerDescriptor = new HttpControllerDescriptor(context.Configuration, "test", typeof(TestController));
HttpActionDescriptor descriptor = ApiControllerHelper.SelectAction(context);
Assert.Equal<string>("PutUser", descriptor.ActionName);
// When you have the HttpMethod attribute, the convention should not be applied.
httpMethod = "PUT";
var exception = Assert.Throws<HttpResponseException>(() =>
{
context = ApiControllerHelper.CreateControllerContext(httpMethod, requestUrl, routeUrl, routeDefault);
context.ControllerDescriptor = new HttpControllerDescriptor(context.Configuration, "test", typeof(TestController));
ApiControllerHelper.SelectAction(context);
});
Assert.Equal(HttpStatusCode.MethodNotAllowed, exception.Response.StatusCode);
var content = Assert.IsType<ObjectContent<string>>(exception.Response.Content);
Assert.Equal("The requested resource does not support http method 'PUT'.", content.Value);
}
}
}

View File

@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http
{
public class ActionAttributeTestController : ApiController
{
[HttpGet]
public void RetriveUsers() { }
[HttpPost]
public void AddUsers(int id) { }
[HttpPut]
public void UpdateUsers(User user) { }
[HttpDelete]
[ActionName("DeleteUsers")]
public void RemoveUsers(string name) { }
[HttpOptions]
public void Help(int id) { }
[HttpHead]
public void Ping(int id) { }
[HttpPatch]
public void Update(int id) { }
[AcceptVerbs("PATCH", "HEAD")]
[CLSCompliant(false)]
public void Users(double key) { }
[NonAction]
public void NonAction() { }
[NonAction]
[AcceptVerbs("ACTION")]
public void NonActionWitHttpMethod() { }
public void Options() { }
public void Head() { }
public void PatchUsers() { }
}
}

View File

@ -0,0 +1,16 @@
// 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
{
public class ParameterAttributeController : ApiController
{
public User GetUserByMyId(int myId) { return null; }
public User GetUser([FromUri(Name = "id")] int myId) { return null; }
public List<User> PostUserNameFromUri(int id, [FromUri]string name) { return null; }
public List<User> PostUserNameFromBody(int id, [FromBody] string name) { return null; }
public void DeleteUserWithNullableIdAndName(int? id, string name) { }
public void DeleteUser(string address) { }
}
}

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.Collections.Generic;
namespace System.Web.Http
{
public class TestController : ApiController
{
public User GetUser(int id) { return null; }
public List<User> GetUsers() { return null; }
public List<User> GetUsersByName(string name) { return null; }
[AcceptVerbs("PATCH")]
public void PutUser(User user) { }
public User GetUserByNameAndId(string name, int id) { return null; }
public User GetUserByNameAndAge(string name, int age) { return null; }
public User GetUserByNameAgeAndSsn(string name, int age, int ssn) { return null; }
public User GetUserByNameIdAndSsn(string name, int id, int ssn) { return null; }
public User GetUserByNameAndSsn(string name, int ssn) { return null; }
public User PostUser(User user) { return null; }
public User PostUserByNameAndAge(string name, int age) { return null; }
public User PostUserByName(string name) { return null; }
public User PostUserByNameAndAddress(string name, UserAddress address) { return null; }
public User DeleteUserByIdAndOptName(int id, string name = "DefaultName") { return null; }
public User DeleteUserByIdNameAndAge(int id, string name, int age) { return null; }
}
}

View File

@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http
{
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}

View File

@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http
{
public class UserAddress
{
public string Street;
public int ZipCode;
}
}

View File

@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http
{
public class UsersController : ApiController
{
// Undecorated action, following convention
public string GetUser() { return "GetUser"; }
// Undecorated action, not following conventions
public string Approve() { return "Approve"; }
// Action decorated with Verb only, following conventions
[AcceptVerbs("UPDATE")]
public string PutUser() { return "PutUser"; }
// Action decorated with Name = "" only, following conventions, not reachable by {action}
[ActionName("")]
public string PutUserWithEmptyName() { return "PutUserWithEmptyName"; }
// Action decorated with Name = "" only, not following conventions, it's a POST by default and not reachable by {action}
[ActionName("")]
public string DefaultActionWithEmptyActionName() { return "DefaultActionWithEmptyActionName"; }
// Action decorated with Name only, following conventions
[ActionName("UpdateUser")]
public string PostUser() { return "PostUser"; }
// Action decorated with both, following conventions
[AcceptVerbs("PATCH")]
[ActionName("ReplaceUser")]
public string DeleteUser() { return "DeleteUser"; }
// Action decorated with Verb only, not following conventions
[HttpDelete]
public string Remove() { return "Remove"; }
// Action decorated with Name only, not following conventions
[ActionName("Reject")]
public string Deny() { return "Deny"; }
// Action decorated with both, not following conventions
[AcceptVerbs("OPTIONS")]
[ActionName("Help")]
public string Assist() { return "Assist"; }
}
}

View File

@ -0,0 +1,55 @@
// 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.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Routing;
namespace System.Web.Http
{
public class ApiControllerHelper
{
public static HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
ApiControllerActionSelector selector = new ApiControllerActionSelector();
HttpActionDescriptor descriptor = selector.SelectAction(controllerContext);
return descriptor;
}
public static HttpControllerContext CreateControllerContext(string httpMethod, string requestUrl, string routeUrl, object routeDefault = null)
{
string baseAddress = "http://localhost/";
HttpConfiguration config = new HttpConfiguration();
HttpRoute route = routeDefault != null ? new HttpRoute(routeUrl, new HttpRouteValueDictionary(routeDefault)) : new HttpRoute(routeUrl);
config.Routes.Add("test", route);
HttpRequestMessage request = new HttpRequestMessage(HttpMethodHelper.GetHttpMethod(httpMethod), baseAddress + requestUrl);
IHttpRouteData routeData = config.Routes.GetRouteData(request);
if (routeData == null)
{
throw new InvalidOperationException("Could not dispatch to controller based on the route.");
}
RemoveOptionalRoutingParameters(routeData.Values);
HttpControllerContext controllerContext = ContextUtil.CreateControllerContext(config, routeData, request);
return controllerContext;
}
private static void RemoveOptionalRoutingParameters(IDictionary<string, object> routeValueDictionary)
{
// Get all keys for which the corresponding value is 'Optional'.
// ToArray() necessary so that we don't manipulate the dictionary while enumerating.
string[] matchingKeys = (from entry in routeValueDictionary
where entry.Value == RouteParameter.Optional
select entry.Key).ToArray();
foreach (string key in matchingKeys)
{
routeValueDictionary.Remove(key);
}
}
}
}