หลายวิธี HttpPost ในตัวควบคุม Web API


126

ฉันกำลังเริ่มใช้โครงการ MVC4 Web API ฉันมีคอนโทรลเลอร์ที่มีหลายHttpPostวิธี คอนโทรลเลอร์มีลักษณะดังต่อไปนี้:

ตัวควบคุม

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

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

ในที่นี้MyRequestTemplateแสดงถึงคลาสเทมเพลตที่รับผิดชอบในการจัดการ Json ที่มาจากการร้องขอ

ข้อผิดพลาด:

เมื่อฉันขอโดยใช้ Fiddler http://localhost:52370/api/VTRouting/TSPRouteหรือhttp://localhost:52370/api/VTRouting/Route ฉันได้รับข้อผิดพลาด:

พบการดำเนินการหลายรายการที่ตรงกับคำขอ

หากฉันลบวิธีใดวิธีหนึ่งข้างต้นก็ใช้ได้ดี

Global.asax

ฉันได้ลองแก้ไขตารางเส้นทางเริ่มต้นglobal.asaxแล้ว แต่ฉันยังคงได้รับข้อผิดพลาดฉันคิดว่าฉันมีปัญหาในการกำหนดเส้นทางใน global.asax นี่คือสิ่งที่ฉันทำใน 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" }
    );
}

ฉันกำลังส่งคำขอใน Fiddler โดยใช้ POST ผ่าน json ใน RequestBody สำหรับ MyRequestTemplate

คำตอบ:


143

คุณสามารถมีการดำเนินการหลายอย่างในตัวควบคุมเดียว

สำหรับสิ่งนั้นคุณต้องทำสองสิ่งต่อไปนี้

  • ขั้นแรกให้ตกแต่งการกระทำด้วยActionNameแอตทริบิวต์เช่น

     [ActionName("route")]
     public class VTRoutingController : ApiController
     {
       [ActionName("route")]
       public MyResult PostRoute(MyRequestTemplate routingRequestTemplate)
       {
         return null;
       }
    
      [ActionName("tspRoute")]
      public MyResult PostTSPRoute(MyRequestTemplate routingRequestTemplate)
      {
         return null;
      }
    }
  • ประการที่สองกำหนดเส้นทางต่อไปนี้ในWebApiConfigไฟล์

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

จะเกิดอะไรขึ้นหากฉันไม่ต้องการตั้งข้อ จำกัด เกี่ยวกับประเภทของรหัส? ความหมาย: ฉันจะยอมรับรหัสสตริงด้วยได้อย่างไร
frapontillo

5
@frapontillo: รหัสควรเป็นจำนวนเต็มเพื่อให้แตกต่างจากชื่อเส้นทางมิฉะนั้นการกำหนดเส้นทางจะถือว่าเป็นชื่อการดำเนินการแทนที่จะเป็นรหัส หากคุณต้องการให้ id เป็นสตริงคุณสามารถสร้างการดำเนินการได้
Asif Mushtaq

ฉันจะใช้ Attribute Routing แทน คุณจะไม่ต้องใช้หลายเส้นทางใน WebApiConfig ด้วยวิธีนั้น ดูลิงค์นี้: docs.microsoft.com/en-us/aspnet/web-api/overview/…
รวย

