C# Web Api请求超时?

C# Web Api请求超时?,c#,asp.net,asp.net-web-api,asp.net-web-api2,C#,Asp.net,Asp.net Web Api,Asp.net Web Api2,与MVC一样,WebApi在异步ASP.NET管道上运行,这意味着 在MVC中,我使用[AsyncTimeout]过滤器,WebApi没有这个过滤器。那么如何在WebApi中超时请求呢?使用WebApi,您通常会在客户端而不是服务器端处理超时。这是因为,而且: 取消HTTP请求的方法是直接在HttpClient上取消它们。原因是多个请求可以在单个HttpClient中重用TCP连接,因此您无法安全地取消单个请求而不可能影响其他请求 您可以控制请求的超时时间——如果我没有记错的话,我认为它在Htt

与MVC一样,WebApi在异步ASP.NET管道上运行,这意味着


在MVC中,我使用
[AsyncTimeout]
过滤器,WebApi没有这个过滤器。那么如何在WebApi中超时请求呢?

使用WebApi,您通常会在客户端而不是服务器端处理超时。这是因为,而且:

取消HTTP请求的方法是直接在HttpClient上取消它们。原因是多个请求可以在单个HttpClient中重用TCP连接,因此您无法安全地取消单个请求而不可能影响其他请求

您可以控制请求的超时时间——如果我没有记错的话,我认为它在HttpClientHandler上

如果您真的需要在API端本身实现超时,我建议您创建一个线程来执行您的工作,然后在一段时间后取消它。例如,您可以将其放入
任务中,使用
任务创建“超时”任务。等待
并使用
任务。等待任何
任务返回。这可以模拟超时


同样,如果您正在执行特定的操作,请检查它是否已经支持超时。通常,我会从我的WebAPI执行一个
HttpWebRequest
,并指定它的属性。

基于Mendhak的建议,可以做你想做的事情,尽管不是你想做的那样,也不需要经历很多困难。在不使用过滤器的情况下执行,可能会出现如下情况:

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 );
    }
}
[TimeoutFilter( 10000 )]
public string Get( )
{
    Thread.Sleep( 5000 );
    return "Results";
}
然后可以这样使用:

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 );
    }
}
[TimeoutFilter( 10000 )]
public string Get( )
{
    Thread.Sleep( 5000 );
    return "Results";
}
这适用于简单类型(例如字符串),在Firefox中为我们提供:
Results
,但正如您所见,序列化并不理想。就序列化而言,将自定义类型与此完全相同的代码一起使用会有点问题,但通过一些工作,这可能在某些特定场景中很有用。动作参数以字典的形式出现而不是以数组的形式出现,这也会在参数顺序方面带来一些问题。显然,得到真正的支持会更好

就vNext而言,由于MVC和API控制器正在统一,他们很可能计划为Web API添加服务器端超时功能。如果他们这样做,则很可能不会通过
System.Web.Mvc.AsyncTimeoutAttribute
类,因为他们会显式删除对
System.Web
的依赖关系

到今天为止,向
project.json
文件添加
System.Web.Mvc
条目似乎还不起作用,但这很可能会改变。如果是这样的话,虽然您将无法将新的云优化框架与此类代码一起使用,但您可能可以对只打算与完整的.NET framework一起运行的代码使用
AsyncTimeout
属性

值得一提的是,这是我尝试添加到
project.json
中的内容。也许一个特定的版本会让它更快乐

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

对它的引用确实会显示在解决方案资源管理器的“引用”列表中,但它会以黄色感叹号表示问题。当此引用保留时,应用程序本身返回500个错误。

为了让您的生活更轻松,请在基本控制器中添加以下方法:

    protected async Task<T> RunTask<T>(Task<T> action, int timeout) {
        var timeoutTask = Task.Delay(timeout);
        var firstTaskFinished = await Task.WhenAny(timeoutTask, action);

        if (firstTaskFinished == timeoutTask) {
            throw new Exception("Timeout");
        }

        return action.Result;
    }
受保护的异步任务RunTask(任务操作,int超时){
var timeoutTask=Task.Delay(超时);
var firstTaskFinished=等待任务.WhenAny(超时任务,操作);
如果(firstTaskFinished==超时任务){
抛出新异常(“超时”);
}
返回动作。结果;
}
现在,从基本控制器继承的每个控制器都可以访问RunTask方法。现在,在API中调用RunTask方法,如下所示:

    [HttpPost]
    public async Task<ResponseModel> MyAPI(RequestModel request) {
        try {
            return await RunTask(Action(), Timeout);
        } catch (Exception e) {
            return null;
        }
    }

    private async Task<ResponseModel> Action() {
        return new ResponseModel();
    }
[HttpPost]
公共异步任务MyAPI(请求模型请求){
试一试{
返回等待运行任务(Action(),超时);
}捕获(例外e){
返回null;
}
}
专用异步任务操作(){
返回新的ResponseModel();
}

对于需要超时的每个端点,通过管道输送一个
取消令牌,例如:

[HttpGet]
public Task<Response> GetAsync()
{
    var tokenSource = new CancellationTokenSource(_timeoutInSec * 1000);
    return GetResponseAsync(tokenSource.Token);
}
[HttpGet]
公共任务GetAsync()
{
var tokenSource=新的CancellationTokenSource(_timeoutInSec*1000);
返回GetResponseAsync(tokenSource.Token);
}

很抱歉,这并不能真正回答我的问题。我的Api由浏览器中的javascript使用,无法启用跨浏览器工作的请求超时。这就是为什么我要寻找与[AsyncTimeout]等效的WebApi,以便我可以在服务器上执行它。请参阅引号后的位-您可以使用
Task.WaitAny来执行它,主任务在一个线程中,而“wait”超时任务在另一个线程中。如果超时任务提前返回,那么您可以立即返回。我理解,谢谢您的回答。但我要寻找的是一个可以像MVC中的[AsyncTimeout]一样全局应用的解决方案。我认为它在我正在回顾的vNext中得到了支持。我相信你是对的,它将在vNext DalSoft中得到支持。不仅是[AsyncTimeout]的添加,还有许多我们在与常规ViewController进行比较时无法访问的属性。请检查这篇文章,downvoter是否愿意解释原因+1这是一个非常全面的概述。