Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this code return "Specified method is not supported."

Why does this code return "Specified method is not supported."

using System;
using System.IO;
using System.Security.Cryptography;
using System.Web.Mvc;

public class ETagAttribute : ActionFilterAttribute
{
    private string GetToken(Stream stream) {
        MD5 md5 = MD5.Create();
        byte [] checksum = md5.ComputeHash(stream);
        return Convert.ToBase64String(checksum, 0, checksum.Length);
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        filterContext.HttpContext.Response.AppendHeader("ETag", GetToken(filterContext.HttpContext.Response.OutputStream));
        base.OnResultExecuted(filterContext);
    }
}

This should work, but is doesn't.

Apparently Microsoft overrode System.Web.HttpResponseStream.Read(Byte[] buffer, Int32 offset, Int32 count) so that it returns "Specified method is not supported.", not sure why they would do that, since it inherits for the System.IO.Stream base class...

Stack Trace:

[NotSupportedException: Specified method is not supported.]
   System.Web.HttpResponseStream.Read(Byte[] buffer, Int32 offset, Int32 count) +29
   ETagAttribute.ReadFully(Stream input) in Filters\ETagAttribute.cs:11
   ETagAttribute.GetToken(Stream stream) in Filters\ETagAttribute.cs:22
   ETagAttribute.OnResultExecuted(ResultExecutedContext filterContext) in Filters\ETagAttribute.cs:29
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +282
   System.Web.Mvc.<>c__DisplayClass1e.<InvokeActionResultWithFilters>b__1b() +19
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +260
   System.Web.Mvc.<>c__DisplayClass1e.<InvokeActionResultWithFilters>b__1b() +19
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +177
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +343
   System.Web.Mvc.Controller.ExecuteCore() +116
   System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +97
   System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +10
   System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__5() +37
   System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +21
   System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +12
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
   System.Web.Mvc.<>c__DisplayClasse.<EndProcessRequest>b__d() +50
   System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
   System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +60
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8920029
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184
like image 394
ThatGuyInIT Avatar asked Jul 11 '11 18:07

ThatGuyInIT


2 Answers

HttpResponse.OutputStream is meant for writing to, not reading from. It's a write-only stream, basically - just like the Output property returns a TextWriter.

like image 128
Jon Skeet Avatar answered Oct 23 '22 10:10

Jon Skeet


After much fighting I found a solution using Response.Filter

using System;
using System.IO;
using System.Security.Cryptography;
using System.Web;
using System.Web.Mvc;

public class ETagAttribute : ActionFilterAttribute {
    public override void OnActionExecuting(ActionExecutingContext filterContext) {
        try {
            filterContext.HttpContext.Response.Filter = new ETagFilter(filterContext.HttpContext.Response);
        } catch (System.Exception) {
            // Do Nothing
        };
    }
}

public class ETagFilter : MemoryStream {
    private HttpResponseBase o = null;
    private Stream filter = null;

    public ETagFilter (HttpResponseBase response) {
        o = response;
        filter = response.Filter;
    }

    private string GetToken(Stream stream) {
        byte[] checksum = new byte[0];
        checksum = MD5.Create().ComputeHash(stream);
        return Convert.ToBase64String(checksum, 0, checksum.Length);
    }

    public override void Write(byte[] buffer, int offset, int count) {
        byte[] data = new byte[count];
        Buffer.BlockCopy(buffer, offset, data, 0, count);
        filter.Write(data, 0, count);
        o.AddHeader("ETag", GetToken(new MemoryStream(data)));
    }
}

Resources:

http://authors.aspalliance.com/aspxtreme/sys/Web/HttpResponseClassFilter.aspx
http://forums.asp.net/t/1380989.aspx/1

like image 32
ThatGuyInIT Avatar answered Oct 23 '22 09:10

ThatGuyInIT