Tên phương thức tùy chỉnh trong ASP.NET Web API


110

Tôi đang chuyển đổi từ API Web WCF sang API Web ASP.NET MVC 4 mới. Tôi có một UsersController và tôi muốn có một phương thức có tên là Xác thực. Tôi thấy các ví dụ về cách thực hiện GetAll, GetOne, Đăng và Xóa, tuy nhiên nếu tôi muốn thêm các phương thức bổ sung vào các dịch vụ này thì sao? Ví dụ: UsersService của tôi phải có một phương thức được gọi là Xác thực nơi họ chuyển tên người dùng và mật khẩu, tuy nhiên nó không hoạt động.

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

Tôi có thể duyệt tới myapi / api / users / và nó sẽ gọi GetAll và tôi có thể duyệt tới myapi / api / users / 1 và nó sẽ gọi Get, tuy nhiên nếu tôi gọi myapi / api / users / authenticate? Username = {0} & password = {1} thì nó sẽ gọi Get (NOT Authenticate) và báo lỗi:

Từ điển tham số chứa một mục nhập rỗng cho tham số 'id' thuộc loại không thể nullable 'System.Int32' cho phương thức 'System.String Get (Int32)' trong 'Navtrak.Services.WCF.NavtrakAPI.Controllers.UsersController'. Một tham số tùy chọn phải là một kiểu tham chiếu, một kiểu nullable hoặc được khai báo như một tham số tùy chọn.

Làm cách nào tôi có thể gọi các tên phương thức tùy chỉnh như Xác thực?


Vui lòng tham khảo liên kết này: 5 câu trả lời stackoverflow.com/questions/12775590/...
Vishwa G

Câu trả lời:


136

Theo mặc định, cấu hình tuyến tuân theo các quy ước RESTFul nghĩa là nó sẽ chỉ chấp nhận các tên hành động Nhận, Đăng, Đặt và Xóa (nhìn vào tuyến đường trong global.asax => mặc định nó không cho phép bạn chỉ định bất kỳ tên hành động nào => nó sử dụng động từ HTTP để gửi). Vì vậy, khi bạn gửi một yêu cầu GET cho /api/users/authenticatebạn, về cơ bản, bạn đang gọi Get(int id)hành động và truyền đi id=authenticatemà rõ ràng là bị lỗi vì Hành động nhận của bạn yêu cầu một số nguyên.

Nếu bạn muốn có các tên hành động khác với tên tiêu chuẩn, bạn có thể sửa đổi định nghĩa tuyến đường của mình trong global.asax:

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

Bây giờ bạn có thể điều hướng đến /api/values/getauthenticateđể xác thực người dùng.


20
Có cách nào để làm cho nó vẫn sử dụng Get (id), Get () Put, Delete, Post trong khi vẫn cho phép các hành động khác không?
Shawn Mclean

@ShawnMclean Tôi đoán rằng bạn có thể chỉ định một tuyến đường khác mà không {action}có ràng buộc nào {id}để bất kỳ thứ gì khác inthoặc Guid(hoặc bất cứ thứ gì) sẽ không khớp. Sau đó, nó sẽ có thể lọt qua với một đề nghị của Darin
Steve Greatrex

2
Một điều quan trọng hơn ở đây là với kiểu định tuyến này, bạn phải sử dụng các thuộc tính để chỉ định các phương thức HTTP được phép (như [HttpGet]).
Himalaya Garg

1
bạn có chắc chắn cần sử dụng các hành động khác không? Bạn đã thực sự cố gắng để phù hợp với những gì bạn đang làm trong các quy ước REST chưa? Nó không cần thiết phải sử dụng các hành động khác.
niico

1
@niico: Hãy tưởng tượng bạn muốn có một phương thức Count (), phương thức này trả về số phần tử Get () sẽ trả về. Tôi không thấy làm thế nào để phù hợp với Get (), Get (id), Post (...), Put (...) hoặc Delete (id). Và, tất nhiên, có nhiều phương pháp khả thi hơn mà tôi có thể tưởng tượng.
Jens Mander

88

Đây là phương pháp tốt nhất mà tôi đưa ra cho đến nay để kết hợp các phương pháp GET bổ sung đồng thời hỗ trợ các phương pháp REST thông thường. Thêm các tuyến sau vào WebApiConfig của bạn:

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

