Skip to content

Commit 7816ef9

Browse files
Handle synchronous exceptions from partial (#16679)
Handle sync exceptions within async context
1 parent a0dfffa commit 7816ef9

File tree

12 files changed

+94
-8
lines changed

12 files changed

+94
-8
lines changed

src/Mvc/Mvc.Core/src/Infrastructure/ContentResultExecutor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public virtual async Task ExecuteAsync(ActionContext context, ContentResult resu
5555
{
5656
response.ContentLength = resolvedContentTypeEncoding.GetByteCount(result.Content);
5757

58-
using (var textWriter = _httpResponseStreamWriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
58+
await using (var textWriter = _httpResponseStreamWriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
5959
{
6060
await textWriter.WriteAsync(result.Content);
6161

src/Mvc/Mvc.Razor/src/RazorView.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ private async Task RenderLayoutAsync(
286286
{
287287
// This means we're writing to a 'real' writer, probably to the actual output stream.
288288
// We're using PagedBufferedTextWriter here to 'smooth' synchronous writes of IHtmlContent values.
289-
using (var writer = _bufferScope.CreateWriter(context.Writer))
289+
await using (var writer = _bufferScope.CreateWriter(context.Writer))
290290
{
291291
await bodyWriter.Buffer.WriteToAsync(writer, _htmlEncoder);
292292
await writer.FlushAsync();

src/Mvc/Mvc.ViewFeatures/src/ViewComponentResultExecutor.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public virtual async Task ExecuteAsync(ActionContext context, ViewComponentResul
123123

124124
_writerFactory ??= context.HttpContext.RequestServices.GetRequiredService<IHttpResponseStreamWriterFactory>();
125125

126-
using (var writer = _writerFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
126+
await using (var writer = _writerFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
127127
{
128128
var viewContext = new ViewContext(
129129
context,
@@ -149,8 +149,8 @@ public virtual async Task ExecuteAsync(ActionContext context, ViewComponentResul
149149
}
150150
else
151151
{
152-
using var bufferingStream = new FileBufferingWriteStream();
153-
using (var intermediateWriter = _writerFactory.CreateWriter(bufferingStream, resolvedContentTypeEncoding))
152+
await using var bufferingStream = new FileBufferingWriteStream();
153+
await using (var intermediateWriter = _writerFactory.CreateWriter(bufferingStream, resolvedContentTypeEncoding))
154154
{
155155
viewComponentResult.WriteTo(intermediateWriter, _htmlEncoder);
156156
}

src/Mvc/Mvc.ViewFeatures/src/ViewExecutor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ protected async Task ExecuteAsync(
233233

234234
OnExecuting(viewContext);
235235

236-
using (var writer = WriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
236+
await using (var writer = WriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
237237
{
238238
var view = viewContext.View;
239239

src/Mvc/Mvc.ViewFeatures/test/ViewExecutorTest.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,44 @@ public static TheoryData<MediaTypeHeaderValue, string, string> ViewExecutorSetsC
8383
}
8484
}
8585

86+
[Fact]
87+
public async Task ExecuteAsync_ExceptionInSyncContext()
88+
{
89+
// Arrange
90+
var view = CreateView((v) =>
91+
{
92+
v.Writer.Write("xyz");
93+
throw new NotImplementedException("This should be raw!");
94+
});
95+
96+
var context = new DefaultHttpContext();
97+
var stream = new Mock<Stream>();
98+
stream.Setup(s => s.CanWrite).Returns(true);
99+
100+
context.Response.Body = stream.Object;
101+
var actionContext = new ActionContext(
102+
context,
103+
new RouteData(),
104+
new ActionDescriptor());
105+
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
106+
107+
var viewExecutor = CreateViewExecutor();
108+
109+
// Act
110+
var exception = await Assert.ThrowsAsync<NotImplementedException>(async () => await viewExecutor.ExecuteAsync(
111+
actionContext,
112+
view,
113+
viewData,
114+
Mock.Of<ITempDataDictionary>(),
115+
contentType: null,
116+
statusCode: null)
117+
);
118+
119+
// Assert
120+
Assert.Equal("This should be raw!", exception.Message);
121+
stream.Verify(s => s.Write(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>()), Times.Never);
122+
}
123+
86124
[Theory]
87125
[MemberData(nameof(ViewExecutorSetsContentTypeAndEncodingData))]
88126
public async Task ExecuteAsync_SetsContentTypeAndEncoding(

src/Mvc/test/Mvc.FunctionalTests/TagHelpersTest.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@ public async Task CanRenderViewsWithTagHelpers(string action)
6464
#endif
6565
}
6666

67+
[Fact]
68+
public async Task GivesCorrectCallstackForSyncronousCalls()
69+
{
70+
// Regression test for https://github.com/aspnet/AspNetCore/issues/15367
71+
// Arrange
72+
var exception = await Assert.ThrowsAsync<HttpRequestException>(async () => await Client.GetAsync("http://localhost/Home/MyHtml"));
73+
74+
// Assert
75+
Assert.Equal("Should be visible", exception.InnerException.InnerException.Message);
76+
}
77+
6778
[Fact]
6879
public async Task CanRenderViewsWithTagHelpersAndUnboundDynamicAttributes_Encoded()
6980
{
@@ -318,4 +329,4 @@ public async Task EncodersPages_ReturnExpectedContent(string actionName)
318329
#endif
319330
}
320331
}
321-
}
332+
}

src/Mvc/test/WebSites/TagHelpersWebSite/Controllers/HomeController.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ public IActionResult Help()
3535
return View();
3636
}
3737

38+
public IActionResult MyHtml()
39+
{
40+
return View();
41+
}
42+
3843
public IActionResult ViewComponentTagHelpers()
3944
{
4045
return View();
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.IO;
5+
using System.Text.Encodings.Web;
6+
using Microsoft.AspNetCore.Html;
7+
using Microsoft.AspNetCore.Mvc.Rendering;
8+
9+
namespace TagHelpersWebSite
10+
{
11+
public class MyHtmlContent : IHtmlContent
12+
{
13+
private IHtmlHelper Html { get; }
14+
15+
public MyHtmlContent(IHtmlHelper html)
16+
{
17+
Html = html;
18+
}
19+
20+
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
21+
{
22+
#pragma warning disable MVC1000 // Use of IHtmlHelper.{0} should be avoided.
23+
Html.Partial("_Test").WriteTo(writer, encoder);
24+
#pragma warning restore MVC1000 // Use of IHtmlHelper.{0} should be avoided.
25+
}
26+
}
27+
}

src/Mvc/test/WebSites/TagHelpersWebSite/Startup.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.IO;
55
using Microsoft.AspNetCore.Builder;
6+
using Microsoft.AspNetCore.Diagnostics;
67
using Microsoft.AspNetCore.Hosting;
78
using Microsoft.AspNetCore.Mvc;
89
using Microsoft.Extensions.DependencyInjection;

src/Mvc/test/WebSites/TagHelpersWebSite/TagHelpersWebSite.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<ItemGroup>
1010
<ProjectReference Include="..\RazorPagesClassLibrary\RazorPagesClassLibrary.csproj" />
1111
</ItemGroup>
12-
12+
1313
<ItemGroup>
1414
<Reference Include="Microsoft.AspNetCore.Mvc" />
1515
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@(new TagHelpersWebSite.MyHtmlContent(Html))
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@{
2+
throw new Exception("Should be visible");
3+
}

0 commit comments

Comments
 (0)