【问题标题】:Having 'the response ended prematurely' exception on client side在客户端出现“响应提前结束”异常
【发布时间】:2022-01-24 18:36:48
【问题描述】:

客户端出现异常,提示复制流内容时出错,然后响应提前结束

我想不出解决方案。在服务器端,我有修改响应流的 asp.net core web api。它实际上读取控制器发送的内容并将其加密为字符串,然后写入响应流。

此外,当内容类型为 text/plain 时,响应会显示在 Postman 上,但当内容类型为 application/json 时,内容不会显示,但在标题中我可以看到内容长度有一些数字。而对于客户端,两种内容类型都会出现上述异常。

那么我的中间件代码中缺少什么?我知道这是导致问题的原因,因为当我在 startup.cs 中注释掉 MyMiddleware 时,正常流程可以正常工作。

下面是服务器端中间件中的Invoke function

public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
    string reponseContent = string.Empty;

    // Store the "pre-modified" response stream.
    var existingBody = context.Response.Body;

    using (var newBody = new MemoryStream())
    {
        // We set the response body to our stream so we can read after the chain of middlewares have been called.
        context.Response.Body = newBody;

        await next(context);

        // Set the stream back to the original.
        context.Response.Body = existingBody;

        newBody.Seek(0, SeekOrigin.Begin);

        //reading the content
        var contentReader = new StreamReader(newBody);
        reponseContent = await contentReader.ReadToEndAsync();

        string encryptedData = _cryptoService.Encrypt(reponseContent);

        // Send our modified content to the response body.
        await context.Response.WriteAsync(encryptedData);
}

【问题讨论】:

  • 你能不能尝试用这个reponseContent = await contentReader.ReadAsStringAsync();替换这个`reponseContent = await contentReader.ReadToEndAsync();`行,如果有任何变化或错误消息发生变化,请告诉我。
  • 另外分享您的_cryptoService 服务详情,以便我们重现问题。
  • @MdFaridUddinKiron contentReader 没有 ReadAsStringAsync() 方法。同样_cryptoService.Encrypt() 将字符串作为输入并使用客户端的公钥加密数据并返回base64string。我认为问题在于写入响应流,也许我在写入响应流时遗漏了一些东西。
  • 您好,请您检查一下答案并仔细检查您的中间件是如何在您的startus.cs 类上调用它的。我认为你可能有实施glitch

标签: c# asp.net-core postman


【解决方案1】:

我不确定您是如何调用 MiddleWare 的。我已成功重现该问题并得到相应的响应。

Middle Ware Class:

 public class CustomMiddleware
    {
        private readonly RequestDelegate next;

        public CustomMiddleware(RequestDelegate next)
        {
            this.next = next;
        }
        public async Task InvokeAsync(HttpContext context)
        {
            string reponseContent = string.Empty;

            // Store the "pre-modified" response stream.
            var existingBody = context.Response.Body;

            using (var newBody = new MemoryStream())
            {
                // We set the response body to our stream so we can read after the chain of middlewares have been called.
                context.Response.Body = newBody;

                await next(context);

                // Set the stream back to the original.
                context.Response.Body = existingBody;

                newBody.Seek(0, SeekOrigin.Begin);

                //reading the content
                var contentReader = new StreamReader(newBody);
                reponseContent = await contentReader.ReadToEndAsync();

                //  string encryptedData = _cryptoService.Encrypt(reponseContent);

                // Send our modified content to the response body.
                await context.Response.WriteAsync(reponseContent);
            }

        }
    }

Note: 你应该像这样使用构造函数来调用你的RequestDelegate。但是你设计了这个有两个参数,不知道你是如何在调用时传递参数的。

Startup.cs:

像这样在Startup.csConfigure调用中间件

app.UseMiddleware<CustomMiddleware>();

Request From Postman:

我用简单的plain textapplication/json 类型进行了测试。向我的控制器发送请求并修改控制器主体上的参数,并在中间件上实现更改。

Controller:

[HttpPost]
public IActionResult MiddlewareReStream([FromBody] Plans plan)
{
    plan.PlanName = "New Text";
    return Ok(plan);
}

MiddlWare Output:

Note: 请注意,我已使用"PlanName":"Test Plan" 调用请求并修改了middleware 成功调用更改的参数。

PostMan:

Note: 确保您已相应地调用或实现了InvokeAsync 中间件。因为我得到了你所期望的响应。

【讨论】:

  • 问题仍然存在。当您取消注释 encrypted 行时..问题仍然存在。 Encrypt() 是必需的,因为它将响应转换为加密字符串。问题是将这种加密形式写入流中。加密的数据是纯字符串,但从控制器返回的模型是 application/jsonSee,这表明我正在接收数据,但在正文中什么都没有。我还尝试在写入流之前序列化字符串,它没有工作
  • 你还没有分享_cryptoService.Encrypt(reponseContent);的实现细节,在这种情况下你需要分享它以便我们可以重现这个问题
  • 我创建了一个新的共享库。 See this。它有中间件,要求解密传入的请求并加密传出的响应。如果您需要了解更多信息,请发表评论。卡在这个问题上很长一段时间了。
  • 所以我理解你的服务有问题_cryptoService.Encrypt(reponseContent);。这不符合预期。
  • 它可以很好地加密和解密数据,但使用它会导致问题。因为无论内容类型是text/plain 还是application/json,加密和解密函数都会返回字符串。如果它是对象,那么它会写入序列化字符串,就像加密时我会序列化对象然后加密它一样。我不知道我应该怎么做才能解决这个问题。任何帮助将不胜感激。
【解决方案2】:

我认为响应的“Content-Length”标头表示数据在加密之前的大小,您需要根据新的加密数据重新计算大小并重置标头。我还认为不同的服务器对这种不匹配的反应不同,它可能适用于 IIS,但不适用于 Kestrel。

简而言之,请确保您的“Content-Length”和“Content-Type”标头与您实际发送的内容相匹配。

【讨论】:

    猜你喜欢
    • 2012-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多