Mat*_*ias 8 .net-core asp.net-core asp.net-core-middleware
我的要求:编写一个中间件,用于过滤来自另一个后续中间件(例如Mvc)的响应中的所有"坏词".
问题:响应的流式传输.因此,当我们FilterBadWordsMiddleware
从已经写入响应的后续中间件回到我们的时候,我们对派对来说太迟了......因为响应已经开始发送,这导致了众所周知的错误response has already started
......
因此,这是许多不同情况下的要求 - 如何处理它?
Ily*_*kov 10
替换响应流MemoryStream
以防止其发送.修改响应后返回原始流:
public class EditResponseMiddleware
{
private readonly RequestDelegate _next;
public EditResponseMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
var originBody = context.Response.Body;
var newBody = new MemoryStream();
context.Response.Body = newBody;
await _next(context);
newBody.Seek(0, SeekOrigin.Begin);
string json = new StreamReader(newBody).ReadToEnd();
context.Response.Body = originBody;
await context.Response.WriteAsync(modifiedJson);
}
}
Run Code Online (Sandbox Code Playgroud)
这是一种解决方法,它可能会导致性能问题.我希望在这里看到更好的解决方案.
Hen*_*all 10
不幸的是我不能发表评论,因为我的分数太低了。所以只想发布我对优秀顶级解决方案的扩展,以及对 .NET Core 3.0+ 的修改
首先
context.Request.EnableRewind();
Run Code Online (Sandbox Code Playgroud)
已更改为
context.Request.EnableBuffering();
Run Code Online (Sandbox Code Playgroud)
在 .NET Core 3.0+ 中
这是我读/写正文内容的方式:
首先是过滤器,所以我们只修改我们感兴趣的内容类型
private static readonly IEnumerable<string> validContentTypes = new HashSet<string>() { "text/html", "application/json", "application/javascript" };
Run Code Online (Sandbox Code Playgroud)
这是一种将 [[[Translate me]]] 之类的掘金文本转换为其翻译的解决方案。这样我就可以标记所有需要翻译的内容,读取我们从翻译器那里获得的 po 文件,然后在输出流中进行翻译替换 - 无论掘金文本是否在剃刀视图中,javascript管他呢。有点像 TurquoiseOwl i18n 包,但在 .NET Core 中,不幸的是,这个优秀的包不支持。
...
if (modifyResponse)
{
//as we replaced the Response.Body with a MemoryStream instance before,
//here we can read/write Response.Body
//containing the data written by middlewares down the pipeline
var contentType = context.Response.ContentType?.ToLower();
contentType = contentType?.Split(';', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); // Filter out text/html from "text/html; charset=utf-8"
if (validContentTypes.Contains(contentType))
{
using (var streamReader = new StreamReader(context.Response.Body))
{
// Read the body
context.Response.Body.Seek(0, SeekOrigin.Begin);
var responseBody = await streamReader.ReadToEndAsync();
// Replace [[[Bananas]]] with translated texts - or Bananas if a translation is missing
responseBody = NuggetReplacer.ReplaceNuggets(poCatalog, responseBody);
// Create a new stream with the modified body, and reset the content length to match the new stream
var requestContent = new StringContent(responseBody, Encoding.UTF8, contentType);
context.Response.Body = await requestContent.ReadAsStreamAsync();//modified stream
context.Response.ContentLength = context.Response.Body.Length;
}
}
//finally, write modified data to originBody and set it back as Response.Body value
ReturnBody(context.Response, originBody);
}
...
private void ReturnBody(HttpResponse response, Stream originBody)
{
response.Body.Seek(0, SeekOrigin.Begin);
response.Body.CopyToAsync(originBody);
response.Body = originBody;
}
Run Code Online (Sandbox Code Playgroud)
基于我使用的代码的更简单版本:
/// <summary>
/// The middleware Invoke method.
/// </summary>
/// <param name="httpContext">The current <see cref="HttpContext"/>.</param>
/// <returns>A Task to support async calls.</returns>
public async Task Invoke(HttpContext httpContext)
{
var originBody = httpContext.Response.Body;
try
{
var memStream = new MemoryStream();
httpContext.Response.Body = memStream;
await _next(httpContext).ConfigureAwait(false);
memStream.Position = 0;
var responseBody = new StreamReader(memStream).ReadToEnd();
//Custom logic to modify response
responseBody = responseBody.Replace("hello", "hi", StringComparison.InvariantCultureIgnoreCase);
var memoryStreamModified = new MemoryStream();
var sw = new StreamWriter(memoryStreamModified);
sw.Write(responseBody);
sw.Flush();
memoryStreamModified.Position = 0;
await memoryStreamModified.CopyToAsync(originBody).ConfigureAwait(false);
}
finally
{
httpContext.Response.Body = originBody;
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
5406 次 |
最近记录: |