我有一个MVC4(.NET 4.5)应用程序。我在控制器上有一个使用简单Response.Write()
方法的void Action。我想写一个FilterAttribute
来检查OnActionExecuted()
方法中的响应长度是否为0。但是,我似乎无法找到合适的ActionExecutedContext
属性来执行此操作。
到目前为止我尝试了什么:
public void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.HttpContext.Response.OutputStream.Length == 0)
{
Logger.Log("empty response detected");
}
}
但是,这会引发Specified method is not supported.
异常。我发现这个流是只写的。我应该检查什么属性?
答案 0 :(得分:0)
是的,由于@Darin描述的原因,你不能依靠HttpContext.Response.OutputStream.Length
来获得响应长度。即使你确实得到了长度,但这不仅仅是内容体的长度,所以它无论如何都不准确。
我将向您展示如何实现类似的任务:如果当天任何AJAX / JSON请求超过应用程序中定义的限制的95%,则在一天结束时发送电子邮件。注意:我只检查ajax请求,即JsonResult
。
MaxJsonLength
JsonResult
MaxJsonLength
的默认JsonResult
设置为102400字节(100k)。所以我需要覆盖JsonResult
,尤其是Json()
Controller
。因此我为此定义了一个自定义Mvc控制器。
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
在名为ResultFilter
的自定义JsonResultFilter
中定义,我稍后会展示。
JsonResult
由于我们无法依靠Response
对象来获取内容体长度,因此我们必须先以某种方式处理响应数据。因此,我将在此定义一个自定义JsonResult
类,该类使用 Newtonsoft JSON Serializer来序列化对象/数据。
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);
}
}
在使用JsonNetResult
进行序列化后,新ContentLength
有一个名为JsonConvert
的附加属性,用于包含内容的长度。
现在我们需要在步骤1中用此类替换JsonResult
。
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()
{
...
};
}
}
现在,从此JsonResult
返回的PowerfulControllerBase
将具有名为ContentLength
的特殊属性。我们需要做的最后一件事是定义ActionFilter
,更具体地说是ResultFilter
。
我们希望在AJAX响应内容大小即将超过我们定义的限制时检测到。因此,我们需要定义一个过滤器并将其注入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作业以发送电子邮件通知。