【问题标题】:Why are my 0-arg and 2-arg GET methods resolved correctly, but not my 3-arg GET method?为什么我的 0-arg 和 2-arg GET 方法可以正确解析,但我的 3-arg GET 方法不能正确解析?
【发布时间】:2013-11-18 22:46:14
【问题描述】:

我可以调用我的 0-arg GET 方法,它工作正常;我的双参数 GET 方法也是如此;但是,当我将第三个参数添加到最后一个参数时,服务器返回“404 - 未找到”。为什么它可以根据方法定义(传递的参数的数量和类型)确定前两种情况下的正确路由,而不是最后一种情况?

以下是服务器应用程序中的 Web API Rest 定义:

存储库接口:

interface IInventoryItemRepository
{
    int Get();

    IEnumerable<InventoryItem> Get(string ID, int CountToFetch);

    IEnumerable<InventoryItem> Get(string ID, string packSize, int CountToFetch);

    InventoryItem Add(InventoryItem item);
}

存储库接口实现/具体类:

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

    public InventoryItemRepository()
    {
        string lastGoodId = string.Empty;
        string id = string.Empty;

        try
        {
            using (var conn = new OleDbConnection(
                @"Provider=Microsoft.Jet.OLEDB.4.0;User ID=PlatypusBrain;Password=Platydude;Data Source=C:\XLWStuff\DATA\XLWDAT03.MDB;Jet OLEDB:System database=C:\XLWWin\Data\abcd.mdw"))
            {
                using (var cmd = conn.CreateCommand())
                {
    . . .
                            Add(new InventoryItem
                            {
    . . .
                            });
                        } // while
. . .
    } // InventoryItemRepository (constructor)

    public int Get()
    {
        return inventoryItems.Count;
    }

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

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

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

库存物品控制器:

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

    public int GetCountOfInventoryItems()
    {
        return inventoryItemsRepository.Get();
    }

    public IEnumerable<InventoryItem> GetBatchOfInventoryItemsByStartingID(string ID, int CountToFetch)
    {
        return inventoryItemsRepository.Get(ID, CountToFetch);
    }

    public IEnumerable<InventoryItem> GetBatchOfInventoryItemsByStartingID(string ID, string packSize, int CountToFetch)
    {
        return inventoryItemsRepository.Get(ID, packSize, CountToFetch);
    }

}

...以及来自客户端应用程序的调用:

// 0-arg method (count)
private void buttonGetInvItemsCount_Click(object sender, EventArgs e)
{
    labelInvItemsCount.Text = string.Format("== {0}", getInvItemsCount());
}

private int getInvItemsCount()
{
    int recCount = 0;
    const string uri = "http://localhost:28642/api/InventoryItems";
    var webRequest = (HttpWebRequest)WebRequest.Create(uri);
    webRequest.Method = "GET";
    using (var webResponse = (HttpWebResponse)webRequest.GetResponse())
    {
        if (webResponse.StatusCode == HttpStatusCode.OK)
        {
            var reader = new StreamReader(webResponse.GetResponseStream());
            string s = reader.ReadToEnd();
            Int32.TryParse(s, out recCount);
        }
    }
    return recCount;
}

    // 2-arg method:
    string lastIDFetched = "0";
    const int RECORDS_TO_FETCH = 100;
    int recordsToFetch = getInvItemsCount();
    bool moreRecordsExist = recordsToFetch > 0;
    int totalRecordsFetched = 0;

    while (moreRecordsExist)
    {
        string formatargready_uri = string.Format("http://localhost:28642/api/InventoryItems/{0}/{1}", lastIDFetched, RECORDS_TO_FETCH);
        var webRequest = (HttpWebRequest)WebRequest.Create(formatargready_uri);
        webRequest.Method = "GET";
        using (var webResponse = (HttpWebResponse)webRequest.GetResponse())
        {
            if (webResponse.StatusCode == HttpStatusCode.OK)
            {
                var reader = new StreamReader(webResponse.GetResponseStream());
                string s = reader.ReadToEnd();
                var arr = JsonConvert.DeserializeObject<JArray>(s);

                foreach (JObject obj in arr)
                {
                    var id = (string)obj["Id"];
                    lastIDFetched = id;
                    int packSize = (Int16)obj["PackSize"];
                    var description = (string)obj["Description"];
                    int dept = (Int16)obj["DeptSubdeptNumber"];
                    int subdept = (Int16)obj["InvSubdepartment"];
                    var vendorId = (string)obj["InventoryName"];
                    var vendorItem = (string)obj["VendorItemId"];
                    var avgCost = (Double)obj["Cost"];
                    var unitList = (Double)obj["ListPrice"];

                    inventoryItems.Add(new WebAPIClientUtils.InventoryItem
                    {
                        Id = id,
                        InventoryName = vendorId,
                        UPC_PLU = vendorId,
                        VendorItemId = vendorItem,
                        PackSize = packSize,
                        Description = description,
                        Quantity = 0.0,
                        Cost = avgCost,
                        Margin = (unitList - avgCost),
                        ListPrice = unitList,
                        DeptSubdeptNumber = dept,
                        InvSubdepartment = subdept
                    });
                } // foreach
            } // if ((webResponse.StatusCode == HttpStatusCode.OK) && (webResponse.ContentLength > 2))
        } // using HttpWebResponse
        int recordsFetched = WebAPIClientUtils.WriteRecordsToMockDatabase(inventoryItems, hs);
        label1.Text += string.Format("{0} records added to mock database at {1}; ", recordsFetched, DateTime.Now.ToLongTimeString());
        totalRecordsFetched += recordsFetched;
        moreRecordsExist = (recordsToFetch > (totalRecordsFetched+1)); //  <-- I think recordsFetched will be a max of 269, instead of 270, so will have to fix that...
    } // while
    if (inventoryItems.Count > 0)
    {
        dataGridViewGETResults.DataSource = inventoryItems;
    }
}

            // 3-arg method; the three differences between this and the 2-arg method above are commented.
            string lastIDFetched = "0";
            string lastPackSizeFetched = "1"; // <-- This is new/different from the 2-arg method
            const int RECORDS_TO_FETCH = 100;
            int recordsToFetch = getInvItemsCount();
            bool moreRecordsExist = recordsToFetch > 0;
            int totalRecordsFetched = 0;

            while (moreRecordsExist)
            {
// A third format arg is added, differening from the 2-arg method
                string formatargready_uri = string.Format("http://localhost:28642/api/InventoryItems/{0}/{1}/{2}", lastIDFetched, lastPackSizeFetched, RECORDS_TO_FETCH);
                var webRequest = (HttpWebRequest)WebRequest.Create(formatargready_uri);
                webRequest.Method = "GET";
                using (var webResponse = (HttpWebResponse)webRequest.GetResponse())
                {
                    if (webResponse.StatusCode == HttpStatusCode.OK)
                    {
                        var reader = new StreamReader(webResponse.GetResponseStream());
                        string s = reader.ReadToEnd();
                        var arr = JsonConvert.DeserializeObject<JArray>(s);

                        foreach (JObject obj in arr)
                        {
                            var id = (string)obj["Id"];
                            lastIDFetched = id;
                            int packSize = (Int16)obj["PackSize"];
                            lastPackSizeFetched = packSize.ToString(); // <-- this is the final difference in this method compared to the 2-arg method
    . . .

如您所见,二参数方法和三参数方法之间几乎没有区别;但前者有效,后者无效。为什么(不)?

更新

要在下面回答 Kiran Challa 的评论/问题,以下是 RouteConfig.cs 中的内容:

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

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

...和 ​​WebApiConfig.cs:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services

        // Web API routes
        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 }
            defaults: new { ID = RouteParameter.Optional, CountToFetch = RouteParameter.Optional }
        );

    }
}

