更新9/12/2012:
我与同事分享了我的代码,第一次一切正常,没有任何变化。所以,我的盒子上一定有环保的东西,任何人有什么想法吗?
请参阅下面的更新
设置:
.Net 4.5
自托管(控制台应用).Net 4.5 Web API应用
使用MSTest测试线束
我的Web API应用程序大部分都是REST ApiControllers,它们都可以像我期望的那样使用标准的CRUD类型。现在我有一个要求(将一些对象添加到内部队列),这似乎并不适合REST CRUD模型。我发现this文章似乎说你可以在Web API中完成RPC样式的非REST操作。
我写了一个新的控制器,看起来像这样:
public class TaskInstanceQueueController : ApiController
{
public void Queue(TaskInstance taskInstance)
{
// Do something with my taskInstance
Console.WriteLine("Method entered!");
}
}
在我调用它的代理类中,我的代码如下所示:
public class TaskInstanceQueueProxy : ITaskInstanceQueueProxy
{
readonly HttpClient _client = new HttpClient();
public TaskInstanceQueueProxy()
{
var apiBaseUrl = System.Configuration.ConfigurationManager.AppSettings["APIBaseUrl"];
_client.BaseAddress = new Uri(apiBaseUrl);
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public void QueueTaskInstances(TaskInstance taskInstance)
{
QueueTaskInstanceViaAPI(taskInstance);
}
private async void QueueTaskInstanceViaAPI(TaskInstance taskInstance)
{
var response = await _client.PostAsJsonAsync("api/TaskInstanceQueue/Queue", taskInstance);
var msg = response.EnsureSuccessStatusCode();
}
}
以下是我的路线:
config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}", new {id = RouteParameter.Optional});
config.Routes.MapHttpRoute("API RPC Style", "api/{controller}/{action}", new { id = RouteParameter.Optional });
当我对我的代理进行测试时,我没有收到任何错误,但是我的控制器方法中没有出现任何断点,方法也没有输入!消息显示在控制台中。 var msg行上的断行线也不会命中。无论出于何种原因,看起来我没有正确使用HttpClient对象来执行此操作。
同样,这个web api应用程序与其他一些apicontrollers一起工作得很好,但他们都在做标准的REST工作。
任何人都有线索吗?
更新
如果我在PostAsJsonAsync调用周围放置一个try / catch,我会得到以下内容:
A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll
System.Threading.ThreadAbortException: Thread was being aborted.
at System.Threading.Tasks.TaskHelpers.RunSynchronously(Action action, CancellationToken token)
at System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext)
at System.Net.Http.ObjectContent.SerializeToStreamAsync(Stream stream, TransportContext context)
at System.Net.Http.HttpContent.LoadIntoBufferAsync(Int64 maxBufferSize)
at System.Net.Http.HttpClientHandler.PrepareAndStartContentUpload(RequestState state)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at TaskManagerProxy.TaskInstanceQueueProxy.<QueueTaskInstanceViaAPI>d__0.MoveNext() in c:\Moso\MOSO\MOSO.Infrastructure\tm\TaskManagerProxy\TaskManagerProxy\TaskInstanceQueueProxy.cs:line 30
第30行是通话线路。
答案 0 :(得分:6)
这个答案取决于你在TaskInstanceQueueController
中定义了多少其他方法。假设Queue是你唯一的那个,那么我相信你的路线已经可以工作了(尽管它们有点不整洁)。
我刚刚构建了一个代码的示例版本,并设法成功发布到Queue方法并使用Fiddler和Curl点击了一个断点。我已经详细阐述了您的示例,并展示了如何将RPC操作与常规REST方法混合使用。
示例代码位于GitHub here。
基本上问题不是与WebApi元素(路由,配置等)有关,尽管您应该删除Optional id并将HttpPost属性添加到队列方法中,而是因为您的初始问题表明它是您的方式调用服务器,这应该是另一个问题。
目前还不清楚你是否有两个项目以及如何托管MS测试代码等等?但是有一个很好的例子WebApi integration test here你可以遵循以及在使用像Fiddler这样的工具调试API时可以快速帮助消除和调试路由配置问题。
工作控制台程序:
static void Main(string[] args)
{
// Set up server configuration
HttpSelfHostConfiguration config = new HttpSelfHostConfiguration("http://localhost:8080");
//Route Catches the GET PUT DELETE typical REST based interactions (add more if needed)
config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional },
new { httpMethod = new HttpMethodConstraint(HttpMethod.Get, HttpMethod.Put, HttpMethod.Delete) });
//This allows POSTs to the RPC Style methods http://api/controller/action
config.Routes.MapHttpRoute("API RPC Style", "api/{controller}/{action}",
new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) });
//Finally this allows POST to typeical REST post address http://api/controller/
config.Routes.MapHttpRoute("API Default 2", "api/{controller}/{action}",
new { action = "Post" },
new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) });
using (HttpSelfHostServer server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
Console.WriteLine("Press Enter to quit.");
Console.ReadLine();
}
}
工作控制器
public class TaskInstanceQueueController : ApiController
{
public void Get(string id)
{
// Do something with my taskInstance
Console.WriteLine("Method entered!" + id);
}
[ActionName("Post")]
[HttpPost]
public void Post(TaskInstance taskInstance)
{
// Do something with my taskInstance
Console.WriteLine("REST Post Method entered!");
}
[ActionName("Queue")]
[HttpPost]
public void Queue(TaskInstance taskInstance)
{
// Do something with my taskInstance
Console.WriteLine("Queue Method entered!");
}
[ActionName("Another")]
[HttpPost]
public void Another(TaskInstance taskInstance)
{
Console.WriteLine("Another Method entered!");
}
}
答案 1 :(得分:4)
你有不明确的路线。
config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}", new {id = RouteParameter.Optional});
config.Routes.MapHttpRoute("API RPC Style", "api/{controller}/{action}", new { id = RouteParameter.Optional });
当请求进入/ api / TaskInstanceQueue / Queue时,它匹配第一个路由,因此路由数据包含{controller =“TaskInstanceQueue”,id =“Queue”}。系统然后尝试发现Post方法,但不能这样做,因为您没有Post(或PostXxx)方法,因此您的HTTP调用失败。
你有几个选择。一个是你可以为这个控制器设置一个显式路由(第一个):
config.Routes.MapHttpRoute("API RPC Style", "api/TaskInstanceQueue/{action}", new { controller = "TaskInstanceQueue" });
config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}", new {id = RouteParameter.Optional});
另一种情况是,如果您知道您的ID将始终只是数字,您可以为基于ID的路由添加约束,这将导致它与“队列”不匹配作为ID,因此落入正确的基于行动的路线。