【问题标题】:Why is my Web API routing being re-routed / falsely routed?为什么我的 Web API 路由被重新路由/错误路由?
【发布时间】:2013-11-26 14:42:02
【问题描述】:

我有两个针对特定控制器/存储库的 GET 方法:

public IEnumerable<InventoryItem> GetAllInventoryItems()
{
    return inventoryItemsRepository.GetAll();
}

[Route("api/{controller}/{ID}/{CountToFetch}")]
public IEnumerable<InventoryItem> GetBatchOfInventoryItemsByStartingID(string ID, int CountToFetch)
{
    return inventoryItemsRepository.Get(ID, CountToFetch); 
}

尽管我已经尝试从客户端以所有这些方式调用它,但有两个参数:

0)
formatargready_uri = string.Format("http://localhost:28642/api/inventoryItems/{0}/{1}", lastIDFetched, RECORDS_TO_FETCH);
var webRequest = (HttpWebRequest)WebRequest.Create(formatargready_uri); 

1)
formatargready_uri = string.Format("http://localhost:28642/api/inventoryItems/?ID={0}&CountToFetch={1}", lastIDFetched, RECORDS_TO_FETCH);
var webRequest = (HttpWebRequest)WebRequest.Create(formatargready_uri); 

2)
formatargready_uri = string.Format("http://localhost:28642/api/inventoryItems/ID={0}&CountToFetch={1}", lastIDFetched, RECORDS_TO_FETCH);
var webRequest = (HttpWebRequest)WebRequest.Create(formatargready_uri); 

...在每种情况下,它仍然是第一个被调用的方法 (GetAll)。为什么?

这是我的存储库代码:

public IEnumerable<InventoryItem> GetAll()
{
    return inventoryItems;
}

public IEnumerable<InventoryItem> Get(string ID, int CountToFetch)
{
    return inventoryItems.Where(i => 0 < String.Compare(i.Id, ID)).Take(CountToFetch);
}

...这是 WebApiConfig.cs 中的内容:

public static void Register(HttpConfiguration config)
{
    config.MapHttpAttributeRoutes();

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

    config.Routes.MapHttpRoute(
        name: "DefaultApiWithParameters",
        routeTemplate: "api/{controller}/{ID}/{CountToFetch}",
        defaults: new { ID = RouteParameter.Optional, CountToFetch = RouteParameter.Optional }
    );

}

更新

这将显示我尝试将调用路由到我尝试运行的控制器方法:

//[HttpGet]
//[Route("api/{controller}/{ID:string}/{CountToFetch:int}")] <-- throws exception - won't run
//[Route("{controller}/{ID:string}/{CountToFetch:int}")] <-- throws exception - won't run
//[Route("inventoryItems/{ID:string}/{CountToFetch:int}")] <-- throws exception - won't run
//[Route("api/inventoryItems/{ID:string}/{CountToFetch:int}")] <-- throws exception - won't run
//[Route("api/{controller}/{ID}/{CountToFetch}")] // <-- runs, but is not called
[Route("api/InventoryItemsController/{ID}/{CountToFetch}")] // <-- runs, but is not called
//[Route("api/{controller}/{ID:string}/{CountToFetch:int}")] <-- throws exception - won't run

所以我不想调用的方法非常健壮:无论我如何装饰另一个方法,或者我如何调用它,不想要的方法都会运行。

更新 2

只允许通过名称调用控制器方法不是更容易吗?例如,给定这个 Controller 方法:

public IEnumerable<InventoryItem> GetBatchOfInventoryItemsByStartingID(string ID, int CountToFetch)
{
    return inventoryItemsRepository.Get(ID, CountToFetch); //.Where(i => string.Equals(p.Category, category, StringComparison.OrdinalIgnoreCase));
}

...为什么 [c,w] 不能像这样从客户端调用它:

formatargready_uri = string.Format("http://localhost:28642/api/InventoryItemsController.GetBatchOfInventoryItemsByStartingID/{0}/{1}", lastIDFetched, RECORDS_TO_FETCH);

???

类似的 ISTM 会更加直观。

更新 3

所以归结为:为什么我的 GetAll() 方法在没有参数的情况下被调用?

可能由于 lastIDFetched 设置为空字符串,路由机制变得混乱:

string lastIDFetched = string.Empty;

...然后formatargready_uri。这是分配给这种方式:

formatargready_uri = string.Format("http://locohost:28642/api/InventoryItems/{0}/{1}", lastIDFetched, RECORDS_TO_FETCH);

...起初是:

"http://locohost:28642/api/InventoryItems//100"

(当我期望它是:

"http://locohost:28642/api/InventoryItems/""/100"

)

可能是“丢失”的第一个参数是什么导致路由机制关闭,所以当它看到时:

