Timeout a Web Api request?

With WebAPI, you would generally handle timeouts on the client side, rather than the server side. This is because, and I quote:

The way to cancel HTTP requests is to cancel them on the HttpClient directly. The reason being that multiple requests can reuse TCP connections within a single HttpClient and so you can't safely cancel a single request without possibly affecting other requests as well.

You can control the timeout for requests -- I think it's on the HttpClientHandler if I recall correctly.

If you really need to implement a timeout on the API side itself, I would recommend creating a thread to do your work in, and then cancelling it after a certain period. You could for example put it in a Task, create your 'timeout' task using Task.Wait and use Task.WaitAny for the first one to come back. This can simulate a timeout.

Similarly, if you are performing a specific operation, check to see if it already supports timeouts. Quite often, I will perform an HttpWebRequest from my WebAPI, and specify its Timeout property.


For each endpoint where you want a timeout, pipe a CancellationToken through, e.g.:

[HttpGet]
public Task<Response> GetAsync()
{
    var tokenSource = new CancellationTokenSource(_timeoutInSec * 1000);
    return GetResponseAsync(tokenSource.Token);
}

Building on the suggestion by Mendhak, it is possible to do what you want, though not exactly the way you'd like to do it without jumping through quite a few hoops. Doing it without a filter might look something like this:

public class ValuesController : ApiController
{
    public async Task<HttpResponseMessage> Get( )
    {
        var work    = this.ActualWork( 5000 );
        var timeout = this.Timeout( 2000 );

        var finishedTask = await Task.WhenAny( timeout, work );
        if( finishedTask == timeout )
        {
            return this.Request.CreateResponse( HttpStatusCode.RequestTimeout );
        }
        else
        {
            return this.Request.CreateResponse( HttpStatusCode.OK, work.Result );
        }
    }

    private async Task<string> ActualWork( int sleepTime )
    {
        await Task.Delay( sleepTime );
        return "work results";
    }

    private async Task Timeout( int timeoutValue )
    {
        await Task.Delay( timeoutValue );
    }
}

Here you will receive a timeout because the actual "work" we're doing will take longer than the timeout.

To do what you want with an attribute is possible, though not ideal. It's the same basic idea as before, but the filter could actually be used to execute the action via reflection. I don't think I would recommend this route, but in this contrived example, you can see how it might be done:

public class TimeoutFilter : ActionFilterAttribute
{
    public int Timeout { get; set; }

    public TimeoutFilter( )
    {
        this.Timeout = int.MaxValue;
    }
    public TimeoutFilter( int timeout )
    {
        this.Timeout = timeout;
    }


    public override async Task OnActionExecutingAsync( HttpActionContext actionContext, CancellationToken cancellationToken )
    {

        var     controller     = actionContext.ControllerContext.Controller;
        var     controllerType = controller.GetType( );
        var     action         = controllerType.GetMethod( actionContext.ActionDescriptor.ActionName );
        var     tokenSource    = new CancellationTokenSource( );
        var     timeout        = this.TimeoutTask( this.Timeout );
        object result          = null;

        var work = Task.Run( ( ) =>
                             {
                                 result = action.Invoke( controller, actionContext.ActionArguments.Values.ToArray( ) );
                             }, tokenSource.Token );

        var finishedTask = await Task.WhenAny( timeout, work );

        if( finishedTask == timeout )
        {
            tokenSource.Cancel( );
            actionContext.Response = actionContext.Request.CreateResponse( HttpStatusCode.RequestTimeout );
        }
        else
        {
            actionContext.Response = actionContext.Request.CreateResponse( HttpStatusCode.OK, result );
        }
    }

    private async Task TimeoutTask( int timeoutValue )
    {
        await Task.Delay( timeoutValue );
    }
}

This could then be used like this:

[TimeoutFilter( 10000 )]
public string Get( )
{
    Thread.Sleep( 5000 );
    return "Results";
}

This works for simple types (e.g. string), giving us: <z:anyType i:type="d1p1:string">Results</z:anyType> in Firefox, though as you can see, the serialization is not ideal. Using custom types with this exact code will be a bit problematic as far as serialization goes, but with some work, this could probably be useful in some specific scenarios. That the action parameters come in the form of a dictionary instead of an array could also pose some issues in terms of the parameter ordering. Obviously having real support for this would be better.

As far as the vNext stuff goes, they may well be planning to add the ability to do server-side timeouts for Web API since MVC and API controllers are being unified. If they do, it will likely not be through the System.Web.Mvc.AsyncTimeoutAttribute class, as they are explicitly removing dependencies on System.Web.

As of today, it doesn't appear that adding a System.Web.Mvc entry to the project.json file works, but this may well change. If it does, while you wouldn't be able to use the new cloud-optimized framework with such code, you might be able to use the AsyncTimeout attribute on code that is only intended to run with the full .NET framework.

For what it's worth, this is what I tried adding to project.json. Perhaps a specific version would have made it happier?

"frameworks": {
    "net451": {
        "dependencies": { 
            "System.Web.Mvc": ""
        }
    }
}

A reference to it does show up in the Solution Explorer's references list, but it does so with a yellow exclamation point indicating a problem. The application itself returns 500 errors while this reference remains.