如何正确执行RPC样式的Asp.Net Web API调用?

Tad*_*ghe 0 asp.net rest rpc asp.net-web-api

更新9/12/2012:

我和同事分享了我的代码,第一次没有任何改变,一切正常.所以,我的盒子上一定有环保的东西,任何人有什么想法?

请参阅下面的更新

建立:

.Net 4.5

自托管(控制台应用程序).​​Net 4.5 Web API应用程序

使用MSTest测试线束

我的Web API应用程序大多充满了REST ApiControllers,它们都能正常运行,就像我期望的标准CRUD类型一样.现在我有一个要求(将一些对象添加到内部队列),这似乎不适合REST CRUD模型.我发现这篇文章似乎说你可以在Web API中做RPC样式的非REST操作就好了.

我写了一个新的控制器,看起来像这样:

public class TaskInstanceQueueController : ApiController
{
    public void Queue(TaskInstance taskInstance)
    {
        // Do something with my taskInstance
        Console.WriteLine("Method entered!");
    }
}
Run Code Online (Sandbox Code Playgroud)

在我调用它的代理类中,我有如下代码:

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();
    }

}
Run Code Online (Sandbox Code Playgroud)

这是我的路线:

    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 });
Run Code Online (Sandbox Code Playgroud)

当我对我的代理运行测试时,我没有收到任何错误,但是我的控制器方法中没有出现任何断点,方法也没有输入!消息显示在控制台中.var msg行上的断行线也不会命中.无论出于何种原因,看起来我没有正确使用HttpClient对象来执行此操作.

同样,这个web api应用程序与其他一些apicontrollers一起正常工作,但他们都在做标准的REST工作.

有人有任何线索吗?

UPDATE

如果我在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
Run Code Online (Sandbox Code Playgroud)

第30行是通话线.

Mar*_*nes 6

这个答案取决于你定义了多少其他方法TaskInstanceQueueController.假设Queue是你唯一的那个,那么我相信你的路线已经可以工作了(尽管它们有点不整洁).

我刚刚构建了一个代码的示例版本,并设法成功发布到Queue方法并使用Fiddler和Curl点击了一个断点.我已经详细阐述了您的示例,并展示了如何将RPC操作与常规REST方法混合使用.

示例代码位于GitHub上这里.

基本上问题不是与WebApi元素(路由,配置等)有关,尽管您应该删除Optional id并将HttpPost属性添加到队列方法中,而是因为您的初始问题表明它是您调用服务器的方式这应该是另一个问题.

目前还不清楚你是否有两个项目以及如何托管MS测试代码等等......但是这里有一个很好的WebApi集成测试示例,你可以遵循这个例子,当使用像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();
        }
    }
Run Code Online (Sandbox Code Playgroud)

工作控制员

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!");
    }
}
Run Code Online (Sandbox Code Playgroud)