Tôi đã xác minh giải pháp này với lớp thử nghiệm bên dưới. Tôi đã có thể thực hiện thành công từng phương pháp trong bộ điều khiển của mình bên dưới:

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

Tôi đã xác minh rằng nó hỗ trợ các yêu cầu sau:

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

Lưu ý Nếu các hành động GET bổ sung của bạn không bắt đầu bằng 'Nhận', bạn có thể muốn thêm thuộc tính HttpGet vào phương thức.


1
giải pháp tốt, bạn có thể cho tôi biết nếu tôi cấu hình các động từ putdeletenhư bạn đã làm trên getpost, cũng sẽ hoạt động tốt không?
Felipe Oriani

1
Theo ý kiến ​​của tôi, điều này nên được bao gồm trong mặc định cho các dự án WebAPI (có lẽ đã được bình luận). Nó mang đến cho bạn phong cách đường WebAPI VÀ MVC cùng một lúc ...
John Culviner

1
@FelipeOriani, tôi không nghĩ rằng bạn sẽ muốn hoặc cần phải định cấu hình puthoặc deleteđộng từ vì những yêu cầu đó thường đi kèm với một tham số id để xác định tài nguyên mà bạn muốn áp dụng thao tác đó. Một deletecuộc gọi đến /api/foosẽ gặp lỗi vì bạn đang cố xóa foo nào? Do đó, tuyến DefaultApiWithId sẽ xử lý tốt những trường hợp đó.
nwayve

4
điều này không hiệu quả với tôi cả. nhận được thông báo lỗi khi tôi cố gắng thực hiện GET cơ bản.
Matt

Đối với cái đầu tiên, DefaultApiWithId, không nên đặt giá trị mặc định là null thay vì {id = RouteParameter.Optional} mới? Không bắt buộc phải có 'id'?
Johnny Oshika

22

Tôi đang từng ngày vào thế giới MVC4.

Đối với giá trị của nó, tôi có một SitesAPIController và tôi cần một phương thức tùy chỉnh, có thể được gọi là:

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

Với các giá trị khác nhau cho tham số cuối cùng để có được bản ghi với các vị trí khác nhau.

Điều cuối cùng đã làm việc cho tôi là:

Phương pháp trong 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;
}

Và điều này trong 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}"
 );

Miễn là tôi đã đặt tên cho {disposition} là {id} mà tôi đã gặp phả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."
}

Khi tôi đổi tên nó thành {disposition} nó bắt đầu hoạt động. Vì vậy, rõ ràng tên tham số được khớp với giá trị trong trình giữ chỗ.

Vui lòng chỉnh sửa câu trả lời này để làm cho nó chính xác hơn / giải thích.


Cảm ơn vì tiền hỗ trợ. Tôi đã mắc phải sai lầm giống như bạn.
abhi,

16

Web Api theo mặc định yêu cầu URL ở dạng api / {controller} / {id}, để ghi đè định tuyến mặc định này. bạn có thể đặt định tuyến bằng bất kỳ cách nào trong hai cách dưới đây.

Lựa chọn đầu tiên:

Thêm đăng ký tuyến đường bên dưới trong WebApiConfig.cs

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

Trang trí phương thức hành động của bạn với HttpGet và các thông số như bên dưới

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

 {

// your code here

}

để gọi phương thức ở trên url sẽ giống như bên dưới

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

Tùy chọn thứ hai Thêm tiền tố tuyến vào lớp Bộ điều khiển và Trang trí phương thức hành động của bạn với HttpGet như bên dưới. Trong trường hợp này, không cần thay đổi bất kỳ WebApiConfig.cs nào. Nó có thể có định tuyến mặc định.

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

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

{

// your code here

}

}

để gọi phương thức ở trên url sẽ giống như bên dưới

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


Tôi thích lựa chọn thứ hai rất nhiều. Bạn cũng có thể chỉ cho tôi cách sử dụng nó trong VB.net? Cảm ơn rất nhiều.
user1617676

12

Trong trường hợp bạn đang sử dụng ASP.NET 5 với ASP.NET MVC 6 , hầu hết các câu trả lời này chỉ đơn giản là sẽ không hoạt động bởi vì thông thường bạn sẽ để MVC tạo bộ sưu tập tuyến thích hợp cho bạn (sử dụng các quy ước RESTful mặc định), nghĩa là bạn sẽ không tìm thấy bất kỳ Routes.MapRoute()cuộc gọi nào để chỉnh sửa theo ý muốn.

