0

I want to modify all request contents that arrives in a web api. I cannot change the code in web api, instead I'm adding a HttpModule with a request filter. I tried using the repo from the following answer https://stackoverflow.com/a/35590493/5901959.

Here is a simplified version of the Read method in the RequestFilter,

public override int Read(byte[] buffer, int offset, int count)
{            
    if (ms == null)
    {
        var sr = new StreamReader(_stream, _encoding);
        string content = sr.ReadToEnd();

        content = content.ToUpper().Substring(0, 3);

        byte[] bytes = _encoding.GetBytes(content);
        ms = new MemoryStream();
        ms.Write(bytes, 0, bytes.Length);
        ms.Seek(0, SeekOrigin.Begin);
    } 

    return ms.Read(buffer, offset, count);
}

Which gives the following error in the web api,

Exception System.AggregateException: One or more errors occurred. ---> System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: offset
   at System.Web.HttpInputStream.Seek(Int64 offset, SeekOrigin origin)
   at System.Web.Http.WebHost.SeekableBufferedRequestStream.SwapToSeekableStream()
   at System.Web.Http.WebHost.SeekableBufferedRequestStream.EndRead(IAsyncResult asyncResult)
   at System.Net.Http.StreamToStreamCopy.StartRead()
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at Echo.Controllers.EchoController.Echo() in C:\repos-test\HttpModule to change request body\Echo\Echo\Controllers\EchoController.cs:line 23
---> (Inner Exception #0) System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: offset
   at System.Web.HttpInputStream.Seek(Int64 offset, SeekOrigin origin)
   at System.Web.Http.WebHost.SeekableBufferedRequestStream.SwapToSeekableStream()
   at System.Web.Http.WebHost.SeekableBufferedRequestStream.EndRead(IAsyncResult asyncResult)
   at System.Net.Http.StreamToStreamCopy.StartRead()<---

I added logging and I can see that the Read method is called but not the Seek method as the error is indicating.

If I remove .Substring(0, 3) then no exception is thrown but the web api gets the original request, which is in lower case, hence not the modified one. It seems like the HttpInputStream is never changed.

================== EDIT =========================
I created a small Web API to test this further. It seems that if I read from the InputStream twice in the WebAPI the first read returns the original response and the second read returns the one created by the filter. For example if change the content to uppercase in the RequestFilter, without the substring part, and then use the following code in the web api with payload="This is my payload"

Stream stream = Request.Content.ReadAsStreamAsync().Result;
StreamReader sr = new StreamReader(stream);
string s = sr.ReadToEnd();
log.Info(s); // This gives: This is my payload

stream.Seek(0, SeekOrigin.Begin);
s = sr.ReadToEnd();
log.Info(s); // This gives: THIS IS MY PAYLOAD

stream.Seek(0, SeekOrigin.Begin);
s = sr.ReadToEnd();
log.Info(s); // This gives: THIS IS MY PAYLOAD

I also tried setting content="Something completely different" in the request filter of the HttpModule, with similar result =>

first read = "This is my payload",
second read = "Something completely different".

How can a stream contains two different contents?


EDIT 2021-05-25

I found that for this to work, the endpoint needs to end with .aspx and the content type of the request must be "x-www-form-urlencoded".

Questions:

  • Can someone explain why it has to be a certain content type and extension?
  • Is it possible to make this work for endpoints not ending in .aspx?

EDIT 2021-05-27

After adding the request filter i added the following line,

var _ = context.Request.Form;

to trigger the evaluation of the input stream. Then if the content type is "x-www-form-urlencoded" the request filter will be applied. If you any other kind of data like json, xml, raw text then the request content wont change for some reason. Maybe request filters only works on form data?

Jesper Lundin
  • 168
  • 1
  • 8

1 Answers1

0

I suggest you to debug Substring(0, 3) this part of the code, the error AggregateException shuold cause by you've attempted to extract a substring that is outside the range of the current string.

The methods that extract substrings all require that you specify the starting position of the substring and, for substrings that do not continue to the end of the string, the number of characters in the substring.

samwu
  • 3,857
  • 3
  • 11
  • 25