2012-03-05 3 views
91

Я перехожу из веб-API WCF к новому веб-API ASP.NET MVC 4. У меня есть UserController, и я хочу иметь метод с именем Authenticate. Я вижу примеры того, как делать GetAll, GetOne, Post и Delete, однако что, если я хочу добавить дополнительные методы в эти сервисы? Например, у моего UserService должен быть метод под названием Authenticate, где они передают имя пользователя и пароль, однако это не работает.Пользовательские имена методов в веб-API ASP.NET

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

я могу перейти к MyAPI/API/пользователей/и он будет вызывать GETALL и я могу перейти к MyAPI/API/пользователей/1 и он будет вызывать Get, однако, если я позвоню MyAPI/API/пользователей/аутентификации имя пользователя = {0} & пароль = {1}, то он будет вызывать Get (НЕ аутентифицировать) и ошибка:

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.

Как я могу называть пользовательские имена методов, таких как Authenticate?

+0

См. Эту ссылку: 5-й ответ http://stackoverflow.com/questions/12775590/web-api-routing-for-multiple-get-methods-in-asp-net-mvc-4/40261803#40261803 –

ответ

110

По умолчанию конфигурация маршрута соответствует соглашениям RESTFul, означающим, что он будет принимать только имена действий Get, Post, Put и Delete (посмотрите на маршрут в global.asax => по умолчанию, он не позволяет вам указывать какие-либо action name => он использует HTTP-глагол для отправки). Поэтому, когда вы отправляете запрос GET на номер /api/users/authenticate, вы в основном вызываете действие Get(int id) и передаете id=authenticate, который, очевидно, падает, потому что ваше действие Get ожидает целое число.

Если вы хотите иметь различные имена действий, чем стандартные вы могли бы изменить ваше определение маршрута в global.asax:

Routes.MapHttpRoute(
    name: "DefaultApi", 
    routeTemplate: "api/{controller}/{action}/{id}", 
    defaults: new { action = "get", id = RouteParameter.Optional } 
); 

Теперь вы можете перейти к /api/values/getauthenticate для аутентификации пользователя.

+16

Есть ли способ заставить его по-прежнему использовать Get (id), Get() Put, Delete, Post, позволяя другим действиям? –

+0

@ShawnMclean Я предполагаю, что вы можете указать другой маршрут без '{action}', который имеет ограничение на '{id}', так что ничего, кроме 'int' или' Guid' (или что-то еще), не будет совпадать. Затем он должен уметь проваливаться до того, что было предложено Darin –

+0

. Еще одна важная вещь в том, что с этим стилем маршрутизации вы должны использовать атрибуты для указания допустимых HTTP-методов (например, [HttpGet]). –

78

Это самый лучший метод, который я придумал до сих пор, чтобы включить дополнительные методы GET, поддерживая нормальные методы REST, а также. Добавьте следующие маршруты к вашему 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)}); 

Я проверил это решение с тестовым классом ниже. Я был в состоянии успешно поразить каждый метод в моем контроллере ниже:

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

Я проверил, что он поддерживает следующие запросы:

GET /Test 
GET /Test/1 
GET /Test/GetAll 
POST /Test 
PUT /Test/1 
DELETE /Test/1 

Примечание, что если ваш дополнительный GET действия не начинаются с ' Получить 'вы можете добавить атрибут HttpGet к методу.

+1

Хорошее решение, не могли бы вы рассказать мне, если я настрою глаголы 'put' и' delete', такие как вы делали на 'get' и' post', тоже будут работать? –

+1

По-моему, это должно быть включено в значения по умолчанию для проектов WebAPI (возможно, закомментировано). Он дает вам маршруты стиля WebAPI и MVC в одно и то же время ... –

+1

@FelipeOriani, я не думаю, что вам нужно или нужно настроить 'put' или' delete' глаголы, поскольку эти запросы обычно сопровождают параметр id для идентификации ресурс, к которому вы хотите применить эту операцию.Вызов 'delete' на'/api/foo' должен вызывать ошибку, потому что какой foo вы пытаетесь удалить? Поэтому маршрут DefaultApiWithId должен обрабатывать эти случаи в порядке. – nwayve

14

Я дни в мире MVC4.

За что его стоит, у меня есть SitesAPIController, и мне нужен был специальный метод, который можно назвать как:

http://localhost:9000/api/SitesAPI/Disposition/0 

с различными значениями для последнего параметра, чтобы получить запись с различными диспозиции.

Что Наконец работал для меня было:

Метод в 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; 
} 

И это в 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}" 
); 

Для тех пор, как я именования {disposition} в качестве {id} i встречается:

{ 
"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." 
} 

Когда я переименовал его в {disposition}, он начал работать. Таким образом, по-видимому, имя параметра совпадает со значением в заполнителе.

Не стесняйтесь редактировать этот ответ, чтобы сделать его более точным/пояснительным.

+0

Спасибо за подсказку. Я делал ту же ошибку, что и вы. – abhi

+2

спасибо кинжал сэр за этот совет. –

12

Web Api по умолчанию ожидает URL в виде api/{controller}/{id}, чтобы переопределить эту маршрутизацию по умолчанию. вы можете настроить маршрутизацию любым из двух способов.

Первый вариант:

Добавить ниже регистрации маршрута в WebApiConfig.cs

config.Routes.MapHttpRoute(
    name: "CustomApi", 
    routeTemplate: "api/{controller}/{action}/{id}", 
    defaults: new { id = RouteParameter.Optional } 
); 

Украсьте свой метод действия с HttpGet и параметров, как показано ниже