Các ConfigureServices()phương pháp gọi bởi các Startup.cstập tin sẽ đăng ký MVC với khuôn khổ dependency injection xây dựng vào ASP.NET 5: theo cách đó, khi bạn gọi ApplicationBuilder.UseMvc()sau trong lớp đó, khuôn khổ MVC sẽ tự động thêm các đường bay mặc định để ứng dụng của bạn. Chúng ta có thể xem xét những gì xảy ra đằng sau màn che bằng cách xem xét việc UseMvc()triển khai phương pháp trong mã nguồn khung:

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

Điều tốt về điều này là khung công tác hiện xử lý tất cả các công việc khó khăn, lặp lại qua tất cả các Hành động của Bộ điều khiển và thiết lập các tuyến mặc định của chúng, do đó giúp bạn tiết kiệm một số công việc thừa.

Điều tồi tệ là, có rất ít hoặc không có tài liệu về cách bạn có thể thêm các tuyến đường của riêng mình. May mắn thay, bạn có thể dễ dàng làm điều đó bằng cách sử dụng phương pháp Dựa trên quy ước và / hoặc dựa trên thuộc tính (hay còn gọi là Định tuyến thuộc tính ).

Dựa trên quy ước

Trong lớp Startup.cs của bạn, hãy thay thế cái này:

app.UseMvc();

Với cái này:

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

Dựa trên thuộc tính

Một điều tuyệt vời về MVC6 là bạn cũng có thể xác định các tuyến trên cơ sở mỗi bộ điều khiển bằng cách trang trí Controllerlớp và / hoặc các Actionphương thức với các tham số thích hợp RouteAttributevà / hoặc HttpGet/ HttpPostmẫu, chẳng hạn như sau:

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

Bộ điều khiển này sẽ xử lý các yêu cầu sau:

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

Cũng lưu ý rằng nếu bạn sử dụng hai phương pháp tiếp cận togheter, các tuyến dựa trên thuộc tính (khi được xác định) sẽ ghi đè các tuyến dựa trên Quy ước và cả hai đều sẽ ghi đè các tuyến mặc định được xác định bởi UseMvc().

Để biết thêm thông tin, bạn cũng có thể đọc bài đăng sau trên blog của tôi.


1
Đây là hoàn hảo! Không có câu trả lời nào khác thực sự làm được những gì tôi cần. Nhưng bạn đã cứu tôi :)
Vua Arthur Đệ Tam

Có cách nào để sử dụng mô hình được xác định trước làm tham số thứ hai không? Ví dụ, khi tôi đang vá một người dùng nhất định như thế này: public IActionResult Patch(int id, [FromQuery] Person person), tất cả các thuộc tính đến là null!
Vua Arthur Đệ Tam


0

Chỉ cần sửa đổi WebAPIConfig.cs của bạn như bên dưới

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

Sau đó, triển khai API của bạn như bên dưới

    // GET: api/Controller_Name/Show/1
    [ActionName("Show")]
    [HttpGet]
    public EventPlanner Id(int id){}

0

Web APi 2 và các phiên bản mới hơn hỗ trợ một kiểu định tuyến mới, được gọi là định tuyến thuộc tính. Như tên của nó, định tuyến thuộc tính sử dụng các thuộc tính để xác định các tuyến đường. Định tuyến thuộc tính cho phép bạn kiểm soát nhiều hơn các URI trong API web của bạn. Ví dụ: bạn có thể dễ dàng tạo các URI mô tả phân cấp tài nguyên.

Ví dụ:

[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }

Sẽ hoàn hảo và bạn không cần thêm bất kỳ mã nào, ví dụ như trong WebApiConfig.cs. Bạn chỉ cần chắc chắn rằng định tuyến api web đã được bật hay chưa trong WebApiConfig.cs, nếu không, bạn có thể kích hoạt như dưới đây:

        // Web API routes
        config.MapHttpAttributeRoutes();

Bạn không phải thực hiện thêm hoặc thay đổi điều gì đó trong WebApiConfig.cs. Để biết thêm chi tiết, bạn có thể xem bài viết này .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.