使用自定义 HttpModule 和 HttpRequest 过滤器修改 POST 请求

问题描述

我想修改到达 web api 的所有请求内容。我无法更改 web api 中的代码,而是添加一个带有请求过滤器的 HttpModule。我尝试使用以下答案 https://stackoverflow.com/a/35590493/5901959 中的存储库。

这是RequestFilter中Read方法的简化版本,

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,bytes.Length);
        ms.Seek(0,SeekOrigin.Begin);
    } 

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

在 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()<---

添加了日志记录,我可以看到调用Read 方法,而不是错误指示的 Seek 方法

如果我删除 .Substring(0,3) 则不会抛出异常,但 web api 获取原始请求,它是小写的,因此不是修改后的请求。好像 HttpInputStream 从未改变过。

================== 编辑 ==========================
我创建了一个小的 Web API 来进一步测试。似乎如果我在 WebAPI 中从 InputStream 读取两次,第一次读取返回原始响应,第二次读取返回过滤器创建的响应。例如如果将RequestFilter中的内容改为大写,不带子字符串部分,然后在web api中使用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

我也尝试在 HttpModule 的请求过滤器中设置 content="Something fully different",结果相似=>

first read = "这是我的有效载荷",
第二次阅读 =“完全不同的东西”。

一个流如何包含两种不同的内容


编辑 2021-05-25

我发现要使其工作,端点需要以 .aspx 结尾,并且请求的内容类型必须是“x-www-form-urlencoded”。

问题:

  • 有人可以解释为什么它必须是某种内容类型和扩展名吗?
  • 是否可以对不以 .aspx 结尾的端点进行这项工作?

编辑 2021-05-27

添加请求过滤器后,我添加了以下行,

var _ = context.Request.Form;

触发输入流的评估。然后,如果内容类型为“x-www-form-urlencoded”,则将应用请求过滤器。如果您使用任何其他类型的数据,如 json、xml、原始文本,则请求内容不会因某种原因而改变。也许请求过滤器只适用于表单数据?

解决方法

我建议您调试 Substring(0,3) 这部分代码,错误 AggregateException shuold 由您尝试提取当前字符串范围之外的子字符串引起。

提取子串的方法都需要指定子串的起始位置,对于不连续到串尾的子串,需要指定子串中的字符数。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...