Nhiều phương thức HTTPPost trong bộ điều khiển API Web


126

Tôi đang bắt đầu sử dụng dự án API Web MVC4, tôi có bộ điều khiển với nhiều HttpPostphương thức. Bộ điều khiển trông như sau:

Bộ điều khiển

public class VTRoutingController : ApiController
{
    [HttpPost]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

Ở đây MyRequestTemplateđại diện cho lớp mẫu chịu trách nhiệm xử lý Json thông qua yêu cầu.

Lỗi:

Khi tôi thực hiện một yêu cầu sử dụng Fiddler http://localhost:52370/api/VTRouting/TSPRoutehoặc http://localhost:52370/api/VTRouting/Route tôi gặp lỗi:

Nhiều hành động đã được tìm thấy phù hợp với yêu cầu

Nếu tôi loại bỏ một trong các phương pháp trên, nó hoạt động tốt.

Toàn cầu

Tôi đã thử sửa đổi bảng định tuyến mặc định global.asax, nhưng tôi vẫn gặp lỗi, tôi nghĩ rằng tôi có vấn đề trong việc xác định tuyến trong global.asax. Đây là những gì tôi đang làm trong global.asax.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.MapHttpRoute(
        name: "MyTSPRoute",
        routeTemplate: "api/VTRouting/TSPRoute",
        defaults: new { }
    );

    routes.MapHttpRoute(
        name: "MyRoute",
        routeTemplate: "api/VTRouting/Route",
        defaults: new { action="Route" }
    );
}

Tôi đang thực hiện yêu cầu trong Fiddler bằng POST, chuyển json trong RequestBody cho MyRequestTemplate.

Câu trả lời:


143

Bạn có thể có nhiều hành động trong một bộ điều khiển duy nhất.

Cho rằng bạn phải làm hai điều sau đây.

  • Hành động trang trí đầu tiên với ActionNamethuộc tính như

     [ActionName("route")]
     public class VTRoutingController : ApiController
     {
       [ActionName("route")]
       public MyResult PostRoute(MyRequestTemplate routingRequestTemplate)
       {
         return null;
       }
    
      [ActionName("tspRoute")]
      public MyResult PostTSPRoute(MyRequestTemplate routingRequestTemplate)
      {
         return null;
      }
    }
  • Thứ hai xác định các tuyến đường sau trong WebApiConfigtập tin.

    // Controller Only
    // To handle routes like `/api/VTRouting`
    config.Routes.MapHttpRoute(
        name: "ControllerOnly",
        routeTemplate: "api/{controller}"               
    );
    
    
    // Controller with ID
    // To handle routes like `/api/VTRouting/1`
    config.Routes.MapHttpRoute(
        name: "ControllerAndId",
        routeTemplate: "api/{controller}/{id}",
        defaults: null,
        constraints: new { id = @"^\d+$" } // Only integers 
    );
    
    // Controllers with Actions
    // To handle routes like `/api/VTRouting/route`
    config.Routes.MapHttpRoute(
        name: "ControllerAndAction",
        routeTemplate: "api/{controller}/{action}"
    );

Điều gì xảy ra nếu tôi không muốn đặt bất kỳ hạn chế nào về loại ID? Ý nghĩa: làm thế nào tôi có thể chấp nhận ID chuỗi?
frapontillo

5
@frapontillo: Id phải là một số nguyên, do đó, nó bị phân biệt với tên tuyến nếu không, enghine định tuyến sẽ coi nó như một tên hành động chứ không phải là id. Nếu bạn cần phải có id dưới dạng chuỗi thì bạn có thể tạo một hành động.
Asif Mushtaq

Tôi sẽ sử dụng Định tuyến thuộc tính thay thế. Bạn sẽ không phải sử dụng nhiều tuyến đường trong WebApiConfig theo cách đó. Kiểm tra liên kết này: docs.microsoft.com/en-us/aspnet/web-api/overview/ mẹo
Giàu

