I'm trying to create a self-hosted Web service that starts its server with Microsoft.Owin.Hosting.WebApp.Start<NotifyStartup>("http://localhost:9000/")
and contains a controller derived from System.Net.Http.ApiController
. NotifyStartup
looks like this:
using System.Web.Http;
using Owin;
...
class NotifyStartup
{
public void Configuration(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
appBuilder.UseWebApi(config);
}
}
The controller has this URI handler:
using System.Web.Http;
...
[Route("notify")]
[HttpPost]
public IHttpActionResult Notify([FromBody]object body)
{
return new HttpAction();
}
Here's HttpAction:
using System.Net.Http;
using System.Web.Http;
using System.Threading;
using System.Threading.Tasks;
...
public class HttpAction : IHttpActionResult
{
public HttpAction() { }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return new Task<HttpResponseMessage>(() =>
{
var rspContent = "here's a response string";
var rsp = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
if (!string.IsNullOrEmpty(rspContent))
{
rsp.Content = new StringContent(rspContent);
}
return rsp;
}, cancellationToken);
}
}
(At some point HttpAction
will pay attention to Notify()
's body
parameter and rspContent
will be assigned something requiring some database lookups, which is why I'm trying to make this work asynchronously.)
When I run the program and POST to http://localhost:9000/notify/ the URI handler is called, it creates an HttpAction
instance and that instance's ExecuteAsync()
method is called. However, the task it returns is never run, and the client hangs waiting for a response. If I change ExecuteAsync()
so that the work is done synchronously and the response returned in a wrapper task:
var rspContent = "here's a response string";
var rsp = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
if (!string.IsNullOrEmpty(rspContent))
{
rsp.Content = new StringContent(rspContent);
}
return Task.FromResult(rsp);
that wrapper task is run and the client receives its response.
As far as I know, the tasks created by new Task<>...
and Task.FromResult()
should look identical to the caller. Why will it await
(or whatever it's actually doing to obtain the result) one and not the other? What am I doing wrong? Is it possible to make this work?
the tasks created by
new Task<>
andTask.FromResult()
should look identical to the caller.
They do look identical from the caller perspective, but they aren't the same from the implementation perspective.
The Task
constructor doesn't start the task, that's why you shouldn't use it. Instead use Task.Run
which returns a hot task:
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.Run(() =>
{
var rspContent = "here's a response string";
var rsp = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
if (!string.IsNullOrEmpty(rspContent))
{
rsp.Content = new StringContent(rspContent);
}
return rsp;
}, cancellationToken);
}
Although I'd argue that this may be redundant by itself, since an action in WebAPI by itself is already ran on a thread pool thread, it is usually redundant to wrap it around inside an additional thread.