Custom method names in ASP.NET Web API
I'm converting from the WCF Web API to the new ASP.NET MVC 4 Web API. I have a UsersController, and I want to have a method named Authenticate. I see examples of how to do GetAll, GetOne, Post, and Delete, however what if I want to add extra methods into these services? For instance, my UsersService should have a method called Authenticate where they pass in a username and password, however it doesn't work.
public class UsersController : BaseApiController
{
public string GetAll()
{
return "getall!";
}
public string Get(int id)
{
return "get 1! " + id;
}
public User GetAuthenticate(string userName, string password, string applicationName)
{
LogWriter.Write(String.Format("Received authenticate request for username {0} and password {1} and application {2}",
userName, password, applicationName));
//check if valid leapfrog login.
var decodedUsername = userName.Replace("%40", "@");
var encodedPassword = password.Length > 0 ? Utility.HashString(password) : String.Empty;
var leapFrogUsers = LeapFrogUserData.FindAll(decodedUsername, encodedPassword);
if (leapFrogUsers.Count > 0)
{
return new User
{
Id = (uint)leapFrogUsers[0].Id,
Guid = leapFrogUsers[0].Guid
};
}
else
throw new HttpResponseException("Invalid login credentials");
}
}
I can browse to myapi/api/users/ and it will call GetAll and I can browse to myapi/api/users/1 and it will call Get, however if I call myapi/api/users/authenticate?username={0}&password={1} then it will call Get (NOT Authenticate) and error:
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.String Get(Int32)' in 'Navtrak.Services.WCF.NavtrakAPI.Controllers.UsersController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
How can I call custom method names such as Authenticate?
By default the route configuration follows RESTFul conventions meaning that it will accept only the Get, Post, Put and Delete action names (look at the route in global.asax => by default it doesn't allow you to specify any action name => it uses the HTTP verb to dispatch). So when you send a GET request to /api/users/authenticate
you are basically calling the Get(int id)
action and passing id=authenticate
which obviously crashes because your Get action expects an integer.
If you want to have different action names than the standard ones you could modify your route definition in global.asax
:
Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "get", id = RouteParameter.Optional }
);
Now you can navigate to /api/values/getauthenticate
to authenticate the user.
This is the best method I have come up with so far to incorporate extra GET methods while supporting the normal REST methods as well. Add the following routes to your WebApiConfig:
routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = @"d+" });
routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
routes.MapHttpRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new {action = "Post"}, new {httpMethod = new HttpMethodConstraint(HttpMethod.Post)});
I verified this solution with the test class below. I was able to successfully hit each method in my controller below:
public class TestController : ApiController
{
public string Get()
{
return string.Empty;
}
public string Get(int id)
{
return string.Empty;
}
public string GetAll()
{
return string.Empty;
}
public void Post([FromBody]string value)
{
}
public void Put(int id, [FromBody]string value)
{
}
public void Delete(int id)
{
}
}
I verified that it supports the following requests:
GET /Test
GET /Test/1
GET /Test/GetAll
POST /Test
PUT /Test/1
DELETE /Test/1
Note That if your extra GET actions do not begin with 'Get' you may want to add an HttpGet attribute to the method.
I am days into the MVC4 world.
For what its worth, I have a SitesAPIController, and I needed a custom method, that could be called like:
http://localhost:9000/api/SitesAPI/Disposition/0
With different values for the last parameter to get record with different dispositions.
What Finally worked for me was:
The method in the SitesAPIController:
// GET api/SitesAPI/Disposition/1
[ActionName("Disposition")]
[HttpGet]
public Site Disposition(int disposition)
{
Site site = db.Sites.Where(s => s.Disposition == disposition).First();
return site;
}
And this in the WebApiConfig.cs
// this was already there
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// this i added
config.Routes.MapHttpRoute(
name: "Action",
routeTemplate: "api/{controller}/{action}/{disposition}"
);
For as long as I was naming the {disposition} as {id} i was encountering:
{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:9000/api/SitesAPI/Disposition/0'.",
"MessageDetail": "No action was found on the controller 'SitesAPI' that matches the request."
}
When I renamed it to {disposition} it started working. So apparently the parameter name is matched with the value in the placeholder.
Feel free to edit this answer to make it more accurate/explanatory.
链接地址: http://www.djcxy.com/p/20534.html上一篇: ASP.NET Web API身份验证