0

2012 年 9 月 12 日更新:

我与一位同事分享了我的代码,第一次对他来说一切正常,没有任何更改。所以,我的盒子上一定有一些环保的东西,有人有什么想法吗?

请参阅下面的更新

设置:

.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!");
    }
}

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

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 应用程序与许多其他 apicontroller 一起工作得很好,但它们都在做标准的 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 行是呼叫所在的行。

4

2 回答 2

6

这个答案确实取决于您在TaskInstanceQueueController. 假设 Queue 是您唯一的一个,那么我相信您的路线已经可以使用(尽管它们有点不整洁)。

我刚刚构建了您的代码的示例版本,并成功使用 Fiddler 和 Curl 成功发布到 Queue 方法并达到断点。我已经详细说明了您的示例,并展示了如何将 RPC 操作与普通 REST 方法混合。

示例代码位于 GitHub上

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

目前尚不清楚您是否有两个项目以及如何托管 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();
        }
    }

工作控制器

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!");
    }
}
于 2012-09-12T08:14:28.717 回答
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,因此落入正确的动作 -基于路线。

于 2012-09-12T01:01:43.620 回答