ถ้าฉันเพิ่มแบบนี้มันทำให้ฉันมีข้อผิดพลาด ------------ namespace ImageDownloadApplication.Controllers {public class FrontModel {public string skus {get; ตั้ง; }} [ActionName ("ProductController")] คลาสสาธารณะ ProductController: ApiController {// GET: api / NewCotroller สาธารณะ IEnumerable <string> Get () {return new string [] {"value1", "value2"}; }
Umashankar

42

ทางออกที่ดีกว่ามากสำหรับปัญหาของคุณคือการใช้Routeซึ่งช่วยให้คุณระบุเส้นทางในวิธีการโดยใช้คำอธิบายประกอบ:

[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 อยู่ในเส้นทางใด ฉันใช้ MVC4 และไม่รู้จักเส้นทาง
eaglei22


ใช่นี่คือวิธีที่ควรไป ขอบคุณ
newman

1
ด้วยเหตุผลบางอย่างฉันไม่สามารถทำงานนี้ได้ นี่คือสิ่งที่ฉันทำอยู่แล้ว
oligofren

2
url จะมีลักษณะอย่างไรมากกว่าที่จะเรียกRouteและTSPRoute?
Si8

27

ใช้:

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

ไม่ใช่แนวทาง RESTful อีกต่อไป แต่ตอนนี้คุณสามารถเรียกการกระทำของคุณตามชื่อ (แทนที่จะให้ Web API กำหนดโดยอัตโนมัติตามคำกริยา) เช่นนี้:

[POST] /api/VTRouting/TSPRoute

[POST] /api/VTRouting/Route

ตรงกันข้ามกับความเชื่อที่นิยมวิธีนี้ไม่มีอะไรผิดและไม่ได้ใช้ Web API ในทางที่ผิด คุณยังคงสามารถใช้ประโยชน์จากคุณสมบัติที่ยอดเยี่ยมทั้งหมดของ Web API (ตัวจัดการการมอบสิทธิ์การเจรจาต่อรองเนื้อหารูปแบบสื่อกลางและอื่น ๆ ) - คุณเพียงแค่ละทิ้งแนวทาง RESTful


1
ขอบคุณสำหรับคำตอบ แต่ก็ยังคงให้ข้อผิดพลาดเดิม
Habib

เป็นไปไม่ได้ดังนั้นต้องกำหนดค่าอย่างอื่นไม่ถูกต้องในแอปของคุณ คุณสามารถแสดงการตั้งค่าเส้นทางทั้งหมดได้หรือไม่ นอกจากนี้ยังมีวิธีการที่ว่าคุณโทรมาการดำเนินการควบคุม?
Filip W

การตั้งค่าเส้นทางทั้งหมดอยู่ใน global.asax ฉันได้โพสต์ส่วนนั้นในคำถามของฉันสำหรับการร้องขอฉันใช้ Fiddler-> Compose-> และเลือกโพสต์เป็นการดำเนินการ
Habib

ลองลบคำจำกัดความของเส้นทางอื่น ๆ ทั้งหมดและปล่อยให้ฉันโพสต์ไว้ จากนั้นคุณสามารถเรียกการดำเนินการ POST ทั้งสองอย่างที่อยู่ภายในตัวควบคุมหนึ่งตัวได้อย่างง่ายดาย (เช่นเดียวกับแนวทาง MVC แบบเก่า)
Filip W

1
Filip ฉันใช้. Net framework 4.5 กับ mvc4 หรือ Visual studio 2012 RC เทมเพลตใดที่คุณใช้สร้างโปรเจ็กต์ของคุณ 'ทำงานได้อย่างสมบูรณ์แบบ
Habib

13

ปลายทางของ web api (คอนโทรลเลอร์) เป็นทรัพยากรเดียวที่ยอมรับคำกริยา get / post / put / delete มันเป็นไม่ได้เป็นตัวควบคุม MVC ปกติ

จำเป็นที่/api/VTRoutingจะมีเมธอด HttpPost เพียงวิธีเดียวที่ยอมรับพารามิเตอร์ที่คุณกำลังส่ง ชื่อฟังก์ชันไม่สำคัญตราบใดที่คุณตกแต่งด้วย [http] สิ่งต่างๆ ฉันไม่เคยลองเลย

แก้ไข: ไม่ได้ผล ในการแก้ไขดูเหมือนว่าจะเป็นไปตามจำนวนพารามิเตอร์ไม่ได้พยายามผูกโมเดลกับประเภท

คุณสามารถโอเวอร์โหลดฟังก์ชันเพื่อยอมรับพารามิเตอร์ต่างๆ ฉันค่อนข้างแน่ใจว่าคุณจะโอเคถ้าคุณประกาศในแบบที่คุณทำ แต่ใช้พารามิเตอร์ที่แตกต่างกัน (เข้ากันไม่ได้) กับวิธีการ หากพารามิเตอร์เหมือนกันคุณจะโชคไม่ดีเนื่องจากการผูกโมเดลจะไม่รู้ว่าคุณหมายถึงอันไหน

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

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

ส่วนนี้ใช้งานได้

เทมเพลตเริ่มต้นที่พวกเขามอบให้เมื่อคุณสร้างใหม่ทำให้สิ่งนี้ค่อนข้างชัดเจนและฉันจะบอกว่าคุณควรยึดตามหลักการนี้:

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

หากคุณต้องการสร้างคลาสเดียวที่ทำหลายอย่างสำหรับการใช้งาน ajax ไม่มีเหตุผลใหญ่ที่จะไม่ใช้คอนโทรลเลอร์ / รูปแบบการกระทำมาตรฐาน ข้อแตกต่างที่แท้จริงเพียงอย่างเดียวคือลายเซ็นวิธีการของคุณไม่สวยเท่าไหร่และคุณต้องห่อของJson( returnValue)ก่อนที่จะส่งคืน

แก้ไข:

การโอเวอร์โหลดทำงานได้ดีเมื่อใช้เทมเพลตมาตรฐาน (แก้ไขเพื่อรวม) เมื่อใช้ประเภทธรรมดา ฉันได้ไปทดสอบวิธีอื่นด้วยโดยมีวัตถุแบบกำหนดเอง 2 ชิ้นที่มีลายเซ็นต่างกัน ไม่สามารถทำให้มันทำงานได้

  • การผูกกับวัตถุที่ซับซ้อนไม่ได้ดู "ลึก" ดังนั้นจึงไม่ต้องไป
  • คุณสามารถแก้ไขปัญหานี้ได้โดยการส่งพารามิเตอร์พิเศษบนสตริงข้อความค้นหา
  • การเขียนที่ดีกว่าที่ฉันสามารถให้กับตัวเลือกที่มีอยู่

สิ่งนี้ได้ผลสำหรับฉันในกรณีนี้ดูว่าคุณจะไปที่ไหน ข้อยกเว้นสำหรับการทดสอบเท่านั้น

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

และเรียกแบบนี้ว่าคอนโซล:

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

ฉันได้ลองเปลี่ยนประเภทพารามิเตอร์แล้ว แต่ดูเหมือนว่าจะอนุญาตวิธีการโพสต์เดียวในคอนโทรลเลอร์เท่านั้น ขอบคุณสำหรับคำตอบ
Habib

ฉันคิดว่ามันจะพยายามเชื่อมโมเดลเพื่อค้นหามันเนื่องจากคุณสามารถโอเวอร์โหลดได้ มันใช้งานได้กับพารามิเตอร์ # ต่างกัน อาจจะไม่ยากที่จะเขียนซ้ำเพื่อทำสิ่งนี้ แต่พวกเขายังไม่ได้เปิดตัวซอร์สโค้ดดังนั้นฉันจึงติดอยู่กับการถอดชิ้นส่วนที่น่าเกลียด
Andrew Backer

2
+1 สำหรับการอธิบายสาเหตุที่ใช้งานไม่ได้จริงและปรัชญาเบื้องหลังเว็บ API
MEMark

ชื่นชมการพังทลาย ... ฉันคิดว่ามันควรจะเป็น POST / PUT / GET เดียวต่อคอนโทรลเลอร์ แต่ฉันไม่แน่ใจ ... จึงเป็นเหตุผลว่าทำไมฉันถึงค้นหามัน ตั้งแต่ฉันเริ่มพัฒนาด้วย MVC สำหรับเว็บแอปที่มีการกระทำหลายอย่างต่อคอนโทรลเลอร์เป็นบรรทัดฐาน ... ดูเหมือนว่าจะเป็นการสิ้นเปลืองดังนั้นฉันจึงเข้าใจได้ว่าทำไมนักพัฒนาจึงต้องการ มีตัวควบคุมมากเกินไปหรือไม่?
Anthony Griggs

6

เป็นไปได้ที่จะเพิ่มวิธีรับและโพสต์หลายวิธีใน Web API Controller เดียวกัน เส้นทางเริ่มต้นที่นี่ทำให้เกิดปัญหา Web API ตรวจหาเส้นทางที่ตรงกันจากบนลงล่างและด้วยเหตุนี้เส้นทางเริ่มต้นของคุณจะตรงกันสำหรับคำขอทั้งหมด ตามเส้นทางเริ่มต้นมีเพียงวิธีการรับและโพสต์เดียวเท่านั้นที่เป็นไปได้ในคอนโทรลเลอร์เดียว วางรหัสต่อไปนี้ไว้ด้านบนหรือแสดงความคิดเห็น / ลบเส้นทางเริ่มต้น

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

1

ใส่คำนำหน้าเส้นทาง [RoutePrefix ("api / Profiles")] ที่ระดับตัวควบคุมและวางเส้นทางที่วิธีการดำเนินการ [Route ("LikeProfile")] ไม่ต้องเปลี่ยนอะไรในไฟล์ 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

ฉันคิดว่าคำถามได้รับคำตอบแล้ว ฉันยังมองหาตัวควบคุม webApi ที่มี mehtods ที่มีลายเซ็นเหมือนกัน แต่ชื่อต่างกัน ฉันพยายามใช้เครื่องคิดเลขเป็น WebApi เครื่องคิดเลขมี 4 วิธีที่มีลายเซ็นเดียวกัน แต่ชื่อต่างกัน

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

และในไฟล์ WebApiConfig ที่คุณมีอยู่แล้ว

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

เพียงตั้งค่าการรับรองความถูกต้อง / การอนุญาตบน IIS เท่านี้ก็เสร็จสิ้น!

หวังว่านี่จะช่วยได้!


0

คุณสามารถใช้แนวทางนี้:

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

ฉันไม่แน่ใจว่าวิธีรับ / โพสต์ที่มากเกินไปนั้นละเมิดแนวคิดของ restfull api หรือไม่ แต่มันใช้ได้ผล หากใครสามารถให้ความกระจ่างในเรื่องนี้ จะเป็นอย่างไรถ้าฉันมี uri เป็น

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

ดังที่คุณอาจเห็นประเภทของ aggregateroot ในวารสารของฉันแม้ว่าฉันจะสามารถกำหนดตัวควบคุมอื่นสำหรับการเผยแพร่ แต่เพียงผู้เดียวและส่งหมายเลขรหัสของสิ่งพิมพ์ใน url ของฉัน แต่สิ่งนี้ให้ความรู้สึกมากกว่า เนื่องจากสิ่งพิมพ์ของฉันจะไม่มีอยู่หากไม่มีวารสาร


-1

ฉันเพิ่งเพิ่ม "action = action_name" ลงใน url และวิธีนี้เครื่องมือกำหนดเส้นทางจะรู้ว่าฉันต้องการการดำเนินการใด ฉันยังเพิ่มแอตทริบิวต์ ActionName ในการดำเนินการ แต่ไม่แน่ใจว่าจำเป็น


-1

คำอธิบายที่ดีที่สุดและง่ายที่สุดที่ฉันเคยเห็นในหัวข้อนี้ - http://www.binaryintellect.net/articles/9db02aa1-c193-421e-94d0-926e440ed297.aspx

  • แก้ไข -

ฉันทำให้มันใช้งานได้กับ Route เท่านั้นและไม่ต้องการ RoutePrefix

ตัวอย่างเช่นในคอนโทรลเลอร์

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

และ

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

จากนั้นชื่อฟังก์ชันจะอยู่ใน jquery เป็น -

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

หรือ

options.url = "/api/customer/PostCustomerAndOrder";
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.