Handle synchronous exceptions from partial (#16679)

Handle sync exceptions within async context
This commit is contained in:
Ryan Brandenburg 2019-11-01 10:56:19 -07:00 committed by GitHub
parent a0dfffa4cb
commit 7816ef94ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 94 additions and 8 deletions

View File

@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
response.ContentLength = resolvedContentTypeEncoding.GetByteCount(result.Content);
using (var textWriter = _httpResponseStreamWriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
await using (var textWriter = _httpResponseStreamWriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
{
await textWriter.WriteAsync(result.Content);

View File

@ -286,7 +286,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
{
// This means we're writing to a 'real' writer, probably to the actual output stream.
// We're using PagedBufferedTextWriter here to 'smooth' synchronous writes of IHtmlContent values.
using (var writer = _bufferScope.CreateWriter(context.Writer))
await using (var writer = _bufferScope.CreateWriter(context.Writer))
{
await bodyWriter.Buffer.WriteToAsync(writer, _htmlEncoder);
await writer.FlushAsync();

View File

@ -123,7 +123,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
_writerFactory ??= context.HttpContext.RequestServices.GetRequiredService<IHttpResponseStreamWriterFactory>();
using (var writer = _writerFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
await using (var writer = _writerFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
{
var viewContext = new ViewContext(
context,
@ -149,8 +149,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
}
else
{
using var bufferingStream = new FileBufferingWriteStream();
using (var intermediateWriter = _writerFactory.CreateWriter(bufferingStream, resolvedContentTypeEncoding))
await using var bufferingStream = new FileBufferingWriteStream();
await using (var intermediateWriter = _writerFactory.CreateWriter(bufferingStream, resolvedContentTypeEncoding))
{
viewComponentResult.WriteTo(intermediateWriter, _htmlEncoder);
}

View File

@ -233,7 +233,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
OnExecuting(viewContext);
using (var writer = WriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
await using (var writer = WriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
{
var view = viewContext.View;

View File

@ -83,6 +83,44 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
}
}
[Fact]
public async Task ExecuteAsync_ExceptionInSyncContext()
{
// Arrange
var view = CreateView((v) =>
{
v.Writer.Write("xyz");
throw new NotImplementedException("This should be raw!");
});
var context = new DefaultHttpContext();
var stream = new Mock<Stream>();
stream.Setup(s => s.CanWrite).Returns(true);
context.Response.Body = stream.Object;
var actionContext = new ActionContext(
context,
new RouteData(),
new ActionDescriptor());
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
var viewExecutor = CreateViewExecutor();
// Act
var exception = await Assert.ThrowsAsync<NotImplementedException>(async () => await viewExecutor.ExecuteAsync(
actionContext,
view,
viewData,
Mock.Of<ITempDataDictionary>(),
contentType: null,
statusCode: null)
);
// Assert
Assert.Equal("This should be raw!", exception.Message);
stream.Verify(s => s.Write(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>()), Times.Never);
}
[Theory]
[MemberData(nameof(ViewExecutorSetsContentTypeAndEncodingData))]
public async Task ExecuteAsync_SetsContentTypeAndEncoding(

View File

@ -64,6 +64,17 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
#endif
}
[Fact]
public async Task GivesCorrectCallstackForSyncronousCalls()
{
// Regression test for https://github.com/aspnet/AspNetCore/issues/15367
// Arrange
var exception = await Assert.ThrowsAsync<HttpRequestException>(async () => await Client.GetAsync("http://localhost/Home/MyHtml"));
// Assert
Assert.Equal("Should be visible", exception.InnerException.InnerException.Message);
}
[Fact]
public async Task CanRenderViewsWithTagHelpersAndUnboundDynamicAttributes_Encoded()
{
@ -318,4 +329,4 @@ page:<root>root-content</root>"
#endif
}
}
}
}

View File

@ -35,6 +35,11 @@ namespace TagHelpersWebSite.Controllers
return View();
}
public IActionResult MyHtml()
{
return View();
}
public IActionResult ViewComponentTagHelpers()
{
return View();

View File

@ -0,0 +1,27 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace TagHelpersWebSite
{
public class MyHtmlContent : IHtmlContent
{
private IHtmlHelper Html { get; }
public MyHtmlContent(IHtmlHelper html)
{
Html = html;
}
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
{
#pragma warning disable MVC1000 // Use of IHtmlHelper.{0} should be avoided.
Html.Partial("_Test").WriteTo(writer, encoder);
#pragma warning restore MVC1000 // Use of IHtmlHelper.{0} should be avoided.
}
}
}

View File

@ -3,6 +3,7 @@
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;

View File

@ -9,7 +9,7 @@
<ItemGroup>
<ProjectReference Include="..\RazorPagesClassLibrary\RazorPagesClassLibrary.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Mvc" />
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />

View File

@ -0,0 +1 @@
@(new TagHelpersWebSite.MyHtmlContent(Html))

View File

@ -0,0 +1,3 @@
@{
throw new Exception("Should be visible");
}