Пользовательские имена методов в Интернете ASP.NET 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 'для метода' System.Строка Get (Int32)' in 'Navtrak.Сервисы.ФОС.Навтракапы.Контроллеры.UsersController'. Необязательный параметр должен быть ссылочным типом, тип, допускающий значение null , или быть объявлен как необязательный параметр.

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

6 99

6 ответов:

по умолчанию конфигурация маршрута следует за соглашениями RESTFul, что означает, что она будет принимать только имена действий Get, Post, Put и Delete (посмотрите на маршрут в global.asax => по умолчанию он не позволяет вам указывать какое-либо имя действия => он использует команду 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 для аутентификации пользователя.

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

The 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().

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