MVC FilterAttribute获取响应长度 [英] MVC FilterAttribute to get Response length
问题描述
我有一个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屋!