Nếu tôi thêm như thế này, nó sẽ báo lỗi ------------ không gian tên ImageDoadApplication.Controllers {public class FrontModel {public chuỗi skus {get; bộ; }} [ActionName ("ProductContoder")] lớp công cộng ProductContoder: ApiControll {// GET: api / NewCotler công khai IEnumerable <string> Get () {trả về chuỗi mới [] {"value1", "value2"}; }
Umashankar

41

Một giải pháp tốt hơn cho vấn đề của bạn sẽ là sử dụng Routecho phép bạn chỉ định tuyến đường trên phương thức bằng cách chú thích:

[RoutePrefix("api/VTRouting")]
public class VTRoutingController : ApiController
{
    [HttpPost]
    [Route("Route")]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost]
    [Route("TSPRoute")]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

Namespace là gì trong tuyến đường? Tôi đang sử dụng MVC4 và Route không được công nhận.
Eaglei22


Vâng, đây là con đường nó nên đi. Cảm ơn.
newman

1
vì một số lý do tôi không thể làm việc này đây chính xác là những gì tôi đã làm
oligofren

2
Làm thế nào các url trông giống như để gọi RouteTSPRoute?
Si8

27

sử dụng:

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

nó không còn là cách tiếp cận RESTful nữa, nhưng giờ đây bạn có thể gọi hành động của mình bằng tên (thay vì để API Web tự động xác định một hành động cho bạn dựa trên động từ) như thế này:

[POST] /api/VTRouting/TSPRoute

[POST] /api/VTRouting/Route

Trái với niềm tin phổ biến, không có gì sai với cách tiếp cận này và nó không lạm dụng API Web. Bạn vẫn có thể tận dụng tất cả các tính năng tuyệt vời của API Web (ủy quyền xử lý, đàm phán nội dung, mediatypeformatters, v.v.) - bạn chỉ cần bỏ cách tiếp cận RESTful.


1
Cảm ơn câu trả lời, nhưng nó vẫn cho tôi lỗi tương tự.
Habib

Điều đó là không thể, sau đó một cái gì đó phải được cấu hình sai trong ứng dụng của bạn. Bạn có thể hiển thị toàn bộ thiết lập Tuyến đường không? Ngoài ra làm thế nào chính xác là bạn đang gọi hành động điều khiển?
Filip W

Toàn bộ thiết lập Tuyến đường là trong global.asax, tôi đã đăng phần đó trong câu hỏi của mình, Để thực hiện yêu cầu, tôi đang sử dụng Fiddler-> Compose-> và chọn Post làm hoạt động
Habib

hãy thử xóa tất cả các định nghĩa tuyến đường khác và chỉ để lại cái tôi đã đăng. Sau đó, bạn có thể dễ dàng gọi cả hai hành động POST nằm trong một bộ điều khiển (giống như cách tiếp cận MVC cũ)
Filip W

1
Filip, tôi đang sử dụng .Net framework 4.5, với mvc4 hoặc Visual studio 2012 RC, mẫu nào bạn đang sử dụng để tạo dự án của bạn, của bạn 'đang hoạt động hoàn hảo
Habib

13

Một điểm cuối api web (bộ điều khiển) là một tài nguyên duy nhất chấp nhận các động từ get / post / put / xóa. Nó không phải là một bộ điều khiển MVC bình thường.

Nhất thiết, tại /api/VTRoutingđó chỉ có thể có một phương thức HttpPost chấp nhận các tham số bạn đang gửi. Tên hàm không quan trọng , miễn là bạn đang trang trí với công cụ [http]. Tôi chưa bao giờ thử, mặc dù.

Chỉnh sửa: Điều này không hoạt động. Trong việc giải quyết, nó dường như đi theo số lượng tham số, không cố gắng mô hình hóa liên kết với loại.

Bạn có thể quá tải các chức năng để chấp nhận các tham số khác nhau. Tôi khá chắc chắn rằng bạn sẽ ổn nếu bạn khai báo theo cách bạn làm, nhưng sử dụng các tham số (không tương thích) khác nhau cho các phương thức. Nếu các thông số giống nhau, bạn sẽ không gặp may vì ràng buộc mô hình sẽ không biết ý bạn là gì.

[HttpPost]
public MyResult Route(MyRequestTemplate routingRequestTemplate) {...}

[HttpPost]
public MyResult TSPRoute(MyOtherTemplate routingRequestTemplate) {...}

Phần này hoạt động

Mẫu mặc định họ cung cấp khi bạn tạo một mẫu mới làm cho điều này khá rõ ràng và tôi sẽ nói bạn nên tuân theo quy ước này:

public class ValuesController : ApiController
{
    // GET is overloaded here.  one method takes a param, the other not.
    // GET api/values  
    public IEnumerable<string> Get() { .. return new string[] ... }
    // GET api/values/5
    public string Get(int id) { return "hi there"; }

    // POST api/values (OVERLOADED)
    public void Post(string value) { ... }
    public void Post(string value, string anotherValue) { ... }
    // PUT api/values/5
    public void Put(int id, string value) {}
    // DELETE api/values/5
    public void Delete(int id) {}
}

Nếu bạn muốn tạo một lớp thực hiện nhiều thứ, để sử dụng ajax, không có lý do gì lớn để không sử dụng mẫu điều khiển / hành động tiêu chuẩn. Sự khác biệt thực sự duy nhất là chữ ký phương thức của bạn không đẹp và bạn phải bọc mọi thứ Json( returnValue)trước khi trả lại chúng.

Biên tập:

Quá tải hoạt động tốt khi sử dụng mẫu tiêu chuẩn (được chỉnh sửa để bao gồm) khi sử dụng các loại đơn giản. Tôi cũng đã đi và thử nghiệm theo cách khác, với 2 đối tượng tùy chỉnh với các chữ ký khác nhau. Không bao giờ có thể làm cho nó hoạt động.

  • Liên kết với các đối tượng phức tạp trông không "sâu", vì vậy đó là điều không nên
  • Bạn có thể khắc phục điều này bằng cách chuyển một tham số phụ, trên chuỗi truy vấn
  • Một bài viết tốt hơn tôi có thể cung cấp cho các tùy chọn có sẵn

Điều này làm việc cho tôi trong trường hợp này, xem nó đưa bạn đến đâu. Ngoại lệ chỉ để thử nghiệm.

public class NerdyController : ApiController
{
    public void Post(string type, Obj o) { 
        throw new Exception("Type=" + type + ", o.Name=" + o.Name ); 
    }
}

public class Obj {
    public string Name { get; set; }
    public string Age { get; set; }
}

Và được gọi như thế này từ bảng điều khiển:

$.post("/api/Nerdy?type=white", { 'Name':'Slim', 'Age':'21' } )

Tôi đã thử thay đổi các loại tham số, nhưng có vẻ như nó chỉ cho phép một phương thức Post duy nhất trong bộ điều khiển. Cảm ơn câu trả lời của bạn
Habib

Tôi giả định rằng nó sẽ thử ràng buộc mô hình để tìm nó, vì bạn có thể quá tải. Mặc dù vậy, nó hoạt động với các thông số khác nhau. Mặc dù có thể không khó để viết lại để làm điều này, nhưng họ chưa phát hành mã nguồn, vì vậy tôi chỉ bị kẹt khi nhìn vào sự phân tách xấu xí
Andrew Backer

2
+1 để thực sự giải thích lý do nó không hoạt động và triết lý đằng sau web api.
MEMark

Đánh giá cao sự cố ... Tôi đã cho rằng nó được cho là một POST / PUT / GET trên mỗi bộ điều khiển nhưng tôi không chắc chắn ... vì vậy đó là lý do tại sao tôi tìm kiếm nó. Vì tôi đã bắt đầu phát triển với MVC cho các ứng dụng web trong đó nhiều hành động như trên mỗi bộ điều khiển là tiêu chuẩn ... nó gần như là một sự lãng phí vì vậy tôi có thể hiểu tại sao một nhà phát triển muốn. Có một điều như quá nhiều bộ điều khiển?
Anthony Griggs

6

Có thể thêm nhiều phương thức Nhận và Đăng trong cùng một Trình điều khiển API Web. Ở đây Tuyến mặc định là nguyên nhân gây ra vấn đề. API Web kiểm tra Tuyến đường phù hợp từ trên xuống dưới và do đó Tuyến đường mặc định của bạn khớp với tất cả các yêu cầu. Theo tuyến đường mặc định, chỉ có một Phương thức Nhận và Đăng trong một bộ điều khiển. Đặt mã sau lên trên hoặc Nhận xét / Xóa tuyến mặc định

    config.Routes.MapHttpRoute("API Default", 
                               "api/{controller}/{action}/{id}",
                               new { id = RouteParameter.Optional });

1

Đặt tiền tố tuyến đường [RoutePrefix ("api / Profiles")] ở cấp độ bộ điều khiển và đặt tuyến đường ở phương thức hành động [Tuyến đường ("LikeProfile")]. Không cần thay đổi bất cứ điều gì trong tệp global.asax

namespace KhandalVipra.Controllers
{
    [RoutePrefix("api/Profiles")]
    public class ProfilesController : ApiController
    {
        // POST: api/Profiles/LikeProfile
        [Authorize]
        [HttpPost]
        [Route("LikeProfile")]
        [ResponseType(typeof(List<Like>))]
        public async Task<IHttpActionResult> LikeProfile()
        {
        }
    }
}

0

Tôi nghĩ rằng câu hỏi đã được trả lời. Tôi cũng đang tìm kiếm một cái gì đó một bộ điều khiển webApi có cùng tên mehtods nhưng tên khác nhau. Tôi đã cố gắng thực hiện Máy tính như WebApi. Máy tính có 4 phương thức có cùng chữ ký nhưng tên khác nhau.

public class CalculatorController : ApiController
{
    [HttpGet]
    [ActionName("Add")]
    public string Add(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Add = {0}", num1 + num2);
    }

    [HttpGet]
    [ActionName("Sub")]
    public string Sub(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Subtract result = {0}", num1 - num2);
    }

    [HttpGet]
    [ActionName("Mul")]
    public string Mul(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Multiplication result = {0}", num1 * num2);
    }

    [HttpGet]
    [ActionName("Div")]
    public string Div(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Division result = {0}", num1 / num2);
    }
}

và trong tệp WebApiConfig bạn đã có

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

Chỉ cần đặt xác thực / ủy quyền trên IIS và bạn đã hoàn tất!

Hi vọng điêu nay co ich!


0

Bạn có thể sử dụng phương pháp này:

public class VTRoutingController : ApiController
{
    [HttpPost("Route")]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost("TSPRoute")]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

-1
public class Journal : ApiController
{
    public MyResult Get(journal id)
    {
        return null;
    }
}

public class Journal : ApiController
{

    public MyResult Get(journal id, publication id)
    {
        return null;
    }
}

Tôi không chắc chắn liệu quá tải phương thức get / post có vi phạm khái niệm api đầy đủ hay không, nhưng nó hoạt động được. Nếu ai đó có thể khai sáng về vấn đề này. Nếu tôi có một uri như thế nào

uri:/api/journal/journalid
uri:/api/journal/journalid/publicationid

vì vậy như bạn có thể thấy loại tạp chí tổng hợp của tôi, mặc dù tôi có thể định nghĩa một bộ điều khiển khác để xuất bản duy nhất và vượt qua số id của ấn phẩm trong url của tôi tuy nhiên điều này có ý nghĩa hơn nhiều. vì ấn phẩm của tôi sẽ không tồn tại mà không có tạp chí.


-1

Tôi vừa thêm "action = action_name" vào url và bằng cách này, công cụ định tuyến biết hành động tôi muốn. Tôi cũng đã thêm Thuộc tính ActionName cho các hành động nhưng tôi không chắc là cần thiết.


-1

Giải thích tốt nhất và đơn giản nhất tôi đã thấy về chủ đề này - http://www.binaryintellect.net/articles/9db02aa1-c193-421e-94d0-926e440ed297.aspx

  • Đã chỉnh sửa -

Tôi đã làm cho nó chỉ hoạt động với Route và không cần RoutePrefix.

Ví dụ: trong bộ điều khiển

[HttpPost]
[Route("[action]")]
public IActionResult PostCustomer
([FromBody]CustomerOrder obj)
{
}

[HttpPost]
[Route("[action]")]
public IActionResult PostCustomerAndOrder
([FromBody]CustomerOrder obj)
{
}

Sau đó, tên hàm đi trong jquery là -

options.url = "/api/customer/PostCustomer";

hoặc là

options.url = "/api/customer/PostCustomerAndOrder";
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.