Пользовательские имена методов в ASP.NET Web API

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

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 / users/ и он будет вызовите GetAll, и я могу перейти к myapi / api / users/1, и он вызовет Get, однако, если я вызову myapi/api/users / authenticate?username={0} & password={1} затем он вызовет Get (не аутентифицируется) и ошибку:

словарь параметров содержит нулевую запись для параметра " id "ненулевого типа" System.Система Int32 "для метода".Строка Get (Int32) 'in' Navtrak.Сервисы.ФОС.Навтракапи.Контроллеры.UsersController'. Необязательным параметром должен быть ссылочный тип, тип nullable, или быть объявлен как необязательный параметр.

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

6 ответов


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

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

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

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


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


Я дни в мире 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} as {id}, с которым я столкнулся:

{
"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}, он начал работать. Таким образом, по-видимому, имя параметра совпадает со значением в заполнителе.

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


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.цезий. Он может иметь маршрутизацию по умолчанию.

[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


см. эту статью для более длинного обсуждения именованных действий. Он также показывает, что вы можете использовать атрибут [HttpGet] вместо префикса имени действия с помощью "get".

http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api


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

на 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());
}

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

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

Конвенция На Основе

в свой стартап.класс 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 методы с соответствующими RouteAttribute и/или HttpGet / HttpPost параметры шаблона, такие как:

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

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

для получения дополнительной информации, вы можете также прочитайте следующее сообщение.