【问题标题】:How can I abort an action in ASP.NET MVC如何中止 ASP.NET MVC 中的操作
【发布时间】:2013-07-19 08:33:48
【问题描述】:

我想停止服务器端jQuery.ajax 方法调用的操作。 我可以在客户端使用 $.ajax.abort() 方法停止 Ajax 请求,但不能在服务器端。

更新:

我使用异步操作而不是同步操作,但我没有得到我想要的!如您所知,服务器不能同时处理多个请求,这导致每个请求都必须等到前一个请求完成,即使前一个请求被 $.Ajax.Abort() 方法取消也是如此。 我知道如果我使用 [SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)] 属性,它几乎是我想要的,但它并不满足我。

最重要的是,我想中止用户在服务器端的处理方法。就是这样:)

【问题讨论】:

  • 如果我理解正确,您不希望任何 XHR 访问您的服务器吗?
  • 谢谢,但这不是我想要的!!我调用一个动作,例如需要 1 分钟。我想让用户根据需要取消请求,我正在使用 $.ajax.abort() 并且它工作正常并且它取消了请求,但我知道服务器正在处理先前的请求。如果我在服务器上使用该会话发送另一个请求,它必须等到最后一个操作结束。
  • @user2349133 只要它在客户端,你就可以检查它的状态,基于询问用户......一旦它进入服务器......我看不到取消它的方法特定的 XHR 请求
  • 长期运行的操作在做什么?你可以在那里使用分页,即每次调用处理 100 个“事物”吗?

标签: c# javascript jquery ajax asp.net-mvc


【解决方案1】:

我发现这篇文章:Cancelling Long Running Queries in ASP.NET MVC and Web API 非常有帮助。我会在这里总结这篇文章。

开始之前的快速说明:

  • 我运行的是 MVC5,而不是 ASP.NET Core MVC。
  • 我希望能够在离开页面时取消操作或使用 jQuery abort 取消操作。

我最终得到了这样的结果:

// Just add the CancellationToken parameter to the end of your parameter list
public async Task<ActionResult> GetQueryDataAsync(
    int id,
    CancellationToken cancellationToken)
{
    CancellationToken dcToken = Response.ClientDisconnectedToken;
    var linkTokenSrc = CancellationTokenSource
        .CreateLinkedTokenSource(
            cancellationToken, 
            dcToken
        );

    using (var dbContext = new Entities())
    {
        var data = await dbContext.ComplicatedView
            /* A bunch of linq statements */
            .ToListAsync(linkTokenSrc.Token)
            .ConfigureAwait(true);

        // Do stuff and return data unless cancelled
        // ...
    }
}

由于我使用的是 MVC5,我不得不使用文章中规定的解决方法,即从 Response.ClientDisconnectedToken 读取并链接令牌源。

如果您改用 ASP.NET Core MVC,您应该可以直接使用CancellationToken 参数,而不必根据文章链接它。

在此之后,我所要做的就是确保在我离开页面时在 JavaScript 中 aborted XHR。

