【发布时间】: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