为什么我的0-arg和2-arg GET方法正确解析,而不是我的3-arg GET方法?

时间:2013-11-18 22:46:15

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

我可以调用我的0-arg GET方法,它工作正常;我的双arg GET方法也是如此;但是,当我向最后一个添加第三个arg时,服务器返回“404 - not found”。为什么它可以弄清楚,大概是基于方法定义(args传递的数量和类型),前两种情况下的正确路由,但不是最后一种?

以下是服务器应用程序中的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
    . . .

正如您所看到的,两个arg方法和三个arg方法之间的差别很小;但是前者是有效的,而后者则没有。为什么(不是)?

更新

要回答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的“name”成员只是一个ID,并且与项目中的任何其他内容都没有关联?我的意思是,我可以用一个新名称(例如“ DuckbilledPlatypiOfThePondsUnite ”)证明当前名为“ DefaultApiWith3Parameters ”的那个,它没有任何区别。

1 个答案:

答案 0 :(得分: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}"
);