【讨论】:

    【解决方案2】:

    这是一个例子 后端:

    [HttpGet]
    public List<SomeEntity> Get(){
            var gotResult = false;
            var result = new List<SomeEntity>();
            var tokenSource2 = new CancellationTokenSource();
            CancellationToken ct = tokenSource2.Token;
            Task.Factory.StartNew(() =>
            {
                // Do something with cancelation token to break current operation
                result = SomeWhere.GetSomethingReallySlow();
                gotResult = true;
            }, ct);
            while (!gotResult)
            {
                // When you call abort Response.IsClientConnected will = false
                if (!Response.IsClientConnected)
                {
                    tokenSource2.Cancel();
                    return result;
                }
                Thread.Sleep(100);
            }
            return result;
    }
    

    Javascript:

    var promise = $.post("/Somewhere")
    setTimeout(function(){promise.abort()}, 1000)
    

    希望我不会迟到。

    【讨论】:

    • 是否可以使用“Task.IsCompleted”而不是“gotResult”标志?
    【解决方案3】:

    我们已经在 IE 中看到了这个问题,其中一个中止的请求仍然被转发到控制器操作 - 但是,在我们的日志和用户活动条目中报告的参数被剥离,这会导致不同的错误。

    我已经使用如下过滤器解决了这个问题

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using TWV.Infrastructure;
    using TWV.Models;
    
    namespace TWV.Controllers
    {
        public class TerminateOnAbortAttribute : FilterAttribute, IActionFilter
        {
            public void OnActionExecuting(ActionExecutingContext filterContext)
            {
                // IE does not always terminate when ajax request has been aborted - however, the input stream gets wiped
                // The content length - stays the same, and thus we can determine if the request has been aborted
                long contentLength = filterContext.HttpContext.Request.ContentLength;
                long inputLength = filterContext.HttpContext.Request.InputStream.Length;
                bool isAborted = contentLength > 0 && inputLength == 0;
                if (isAborted)
                {
                    filterContext.Result = new EmptyResult();
                }
            }
    
            public void OnActionExecuted(ActionExecutedContext filterContext)
            {
                // Do nothing
            }
        }
    }
    

    【讨论】:

    • 不起作用。我正在使用 .NET Framework 4.8。
    【解决方案4】:

    一个简单的解决方案是使用类似下面的东西。我用它来取消长时间运行的任务(特别是生成数千条通知)。我也使用相同的方法通过 AJAX 轮询进度和更新进度条。此外,这实际上适用于任何版本的 MVC,并且不依赖于 .NET 的新功能

    public class MyController : Controller
    {
    
    private static m_CancelAction = false;
    
    public string CancelAction()
    {
        m_CancelAction = true;
        return "ok";
    }
    
    public string LongRunningAction()
    {
        while(...)
        {
            Dosomething (i.e. Send email, notification, write to file, etc)
    
            if(m_CancelAction)
            {
                m_CancelAction = false;
                break;
                return "aborted";
            }
        }
    
        return "ok";
    }
    }
    

    【讨论】:

    • 是否存在安全风险,即另一个客户端可以取消来自第一个客户端的长时间运行请求的操作。
    【解决方案5】:

    您可能想看看使用以下类型的控制器Using an Asynchronous Controller in ASP.NET MVC

    另外看看这篇文章是否对你也有帮助Cancel async web service calls,很抱歉这次我不能给出任何代码示例。

    我创建了一个示例来证明您可以取消服务器端请求。 My github async cancel example

    如果您通过代码调用其他站点,则有两种选择,具体取决于您的目标框架和您要使用的方法。我在此处包含参考供您查看:

    WebRequest.BeginGetResponse 用于 .Net 4.0 HttpClient 用于.Net 4.5,这个类有一个方法可以取消所有挂起的请求。

    希望这可以为您提供足够的信息来实现您的目标。

    【讨论】:

    • 谢谢,我相信你已经正确理解了我,你能给我看一个清晰的代码示例吗?
    • 做更多研究可能取决于您使用的 mvc 版本。澄清一下,我的理解是你想开始一个异步请求,因为它是一个长时间运行的过程,用户可以取消。当用户取消请求时,服务器会抓取该异步线程并基本上中止操作。对吗?
    • 是的,没错!我正在使用 ASP.NET MVC 4,我没有在 c# 中使用异步,如果您有示例代码,它将对我有所帮助!不过谢谢:)
    • 对不起,我自己没有任何实际示例,但我的答案中的第一个链接使用了一个示例 zip,您可以使用它称为 Async.zip,它位于此处:示例中的 archive.msdn.microsoft.com/aspnetmvcsamples/Release/…看起来控制器有一个用于异步工作的服务,并且该服务上有一个 abort 方法。至少它应该让您了解如何实现执行实际异步工作的服务。
    • @SaeedHamed 您对长期运行的服务有多少控制权?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多