如何让 MVC RPC 风格的 API 控制器与多个 POST 方法一起工作

Posted

技术标签:

【中文标题】如何让 MVC RPC 风格的 API 控制器与多个 POST 方法一起工作【英文标题】:How to get MVC RPC style API Controller working with multiple POST methods 【发布时间】:2014-05-17 02:38:29 【问题描述】:

我知道这个问题已经在很多领域被问过和回答,因为我搜索并看到了许多不同的答案和示例;但是,我无法应用其中任何一个并使其正常工作。

我正在尝试在 MVC 4 中组合一个 RPC 样式的 apicontroller。在 aipcontroller 中,我尝试执行发送和接收 JSon 的两种方法:TestFunction1 和 TestFunction2。如果我注释掉其中一个,另一个就可以正常工作。

当我尝试同时包含它们时,它会失败并出现以下(常见)错误:

Multiple actions were found that match the request:
    TestFunction1 on type test.api.TestController
    TestFunction2 on type test.api.TestController"

尽我所能,我正在遵循我找到的示例,并且我已经阅读了 SO QnAs,直到我失明并且我仍然无法弄清楚我做错了什么。

有人可以帮我解决和理解这个问题吗?

编辑:附加说明 - 如果我注释掉 TestFunction2 并且不更改我的 javascript 操作以匹配,我仍然会收到对 TestFunction1 的调用。

我的控制器方法:

    [HttpPost, ActionName("TestFunction1")]
    public HttpResponseMessage TestFunction1(SomeModel somemodel)
       SomeModel otherModel = new SomeModel() 
            id = 7,
            username = "other API-PostSomeModel Username",
            email = "other API-PostSomeModel email",
            password = "other API-PostSomeModel passowrd"
        ;

        if (ModelState.IsValid) 
            HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, somemodel);
            var serializer = new JavaScriptSerializer();

            response.Headers.Location = new Uri(Url.Link("DefaultApi", new  id = somemodel.id ));
            response.Content = new StringContent(serializer.Serialize(otherModel));

            return response;
        
        else 
            return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
        
    

    [HttpPost, ActionName("TestFunction2")]
    public HttpResponseMessage TestFunction2(SomeModel somemodel)
       SomeModel otherModel = new SomeModel() 
            id = 7,
            username = "other API-TestFunction Username",
            email = "other API-TestFunction email",
            password = "other API-TestFunction passowrd"
        ;

        var serializer = new JavaScriptSerializer();
        var resp = new HttpResponseMessage() 
            Content = new StringContent(serializer.Serialize(otherModel))
        ;

        resp.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

        return resp;
    

型号:

public class SomeModel
   [Key]
    public long id  get; set; 
    public string username  get; set; 
    public string password  get; set; 
    public string email  get; set; 

    public SomeModel() 
    public SomeModel(long _id, string usrname, string pswd, string _email) 
       id = _id;
        username = usrname;
        password = pswd;
        email = _email;
       

我的路线:

    public static void RegisterRoutes(RouteCollection routes)
    
        routes.IgnoreRoute("resource.axd/*pathInfo");

        //Route Catches the GET PUT DELETE typical REST based interactions (add more if needed)
        routes.MapHttpRoute(
            name: "API Default",
            routeTemplate: "api/controller/id",
            defaults: new 
                id = RouteParameter.Optional,
                httpMethod = new HttpMethodConstraint("GET", "PUT", "DELETE")
            
        );

        //This allows POSTs to the RPC Style methods http://api/controller/action
        routes.MapHttpRoute(
            name: "API RPC Style",
            routeTemplate: "api/controller/action",
            defaults: new 
                httpMethod = new HttpMethodConstraint("POST"),  
            
        );

        //Finally this allows POST to typical REST post address http://api/controller/
        routes.MapHttpRoute(
            name: "API Default 2",
            routeTemplate: "api/controller/action",
            defaults: new 
                httpMethod = new HttpMethodConstraint("POST"),  
            
        );

        routes.MapRoute(
             name: "Default",
             url: "controller/action/id",
             defaults: new  controller = "Home", action = "Index", id = UrlParameter.Optional 
        );
    

我的javascript:

 function jsTestFunction(id, username, password, email) 
    alert("Verify data:" + "\n\r" + id + "\n\r" + username + "\n\r" + password + "\n\r" + email);
    var obj =  
        'id' : id,
        'username' : username,
        'password' : password,
        'email' : email,
    ;
    var postdata = JSON.stringify(obj);
    $.ajax(
        type: "POST",
        url: "api/Test/TestFunction2/",
        dataType: "json",
        traditional: true,
        data: postdata,
        contentType: "application/json; charset=utf-8",
        success: function (responsedata) 
            var temp0 = responsedata['id'];
            var temp1 = responsedata['username'];
            var temp2 = responsedata['password'];
            var temp3 = responsedata['email'];
            if (!responsedata.error) 
                alert("The process was successful: " + "\n\r" + temp0 + "\n\r" + temp1 + "\n\r" + temp2 + "\n\r" + temp3);
            
            else 
                alert("An error occured during the process: " + responsedata.msg);
                $('#reservation-result').html("Error :" + responsedata.msg).fadeIn(2000);
            
        ,
        error: function (error) 
            alert("There was an error posting the data to the server: " + error.responseText);
        
    );
 

【问题讨论】:

【参考方案1】:

我终于修好了。很明显,问题出在路由中,当我在 javascript 中调用 TestFunction2 时,我可以注释掉 TestFunction2 并仍然调用 TestFunction1。

我还注意到我在 RouteConfig.cs 中有我的映射。这可能与问题无关;除了,它应该在 WebApiConfig 中正确执行。在功能上,我认为这与它没有任何关系。

当我将“API RPC 样式”路由的顺序与“API 默认”交换时,最终解决了这个问题。然后它立即开始工作。现在我可以以 RPC 方式执行 API POST 调用并且它可以正常工作。

我不能说我完全了解路由机制;但是,我知道顺序很重要。它是选择第一个匹配,而不是最佳匹配。在这种情况下,这是有道理的;因此,我将使用它。

【讨论】:

以上是关于如何让 MVC RPC 风格的 API 控制器与多个 POST 方法一起工作的主要内容,如果未能解决你的问题,请参考以下文章

浅谈四种API设计风格(RPCRESTGraphQL服务端驱动)

API设计风格(RRCRESTGraphQL服务端驱动)

ASP.NEt MVC 使用 Web API 返回 Razor 视图

如何使用 JWT 令牌 ASP.NET MVC

SpringMVC总结帖

SpringMVC总结篇