2014-11-27 2 views
16

У меня есть следующие контроллеры:Почему мой метод действия не заканчивается?

[TimeoutFilter] 
public abstract class BaseController: Controller 
{ 
} 

public class IntegrationTestController : BaseController 
{ 
    [HttpGet] 
    public ActionResult TimeoutSeconds() 
    { 
     return Content(HttpContext.Server.ScriptTimeout.ToString(CultureInfo.InvariantCulture)); 
    } 

    [HttpGet] 
    public ActionResult ForceTimeout() 
    { 
     var timeoutWindow = TimeoutFilter.TimeoutSeconds; 
     Thread.Sleep((timeoutWindow + 5) * 1000); 
     return Content("This should never get returned, mwahahaaa!"); 
    } 
} 

Для моего тестового сценария я использую настройку конфигурации 5 секунд в TimeoutFilter, и я знаю, что это работает, потому что, когда мой тест вызывает TimeoutSeconds, я получаю правильное значение 5, но когда тест вызывает ForceTimeout, я получаю HTTP-ответ 200 и мой «никогда не возвращенный» текст.

И фильтр:

public class TimeoutFilter : ActionFilterAttribute 
{ 
    internal const string TimeoutSecondsSettingsKey = "MvcActionTimeoutSeconds"; 
    internal static int TimeoutSeconds; 
    public TimeoutFilter() 
    { 
     TimeoutSeconds = int.Parse(ConfigurationManager.AppSettings[TimeoutSecondsSettingsKey]); 
    } 

    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     filterContext.Controller.ControllerContext.HttpContext.Server.ScriptTimeout = TimeoutSeconds; 
     base.OnActionExecuting(filterContext); 
    } 
} 
+0

Можете ли вы представить свою реализацию фильтра времени ожидания? –

+0

@JalpeshVadgama Добавлено, как просили. – ProfK

ответ

7

я не мог получить эту работу, установив ScriptTimeout свойство либо, даже при установке debug="false" в web.config как предложено пользователем Erik Funkenbusch:

<configuration> 
    <system.web> 
     <compilation debug="false" targetFramework="4.5"/> 
     ... 

Он продолжал возвращать текст «Это никогда не должно возвращаться», а не тайминг во время Thread.Sleep.

Следует также отметить, что я также расширил Thread.Sleep до уровня, превышающего Server.ScriptTimeout, по умолчанию 110 секунд, но он все же в конечном итоге вернул тот же текст, а не тайм-аут.

Я тогда вместо пытался установить executionTimeout в web.config:

<configuration> 
    <system.web> 
     <httpRuntime targetFramework="4.5" executionTimeout="5"/> 
     ... 

Если бы вы теперь добавить контрольную точку в TimeoutFilter вы будете наблюдать, что значение ScriptTimeout было присвоено значение executionTimeout от web.config. Увы, это все еще не работало для меня (независимо от того, debug="false").

Я тогда наткнулся на this link около ScriptTimeout и executionTimeout, не работая с аналогичной настройкой того, что вы описали. Первый ответ в сообщении описывает использование debug="false", а также упоминание о том, что таймаут будет иметь задержку от 5 до 15 секунд. Мне все еще не повезло, даже если вы используете большое значение Thread.Sleep. Второй ответ в этой статье предполагает, что настройка конфигурации executionTimeout является заменой свойства ScriptTimeout (который, по-видимому, является интерфейсом COM, используемым в классическом ASP). Этот ответ предполагает, что невозможно установить определенный тайм-аут, не используя собственную логику тайм-аута.

Далее я натолкнулся на following (more recent) link, где первый ответ предполагает, что тайм-аут отключен в MVC. Еще один ответ предполагает, что это связано с тем, что MVCHandler (который выбирает контроллер, который будет обрабатывать HTTPRequest), является IHttpAsyncHandler, и поэтому он может выполнять другой запрос (который является точкой использования запроса async), поэтому он внутренне переключает время -out state off (это то, что я собираю, прочитав эту ссылку). Он должен работать с прямым asp.net, хотя использование ScriptTimeout представляется приемлемым способом в this answer.

Ссылка предполагает, что добавление следующей строки позволит ему работать (но не в приложении среднего доверия):

System.Web.HttpContext.Current.GetType().GetField("_timeoutState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).SetValue(System.Web.HttpContext.Current, 1); 

Таким образом, изменяя TimeoutFilterOnActionExecuting() к:

public override void OnActionExecuting(ActionExecutingContext filterContext) 
{ 
    System.Web.HttpContext.Current.GetType().GetField("_timeoutState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).SetValue(System.Web.HttpContext.Current, 1); 
    base.OnActionExecuting(filterContext); 
} 

и установка web.config:

<configuration> 
    <system.web> 
     <compilation debug="false" targetFramework="4.5"/> 
     <httpRuntime targetFramework="4.5" executionTimeout="5"/> 
     ... 

позволяет тайм-ауту работать, но имеет небольшую задержку в 5 секунд, указанную в первом сообщении.

Примечание: Используя этот метод, вы не можете установить свойство ScriptTimeout в фильтр. Попытка установить ScriptTimeout и переопределить значение, установленное в web.config, не работает.

+0

Действительно, хорошая детективная работа. Кроме того, я нашел этот отчет о подключении, который предлагает обходное решение (не очень хорошее, но если вы можете заставить его работать, то лучше, чем ничего) https://connect.microsoft.com/VisualStudio/feedback/details/781171/ жерех-нетто-MVC-ExecutionTimeout-делает-не-работы –

6

Вы используете отладочную сборку?

Из документации ScriptTimeout:

http://msdn.microsoft.com/en-us/library/system.web.httpserverutility.scripttimeout(v=vs.110).aspx

Если вы установите атрибут отладки элемента компиляции истина в файле Web.config, значение ScriptTimeout будет игнорироваться.

Кроме того, поскольку это значение такое же, как установлено в элементе httpRuntime, я действительно не понимаю этого, так как вы можете просто настроить этот параметр в своем web.config.

Edit:

Dangerous проделал хорошую работу, выяснив детали, и в самом деле, ScriptTimeout не поддерживается в асинхронных трубопроводах (которые MVC уже были, по крайней мере, я думаю, MVC4, а также WebAPI .. даже если не с помощью методов асинхронной)

"обойти", как предложено этим подключения отчета:

https://connect.microsoft.com/VisualStudio/feedback/details/781171/asp-net-mvc-executiontimeout-does-not-work

Используйте [AsyncTimeout] Att ребро и взять токен отмены в качестве параметра, а затем вызвать CanclationToken.ThrowIfCancelationRequested или использовать маркер отмены в методе асинхронного вызова.

Вот пример:

[AsyncTimeout(5000)] 
public async Task<ContentResult> Index(CancellationToken ct) 
{ 
    await Task.Delay(10 * 1000, ct); 
    return Content("This should never get returned, mwahahaaa!"); 
} 

Это бросает исключение OperationCanceled с YSOD через 5 секунд. Бонус для этого является то, что он работает даже в режиме отладки;)

+0

Я не уверен, каково окончательное значение конфигурации, после некоторого чтения о 'machine.config', переопределяющем значение, а« машина »- веб-сайт Azure. Таким образом, я могу настаивать на определенном значении тайм-аута. – ProfK

+1

@ProfK - Почему вы опубликовали награду, когда я дал вам ответ? –

+0

Эрик, так как опасные заметки выше, изменение атрибута 'debug' не имеет никакого эффекта. – ProfK

Смежные вопросы