问题描述
我想修改到达 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 由您尝试提取当前字符串范围之外的子字符串引起。
提取子串的方法都需要指定子串的起始位置,对于不连续到串尾的子串,需要指定子串中的字符数。