MVC FilterAttribute获取响应长度 [英] MVC FilterAttribute to get Response length

查看:79
本文介绍了MVC FilterAttribute获取响应长度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个MVC4(.NET 4.5)应用程序.我在使用简单Response.Write()方法的控制器上有一个空操作.我想编写一个FilterAttribute,它将检查OnActionExecuted()方法中的响应长度是否为0.但是,我似乎找不到合适的ActionExecutedContext属性来执行此操作. 到目前为止,我尝试过的事情:

I have an MVC4 (.NET 4.5) application. I have a void Action on the controller that uses a simple Response.Write() method. I want to write a FilterAttribute that would check if the Response length is 0 in the OnActionExecuted() method. However, i can't seem to find a suitable ActionExecutedContext property to do this. What i tried so far:

public void OnActionExecuted(ActionExecutedContext filterContext)
{
    if (filterContext.HttpContext.Response.OutputStream.Length == 0)
    {
        Logger.Log("empty response detected");
    }
}

但是,这将引发Specified method is not supported.异常.我发现该流是只写的.我应该检查什么财产?

However, this throws a Specified method is not supported. exception. I foudn that this stream is write-only. What property should i check?

推荐答案

是的,由于@Darin描述的原因,您不能依赖HttpContext.Response.OutputStream.Length来获取响应长度.而且即使您得到了长度,也不只是内容主体的长度,因此无论如何它都不是准确的.

Yea you can't depend on the HttpContext.Response.OutputStream.Length to get the response length because of the reason @Darin described. And even if you do get the length, that's not the length for the content body only so it wouldn't be accurate anyway.

我将向您展示如何完成类似的任务:如果一天中的任何AJAX/JSON请求超过应用程序中定义的限制的95%,请在一天结束时发送电子邮件强>.注意:我只检查ajax请求,即JsonResult.

I am going to show you how I would achieve a similar task: email out at the end of the day if any of AJAX/JSON requests during the day exceeds 95% of the limits defined in the application. Note: I am only checking for ajax requests, i.e., JsonResult.

JsonResult的默认MaxJsonLength设置为102400字节(100k).因此,我需要覆盖JsonResult,尤其是Controller中的Json().因此,我为此定义了一个自定义Mvc控制器.

The default MaxJsonLength of JsonResult is set to 102400 bytes (100k). So I need to override the JsonResult, especially Json() from the Controller. Hence I define a custom Mvc Controller for that.

public abstract class PowerfulControllerBase : Controller
{
    protected override JsonResult Json(object data, string contentType, Encoding contentEncoding)
    {
        return new JsonResult()
        {
            Data = data,
            ContentType = contentType,
            ContentEncoding = contentEncoding,
            MaxJsonLength = JsonResultFilter.MaxJsonLengthInBytes
        };

    protected override JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior)
    {
        return new JsonResult()
        {
            Data = data,
            ContentType = contentType,
            ContentEncoding = contentEncoding,
            JsonRequestBehavior = behavior,
            MaxJsonLength = JsonResultFilter.MaxJsonLengthInBytes
        };
    }        
}

MaxJsonLength是在名为JsonResultFilter的自定义ResultFilter中定义的,我将在后面显示.

The MaxJsonLength is defined in the custom ResultFilter called JsonResultFilter I am going to show later.

由于我们不能依靠Response对象来获取内容主体的长度,因此我们必须以某种方式首先处理响应数据.因此,在这里我将定义一个自定义的JsonResult类,该类使用 Newtonsoft JSON序列化器序列化对象/数据.

Since we can't count on the Response object to get the content body length, we have to somehow process the response data first. Hence here I will define a custom JsonResult class that uses Newtonsoft JSON Serializer to serialize the object/data.

public class JsonNetResult : JsonResult
{
    public int ContentLength { get; private set; }

    public JsonNetResult()
    {
        this.ContentLength = 0;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        var response = context.HttpContext.Response;

        response.ContentType = !String.IsNullOrEmpty(base.ContentType)
            ? base.ContentType
            : "application/json";

        var serializedObject = JsonConvert.SerializeObject(base.Data, Formatting.None);

        if (base.ContentEncoding != null)
        {
            response.ContentEncoding = base.ContentEncoding;

            this.ContentLength = response.ContentEncoding.GetByteCount(serializedObject);
        }
        else
        {
            this.ContentLength = Encoding.UTF8.GetByteCount(serializedObject);
        }

        response.Write(serializedObject);
    }
}

在使用JsonConvert进行序列化之后,新的JsonNetResult还有一个名为ContentLength的附加属性来包含内容的长度.

Here the new JsonNetResult has an additional property called ContentLength to contain the length of the content, after the serialization using JsonConvert.

现在,我们需要在第1步中将JsonResult替换为此类.

Now we need to replace JsonResult in step 1 with this class.

public abstract class PowerfulControllerBase : Controller
{
    protected override JsonResult Json(object data, string contentType, Encoding contentEncoding)
    {
        return new JsonNetResult()
        {
            ...
        };
    }

    protected override JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior)
    {
        return new JsonNetResult()
        {
            ...
        };
    }
}

现在,从此PowerfulControllerBase返回的JsonResult将具有称为ContentLength的特殊属性.我们需要做的最后一件事是定义一个ActionFilter,更具体地说,是一个ResultFilter.

Now the JsonResult returned from this PowerfulControllerBase will have the special property called ContentLength. The last thing we need to do is to define a ActionFilter, more specially, a ResultFilter.

我们想检测AJAX响应内容的大小何时将超过我们定义的限制.因此,我们需要定义一个过滤器并将其注入GlobalFilterCollection.

We want to detect whenever the AJAX response content size is about to exceed the limit we definied. Hence we need to define a filter and inject it into the GlobalFilterCollection.

public class JsonResultFilter : IResultFilter
{
    public const int MaxJsonLengthInBytes = 15728640;    // 15360 k = 15 mb
    public const double AlertPercentage = .95;           // 95%

    // A service that records this warning
    private readonly ISystemWarningService _systemWarningService;

    public JsonResultFilter(ISystemWarningService systemWarningService)
    {
        _systemWarningService = systemWarningService;
    }

    public void OnResultExecuted(ResultExecutedContext filterContext)
    {
        // We're only interested in JsonNetResult
        if (filterContext.Result is JsonNetResult)
        {
            JsonNetResult jsonResult = filterContext.Result as JsonNetResult;
            // See if the content length exceeds the percentage of the limit
            if ((double)jsonResult.ContentLength / MaxJsonLengthInBytes >= AlertPercentage)
            {
                _systemWarningService.LogSizeWarning(filterContext.HttpContext.Request.RawUrl,
                    filterContext.HttpContext.User.Identity.Name,
                    jsonResult.ContentLength,
                    MaxJsonLengthInBytes);
            }
        }
    }

    public void OnResultExecuting(ResultExecutingContext filterContext)
    {
    }
}

例如,所有警告都可以插入数据库,并且您可以定义每天结束时运行的SQL作业,以发送电子邮件通知.

All the warnings could be inserted into the database, for example, and you can define a SQL job that runs at the end of each day to send out email notification.

这篇关于MVC FilterAttribute获取响应长度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