响应内容长度不匹配:写入的字节太少

10
我的 ASP.NET Core 应用程序使用“开箱即用”的外部登录身份验证。我想要实现的是,在 Facebook 挑战后,我想要包装重定向 URL 并将其作为 JSON 返回以在 jQuery 前端中使用。但在请求结束后,我在浏览器中看到 500 错误,并看到应用程序控制台中的下一个错误:
``` fail: Microsoft.AspNetCore.Server.Kestrel[13] Connection id "0HLV651D6KVJC", Request id "0HLV651D6KVJC:00000005": An unhandled exception was thrown by the application. System.InvalidOperationException: Response Content-Length mismatch: too few bytes written (0 of 470). ```
我的外部登录行动没有什么特别之处。
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public IActionResult ExternalLogin(string provider, string returnUrl = null)
{
    // Request a redirect to the external login provider.
    var redirectUrl = Url.Action(nameof(ExternalLoginCallback), "Account", new { returnUrl });
    var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
    return Challenge(properties, provider);
}

Facebook认证配置:

services.AddAuthentication().AddFacebook(facebookOptions =>
{
    facebookOptions.AppId = Configuration["Authentication:Facebook:AppId"];
    facebookOptions.AppSecret = Configuration["Authentication:Facebook:AppSecret"];

    facebookOptions.Events.OnRedirectToAuthorizationEndpoint =
        async (x) =>
        {

            UTF8Encoding encoding = new UTF8Encoding();
            var content = JsonConvert.SerializeObject(new { redirect_url = x.RedirectUri });
            byte[] bytes = encoding.GetBytes(content);

            x.Response.StatusCode = 200;
            x.Response.ContentLength = bytes.Length;
            x.Response.ContentType = "text/plain";
            x.Response.Body = new MemoryStream();

            await x.Response.WriteAsync(content);
            // at this point I see that x.Response.Body.Length == 470, but message states there are 0 of 470 written
        };
});

有没有什么办法让它工作?


无法修复此问题,因此不得不构建大量视图和操作,以创建自定义管道来解决OAuth弹出窗口的问题。 - Yehor Androsov
1
为什么要设置长度然后再分配主体?直接将字节写入x.Response.Body而不替换它即可。它显示0是因为您替换了主体并未向实际响应(原始主体)写入任何内容。 - davidfowl
@davidfowl 不记得为什么了,可能是我在这里找到了其他的代码片段。但你说得对,谢谢。 - Yehor Androsov
3个回答

18

当使用新的C# using 语法时,也可能发生这种情况:

using var ms = new MemoryStream();
using var writer = new StreamWriter(ms);
writer.WriteLine("my content");
memoryStream.Position = 0;
return File(ms, "text/plain");

在这种情况下,在 StreamWriter 刷新之前访问了 MemoryStream。要么使用 StreamWriter 的旧语法:

using var ms = new MemoryStream();
using (var writer = new StreamWriter(ms, Encoding.UTF8, -1, true))
{
    writer.WriteLine("my content");
}
memoryStream.Position = 0;
return File(ms, "text/plain");

或刷新写入器:

using var ms = new MemoryStream();
using var writer = new StreamWriter(ms);
writer.WriteLine("my content");
writer.Flush();
memoryStream.Position = 0;
return File(ms, "text/plain");

3
这对我很有帮助memoryStream.Position = 0;``` - Ricardo Huertas

2

代码已更改为写入原始响应流并且现在它有效了。

facebookOptions.Events.OnRedirectToAuthorizationEndpoint =
    async (x) =>
    {
        var content = JsonConvert.SerializeObject(new { redirect_url = x.RedirectUri });

        x.Response.StatusCode = 200;
        x.Response.ContentType = "text/plain";

        await x.Response.WriteAsync(content);
    };

1
移除转换为字节的操作,你没有在使用它 :D。 - davidfowl

2
你可以使用类似这样的东西:
var stream = new MemoryStream();
/// writing to the stream
if (stream.CanSeek)
{
   stream.Seek(0, SeekOrigin.Begin);
}
/// then read stream

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接