[HttpGet] 
public HttpResponseMessage ReadMyData(string param1, 
         string param2, string param3) 

{ 

// your code here 

} 

для вызова метода выше URL будет выглядеть следующим образом

http://localhost:[yourport]/api/MyData/ReadMyData?param1=value1&param2=value2&param3=value3

Второй вариант Добавить префикс маршрута в класс контроллера и Украсьте свой метод действия с HttpGet, как показано ниже. В этом случае не нужно изменять любой WebApiConfig.cs. Он может иметь маршрутизацию по умолчанию.

[RoutePrefix("api/{controller}/{action}")] 
public class MyDataController : ApiController 
{ 

[HttpGet] 
public HttpResponseMessage ReadMyData(string param1, 
         string param2, string param3) 

{ 

// your code here 

} 

} 

для вызова метода выше URL будет, как показано ниже

http://localhost:[yourport]/api/MyData/ReadMyData?param1=value1&param2=value2&param3=value3

+0

Мне нравится второй вариант. Не могли бы вы также показать мне, как использовать его в VB.net? Большое спасибо. – user1617676

6

В случае, если вы используете ASP.NET 5 с ASP.NET MVC 6, большинство из этих ответов просто не будет работать, потому что вы обычно позволяете MVC создавать для вас соответствующую коллекцию маршрутов (используя стандартные соглашения RESTful), что означает, что вы не найдете никакого вызова для редактирования по желанию.

ConfigureServices() метод вызывается Startup.cs файла зарегистрирует MVC с рамками внедрения зависимостей, встроенной в ASP.NET 5: Таким образом, когда вы звоните ApplicationBuilder.UseMvc() позже в этом классе, структура MVC автоматически добавит эти маршруты по умолчанию к вашему приложение.Мы посмотрим, что происходит за капотом может, глядя на реализацию в UseMvc() метода в рамках исходного кода:

public static IApplicationBuilder UseMvc(
    [NotNull] this IApplicationBuilder app, 
    [NotNull] Action<IRouteBuilder> configureRoutes) 
{ 
    // Verify if AddMvc was done before calling UseMvc 
    // We use the MvcMarkerService to make sure if all the services were added. 
    MvcServicesHelper.ThrowIfMvcNotRegistered(app.ApplicationServices); 

    var routes = new RouteBuilder 
    { 
     DefaultHandler = new MvcRouteHandler(), 
     ServiceProvider = app.ApplicationServices 
    }; 

    configureRoutes(routes); 

    // Adding the attribute route comes after running the user-code because 
    // we want to respect any changes to the DefaultHandler. 
    routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(
     routes.DefaultHandler, 
     app.ApplicationServices)); 

    return app.UseRouter(routes.Build()); 
} 

Хорошая вещь об этом является то, что структура теперь обрабатывает всю тяжелую работу, перебирая все Действия диспетчера и настройки маршрутов по умолчанию, тем самым сохраняя вам некоторую избыточную работу.

Плохо, документация о том, как вы можете добавить свои собственные маршруты, мало или вообще отсутствует. К счастью, вы можете легко сделать это, используя на основе Конвенции и/или подход на основе атрибутов (aka Маршрутизация атрибутов).

конвенции на основе

В классе Startup.cs заменить это:

app.UseMvc(); 

с этим:

app.UseMvc(routes => 
      { 
       // Route Sample A 
       routes.MapRoute(
        name: "RouteSampleA", 
        template: "MyOwnGet", 
        defaults: new { controller = "Items", action = "Get" } 
       ); 
       // Route Sample B 
       routes.MapRoute(
        name: "RouteSampleB", 
        template: "MyOwnPost", 
        defaults: new { controller = "Items", action = "Post" } 
       ); 
      }); 

атрибутов На основе

Замечательная вещь в MVC6 заключается в том, что вы также можете определять маршруты на основе каждого контроллера, украшая классы Controller и/или Action с соответствующими параметрами шаблона и/или /, такими как:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 
using Microsoft.AspNet.Mvc; 

namespace MyNamespace.Controllers 
{ 
    [Route("api/[controller]")] 
    public class ItemsController : Controller 
    { 
     // GET: api/items 
     [HttpGet()] 
     public IEnumerable<string> Get() 
     { 
      return GetLatestItems(); 
     } 

     // GET: api/items/5 
     [HttpGet("{num}")] 
     public IEnumerable<string> Get(int num) 
     { 
      return GetLatestItems(5); 
     }  

     // GET: api/items/GetLatestItems 
     [HttpGet("GetLatestItems")] 
     public IEnumerable<string> GetLatestItems() 
     { 
      return GetLatestItems(5); 
     } 

     // GET api/items/GetLatestItems/5 
     [HttpGet("GetLatestItems/{num}")] 
     public IEnumerable<string> GetLatestItems(int num) 
     { 
      return new string[] { "test", "test2" }; 
     } 

     // POST: /api/items/PostSomething 
     [HttpPost("PostSomething")] 
     public IActionResult Post([FromBody]string someData) 
     { 
      return Content("OK, got it!"); 
     } 
    } 
} 

Этот контроллер будет обрабатывать следующие запросы:

[GET] api/items 
[GET] api/items/5 
[GET] api/items/GetLatestItems 
[GET] api/items/GetLatestItems/5 
[POST] api/items/PostSomething 

Также обратите внимание, что если вы используете эти два подхода совмещены, атрибутов на основе маршрутов (если она определена) будет превалировать те конвенции на основе, и оба они будут переопределять ault маршруты, определенные UseMvc().

Для получения дополнительной информации вы также можете получить read the following post.

Смежные вопросы