"http://locohost:28642/api/InventoryItems//100" 

...不知道要不要这样调用:

public IEnumerable<InventoryItem> GetBatchOfInventoryItemsByStartingID(string ID, int CountToFetch)
{
    return inventoryItemsRepository.Get(ID, CountToFetch); //.Where(i => string.Equals(p.Category, category, 

StringComparison.OrdinalIgnoreCase)); }

...或者这个:

public IEnumerable<InventoryItem> GetAllInventoryItems()
{
    return inventoryItemsRepository.GetAll();
}

???

更新 4

当我注释掉另一个方法时,让客户端别无选择,只能看到 Controller/Repository 中唯一存在的方法,它在这一行什么都不做:

var webRequest = (HttpWebRequest)WebRequest.Create(formatargready_uri);

(Controller中的双参数方法仍然没有被调用)

这就是控制器中的全部内容:

public class InventoryItemsController : ApiController
{
    static readonly IInventoryItemRepository inventoryItemsRepository = new InventoryItemRepository();

    [Route("api/InventoryItems/{ID}/{CountToFetch:int}")] // <-- with this route decoration commented out or not, makes no difference
    public IEnumerable<InventoryItem> GetBatchOfInventoryItemsByStartingID(string ID, int CountToFetch)
    {
        return inventoryItemsRepository.Get(ID, CountToFetch); //.Where(i => string.Equals(p.Category, category, StringComparison.OrdinalIgnoreCase));
    }    
}

下面是对应的Repository接口:

interface IInventoryItemRepository
{
    IEnumerable<InventoryItem> Get(string ID, int CountToFetch); 
    InventoryItem Add(InventoryItem item);
}

...存储库实现:

public class InventoryItemRepository : IInventoryItemRepository
{
    private readonly List<InventoryItem> inventoryItems = new List<InventoryItem>();

    public InventoryItemRepository()
    {
// code that populates inventoryItems by calling Add() not shown - it works, though
    } 

    public IEnumerable<InventoryItem> Get(string ID, int CountToFetch)
    {
        return inventoryItems.Where(i => 0 < String.Compare(i.Id, ID)).Take(CountToFetch);
    }

    public InventoryItem Add(InventoryItem item)
    {
        if (item == null)
        {
            throw new ArgumentNullException("item");
        }
        inventoryItems.Add(item);
        return item;
    }        
}

...以及调用它的客户端代码:

formatargready_uri = string.Format("http://localhost:28642/api/InventoryItems/{0}/{1}", lastIDFetched, RECORDS_TO_FETCH);
var webRequest = (HttpWebRequest)WebRequest.Create(formatargready_uri);

更新 5

好吧,我会像一只破袜子一样被缝补。毕竟,从空字符串开始似乎是个问题。当我将 lastIDFetched 的初始值从 string.Empty 更改为“billy”时,它起作用了……这是一个错误吗?有解决方法吗?如果我想从“scratch”开始,我会使用什么而不是 string.Empty?空格 (" ") 也不起作用。

【问题讨论】:

    标签: rest controller routing repository asp.net-mvc-routing


    【解决方案1】:

    首先,你为什么在下面的属性路由中有{controller}?通过在此处使用属性路由进行装饰,您已经指示了应该命中的控制器和操作,因此请在此处删除 {controller} 并将其替换为控制器名称。

    [Route("api/{controller}/{ID}/{CountToFetch}")]
    public IEnumerable<InventoryItem> GetBatchOfInventoryItemsByStartingID(string ID, int  CountToFetch)
    {
       return inventoryItemsRepository.Get(ID, CountToFetch); 
    }
    

    您使用查询字符串的请求 1) 和 2) 将不起作用,因为 ID 和 CountToFetch 是必需的路由参数。

    【讨论】:

    • "在此处移除 {controller} 并将其替换为控制器名称。"想一想,为什么需要它?既然在Controller本身,指定它不应该是多余的吗?
    • 我不明白为什么需要的参数会阻止它们通过 string.Format() 添加,如果这就是你的意思的话。
    • ""把这里的{controller}去掉,换成控制器名。"你说应该是(类名是InventoryItemsController):[Route("api/InventoryItemsController/{ID}/{ CountToFetch}")]
    • 我的意思是,如果您的意图是让用户提出像 api/inventoryItems/.../../ 这样的请求,那么您需要使用 'inventoryItems' 而不是这里的“变量”{controller}
    • 我已经试过了;我的各种排列都不起作用 - 我所做的一切都会导致“通用” GET 方法被调用。我第一次更新中被注释掉的部分显示了我在这些方面尝试过的各种事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-24
    • 1970-01-01
    • 2018-07-09
    • 2017-09-02
    • 2016-12-06
    • 2020-06-01
    • 1970-01-01
    相关资源
    最近更新 更多