Web Api 属性路由与 POST 请求不匹配

Posted

技术标签:

【中文标题】Web Api 属性路由与 POST 请求不匹配【英文标题】:Web Api attribute routing won't match POST request 【发布时间】:2019-11-25 17:04:37 【问题描述】:

我的控制器上的所有操作都有属性路由(尽管定义了默认的常规路由)。我已经(我认为)尝试了是否使用 [HttpPost] 以及是否使用 [FromBody] 的所有组合。

如果我尝试使用仅定义了控制器名称的属性...

[HttpPost]
[Route("Relationships")]
private async Task<IHttpActionResult> PostRelationship([FromBody]Relationship relationship)

Request URL:http://localhost:51599/api/Relationships
Request method:POST

...我得到一个 405。

它似乎正在使用属性路由 - 调试 GetRouteData() 显示了一个子路由,它正在解析为“GetController”操作。

subroutes   System.Web.Http.Routing.IHttpRouteData[1] System.Collections.Generic.IEnumerable<System.Web.Http.Routing.IHttpRouteData> System.Web.Http.Routing.IHttpRouteData[]
-       [0] System.Web.Http.Routing.HttpRouteData System.Web.Http.Routing.IHttpRouteData System.Web.Http.Routing.HttpRouteData
-           Route   System.Web.Http.Routing.HttpRoute System.Web.Http.Routing.IHttpRoute System.Web.Http.Routing.HttpRoute
    +           Constraints Count = 0   System.Collections.Generic.IDictionary<string, object> System.Web.Http.Routing.HttpRouteValueDictionary
    -           DataTokens  Count = 2   System.Collections.Generic.IDictionary<string, object> System.Web.Http.Routing.HttpRouteValueDictionary
        -       [0] [actions, System.Web.Http.Controllers.HttpActionDescriptor[]] System.Collections.Generic.KeyValuePair<string, object>
                    Key "actions"   string
            -       Value   System.Web.Http.Controllers.HttpActionDescriptor[1]   object System.Web.Http.Controllers.HttpActionDescriptor[]
                -       [0] System.Web.Http.Controllers.ReflectedHttpActionDescriptor System.Web.Http.Controllers.HttpActionDescriptor System.Web.Http.Controllers.ReflectedHttpActionDescriptor
                    +       ActionBinding   System.Web.Http.Controllers.HttpActionBinding System.Web.Http.Controllers.HttpActionBinding
                            ActionName  "GetRelationships"  string

如果我尝试使用带有动作名称的路由...

[HttpPost]
[Route("Relationships/PostRelationship")]
private async Task<IHttpActionResult> PostRelationship([FromBody]Relationship relationship)

http://localhost:51599/api/Relationships/PostRelationship
Request method:POST

...我得到一个 404。

在这种情况下,它似乎正在使用默认控制器 - 调试 GetRouteData() 表明它认为我正在提供“控制器”和“id”。

routeData   System.Web.Routing.RouteData  System.Web.Routing.RouteData
+       DataTokens  System.Web.Routing.RouteValueDictionary   System.Web.Routing.RouteValueDictionary
+       Route   System.Web.Http.WebHost.Routing.HttpWebRoute  System.Web.Routing.RouteBase System.Web.Http.WebHost.Routing.HttpWebRoute
+       RouteHandler    System.Web.Http.WebHost.HttpControllerRouteHandler    System.Web.Routing.IRouteHandler System.Web.Http.WebHost.HttpControllerRouteHandler
-       Values  System.Web.Routing.RouteValueDictionary   System.Web.Routing.RouteValueDictionary
            Count   2   int
    -       Keys    Count = 2   System.Collections.Generic.Dictionary<string, object>.KeyCollection
                [0] "controller"    string
                [1] "id"    string  
    -       Values  Count = 2   System.Collections.Generic.Dictionary<string, object>.ValueCollection
                [0] "Relationships" object string
                [1] "PostRelationship"  object string

这是整个控制器:

    [RoutePrefix("api")]
    public class RelationshipsController : ApiController
    
        private CMDBContext db = new CMDBContext();

        [Route("Relationships")]
        public IQueryable<Relationship> GetRelationships()
        
            return db.Relationships;
        

        [Route("Relationships/id:int")]
        private async Task<Relationship> GetRelationship(int id)
        
            Relationship relationship = await db.Relationships.FindAsync(id);
            if (relationship == null)
            
                throw new HttpResponseException(HttpStatusCode.NotFound);
            
            return relationship;
        

        [HttpPost]
        [Route("Relationships/PostRelationship")]
        private async Task<IHttpActionResult> PostRelationship([FromBody]Relationship relationship)
        
            if (!ModelState.IsValid)
            
                return BadRequest(ModelState);
            

            db.Relationships.Add(relationship);
            await db.SaveChangesAsync();

            return CreatedAtRoute("DefaultApi", new  id = relationship.Id , relationship);
        

        [HttpGet]
        [Route("Relationships/RelTypes")]
        public Dictionary<string, string> RelTypes()  // returns a list of available Relationship types
        
            var RelTypesDict = new Dictionary<string, string>();
            foreach (var type in Relationship.RelTypes)
            
                RelTypesDict.Add(type, Regex.Replace(type, @"(\B[A-Z]+?(?=[A-Z][^A-Z])|\B[A-Z]+?(?=[^A-Z]))", " $1")); // expands camel case with spaces for display
            
            return RelTypesDict;
        

        private bool RelationshipExists(int id)
        
            return db.Relationships.Count(e => e.Id == id) > 0;
        
    

和 WebApiConfig:

// Web API routes
config.MapHttpAttributeRoutes();

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

以及我用来调用它的 AJAX:

async function postEdge()   // persists a new edge to data store from form
    var ItemLeftId = $('#ItemLeftId').val();
    var ItemRightId = $('#ItemRightId').val();
    var RelType = $('#ddlRelType').val();
    var Description = $('#Description').val();
    $.ajax(
        method: "POST",
        contentType: "application/json; charset=utf-8",
        url: '/api/Relationships',
        data: 
            Id: 0,  // TESTING
            ItemLeftId: ItemLeftId,
            ItemRightId: ItemRightId,
            RelType: RelType,
            Description: Description,
        , 

【问题讨论】:

【参考方案1】:

我想通了——如果你看上面的话,GetRelationship 和 PostRelationship 方法都设置为 Private。这是我从另一个基类继承关系并使用不同的公共方法进行区分时遗留下来的。愚蠢的错误,但如果症状看起来不那么奇怪/无关,就会更容易发现。

【讨论】:

以上是关于Web Api 属性路由与 POST 请求不匹配的主要内容,如果未能解决你的问题,请参考以下文章

Asp.Net Web APi 路由的特点

在 Web Api 中找到多个与请求匹配的操作

在 Web Api 中找到多个与请求匹配的操作

API ASP.NET 5“POST”成功,但出现错误“没有路由与提供的值匹配”

Express的使用方法,路由的匹配与使用

.net web api 一