更新 2

我将此添加到 WebApiConfig.cs:

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

...现在可以使用了。去搞清楚;不需要装饰方法 - 也许这就是我之前搞砸的原因,是我试图将装饰/注释/属性添加到方法和 WebApiConfig 的条目。

我不知道,但在被证明是错误的之前,我会避开那些大肆宣传的属性装饰。

现在我将测试 Tim S. 关于可选位的 86 位的建议……效果很好。

我认为 WebApiConfig.cs 中每个 MapHttpRoute 的“名称”成员只是一个 ID,与项目中的其他任何内容都没有关系?我的意思是,我可以用诸如“DuckbilledPlatypiOfThePondsUnite”之类的新名称来证明当前名为“DefaultApiWith3Parameters”的那个,这没有任何区别。

【问题讨论】:

  • 你能分享一下你的路由配置是什么样子的吗?
  • 路由配置我什么都没做;我之前曾对此进行过测试,但从未让它达到我的预期。通过将其保留为/空白,它会更好地工作,并且它找出了调用 0-arg 和 2-arg 方法的方法。不过,现在 3 失败了。

标签: c# rest asp.net-web-api get httpwebrequest


【解决方案1】:

您的路由配置声明api/InventoryItems/{0}/{1} 映射到IDCountToFetch,但没有说明api/InventoryItems/{0}/{1}/{2} 应该映射到ID, packSize, CountToFetch。此外,您可能并不真的想让这些参数中的任何一个完全可选。试试这个:

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

config.Routes.MapHttpRoute(
    name: "DefaultApiWith2Parameters",
    routeTemplate: "api/{controller}/{ID}/{CountToFetch}"
);

config.Routes.MapHttpRoute(
    name: "DefaultApiWith3Parameters",
    routeTemplate: "api/{controller}/{ID}/{packSize}/{CountToFetch}"
);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多