Skip to content

Commit 845726e

Browse files
Ihar YakimushIhar Yakimush
authored andcommitted
use stream instead of stream writer for response body
1 parent 4a29579 commit 845726e

File tree

15 files changed

+441
-28
lines changed

15 files changed

+441
-28
lines changed

Community.AspNetCore.ExceptionHandling.Integration/Startup.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Data;
3+
using System.Text;
34
using Community.AspNetCore.ExceptionHandling.Mvc;
45
using Microsoft.AspNetCore.Builder;
56
using Microsoft.AspNetCore.Hosting;
@@ -33,7 +34,11 @@ public void ConfigureServices(IServiceCollection services)
3334
options.For<InvalidCastException>()
3435
.Response(e => 400)
3536
.Headers((h, e) => h["X-qwe"] = e.Message)
36-
.WithBody((req,sw, exception) => sw.WriteAsync(exception.ToString()))
37+
.WithBody((req,sw, exception) =>
38+
{
39+
byte[] array = Encoding.UTF8.GetBytes(exception.ToString());
40+
return sw.WriteAsync(array, 0, array.Length);
41+
})
3742
.NextPolicy();
3843

3944
options.For<Exception>()
@@ -49,7 +54,8 @@ public void ConfigureServices(IServiceCollection services)
4954
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
5055
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
5156
{
52-
app.UseResponseBuffering().UseDeveloperExceptionPage().UseExceptionHandlingPolicies();
57+
//app.UseResponseBuffering();
58+
app.UseDeveloperExceptionPage().UseExceptionHandlingPolicies();
5359
app.UseMvc();
5460
}
5561
}

Community.AspNetCore.ExceptionHandling.Mvc/PolicyBuilderExtensions.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,14 @@ public static IResponseHandlers<TException> WithActionResult<TException, TResult
4444
return builder.WithBody((request, streamWriter, exception) =>
4545
{
4646
var context = request.HttpContext;
47-
var executor = ServiceProviderServiceExtensions.GetService<IActionResultExecutor<TResult>>(context.RequestServices);
47+
var executor = context.RequestServices.GetService<IActionResultExecutor<TResult>>();
4848

4949
if (executor == null)
5050
{
5151
throw new InvalidOperationException($"No result executor for '{typeof(TResult).FullName}' has been registered.");
5252
}
5353

54-
var routeData = RoutingHttpContextExtensions.GetRouteData(context) ?? EmptyRouteData;
54+
var routeData = context.GetRouteData() ?? EmptyRouteData;
5555

5656
var actionContext = new ActionContext(context, routeData, EmptyActionDescriptor);
5757

@@ -85,7 +85,7 @@ public static IResponseHandlers<TException> WithActionResult<TException, TResult
8585
where TException : Exception
8686
where TResult : IActionResult
8787
{
88-
return WithActionResult<TException, TResult>(builder, (request, exception) => result);
88+
return WithActionResult(builder, (request, exception) => result);
8989
}
9090

9191
/// <summary>
@@ -113,7 +113,7 @@ public static IResponseHandlers<TException> WithObjectResult<TException, TObject
113113
this IResponseHandlers<TException> builder, TObject value, int index = -1)
114114
where TException : Exception
115115
{
116-
return WithActionResult<TException, ObjectResult>(builder, new ObjectResult(value), index);
116+
return WithActionResult(builder, new ObjectResult(value), index);
117117
}
118118

119119
/// <summary>
@@ -141,7 +141,7 @@ public static IResponseHandlers<TException> WithObjectResult<TException, TObject
141141
this IResponseHandlers<TException> builder, Func<HttpRequest, TException, TObject> valueFactory, int index = -1)
142142
where TException : Exception
143143
{
144-
return WithActionResult<TException, ObjectResult>(builder, (request, exception) => new ObjectResult(valueFactory(request, exception)), index);
144+
return WithActionResult(builder, (request, exception) => new ObjectResult(valueFactory(request, exception)), index);
145145
}
146146
}
147147
}

Community.AspNetCore.ExceptionHandling.NewtonsoftJson/PolicyBuilderExtensions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.IO;
3+
using System.Text;
34
using System.Threading.Tasks;
45
using Community.AspNetCore.ExceptionHandling.Builder;
56
using Microsoft.AspNetCore.Http;
@@ -40,7 +41,7 @@ public static IResponseHandlers<TException> WithBodyJson<TException, TObject>(
4041
this IResponseHandlers<TException> builder, Func<HttpRequest, TException, TObject> valueFactory, JsonSerializerSettings settings = null, int index = -1)
4142
where TException : Exception
4243
{
43-
return builder.WithBody((request, streamWriter, exception) =>
44+
return builder.WithBody((request, stream, exception) =>
4445
{
4546
if (settings == null)
4647
{
@@ -65,15 +66,14 @@ public static IResponseHandlers<TException> WithBodyJson<TException, TObject>(
6566
//return Task.CompletedTask;
6667
using (MemoryStream ms = new MemoryStream())
6768
{
68-
using (StreamWriter sw = new StreamWriter(ms, streamWriter.Encoding, 1024, true))
69+
using (StreamWriter sw = new StreamWriter(ms, Encoding.UTF8, 1024, true))
6970
{
7071
jsonSerializer.Serialize(sw, val);
7172
}
7273

7374
ms.Seek(0, SeekOrigin.Begin);
7475
byte[] array = ms.ToArray();
75-
BinaryWriter binaryWriter = new BinaryWriter(streamWriter.BaseStream, streamWriter.Encoding, true);
76-
binaryWriter.Write(array);
76+
stream.WriteAsync(array, 0, array.Length);
7777

7878
return Task.CompletedTask;
7979
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp2.1</TargetFramework>
5+
6+
<IsPackable>false</IsPackable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.2" />
11+
<PackageReference Include="Microsoft.AspNetCore.Buffering" Version="0.2.2" />
12+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="2.1.1" />
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
14+
<PackageReference Include="Moq" Version="4.9.0" />
15+
<PackageReference Include="xunit" Version="2.3.1" />
16+
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
17+
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
18+
</ItemGroup>
19+
20+
<ItemGroup>
21+
<ProjectReference Include="..\Community.AspNetCore.ExceptionHandling.Mvc\Community.AspNetCore.ExceptionHandling.Mvc.csproj" />
22+
<ProjectReference Include="..\Community.AspNetCore.ExceptionHandling.NewtonsoftJson\Community.AspNetCore.ExceptionHandling.NewtonsoftJson.csproj" />
23+
<ProjectReference Include="..\Community.AspNetCore.ExceptionHandling\Community.AspNetCore.ExceptionHandling.csproj" />
24+
</ItemGroup>
25+
26+
</Project>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System.Threading.Tasks;
2+
using Community.AspNetCore.ExceptionHandling.Exc;
3+
using Community.AspNetCore.ExceptionHandling.Handlers;
4+
using Xunit;
5+
6+
namespace Community.AspNetCore.ExceptionHandling.Tests.Exc
7+
{
8+
9+
public class ReThrowExceptionHandlerTests
10+
{
11+
[Fact]
12+
public async Task AlwaysReThrowResult()
13+
{
14+
ReThrowExceptionHandler handler = new ReThrowExceptionHandler();
15+
HandlerResult result = await handler.Handle(null, null);
16+
17+
Assert.Equal(HandlerResult.ReThrow, result);
18+
}
19+
}
20+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.Threading.Tasks;
2+
using Community.AspNetCore.ExceptionHandling.Handlers;
3+
using Xunit;
4+
5+
namespace Community.AspNetCore.ExceptionHandling.Tests.Handlers
6+
{
7+
public class MarkHandledHandlerTests
8+
{
9+
[Fact]
10+
public async Task AlwaysHandledResult()
11+
{
12+
MarkHandledHandler handler = new MarkHandledHandler();
13+
HandlerResult result = await handler.Handle(null, null);
14+
15+
Assert.Equal(HandlerResult.Handled, result);
16+
}
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.Threading.Tasks;
2+
using Community.AspNetCore.ExceptionHandling.Handlers;
3+
using Xunit;
4+
5+
namespace Community.AspNetCore.ExceptionHandling.Tests.Handlers
6+
{
7+
public class NextChainHandlerTests
8+
{
9+
[Fact]
10+
public async Task AlwaysNextChainResult()
11+
{
12+
NextChainHandler handler = new NextChainHandler();
13+
HandlerResult result = await handler.Handle(null, null);
14+
15+
Assert.Equal(HandlerResult.NextChain, result);
16+
}
17+
}
18+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System;
2+
using System.Runtime.Serialization;
3+
4+
namespace Community.AspNetCore.ExceptionHandling.Tests.Scenarious
5+
{
6+
[Serializable]
7+
public class BaseException : Exception
8+
{
9+
10+
}
11+
12+
[Serializable]
13+
public class NotBaseException : Exception
14+
{
15+
16+
}
17+
18+
[Serializable]
19+
public class RethrowException : BaseException
20+
{
21+
22+
}
23+
24+
[Serializable]
25+
public class NextEmptyException : BaseException
26+
{
27+
28+
}
29+
30+
[Serializable]
31+
public class EmptyChainException : BaseException
32+
{
33+
34+
}
35+
36+
[Serializable]
37+
public class HandledWithoutResponseException : BaseException
38+
{
39+
40+
}
41+
42+
[Serializable]
43+
public class CommonResponseException : BaseException
44+
{
45+
46+
}
47+
48+
[Serializable]
49+
public class CustomResponseException : BaseException
50+
{
51+
52+
}
53+
54+
[Serializable]
55+
public class CustomObjectResponseException : BaseException
56+
{
57+
58+
}
59+
60+
[Serializable]
61+
public class CustomJsonResponseException : BaseException
62+
{
63+
64+
}
65+
66+
[Serializable]
67+
public class RetryException : BaseException
68+
{
69+
70+
}
71+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using Microsoft.AspNetCore.Mvc.Testing;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Net;
6+
using System.Net.Http;
7+
using System.Threading.Tasks;
8+
using Community.AspNetCore.ExceptionHandling.Tests.Scenarious;
9+
using Microsoft.AspNetCore;
10+
using Xunit;
11+
using Microsoft.AspNetCore.Hosting;
12+
13+
namespace Community.AspNetCore.ExceptionHandling.Tests
14+
{
15+
public class ScenariosTests : WebApplicationFactory<Startup>
16+
{
17+
protected override IWebHostBuilder CreateWebHostBuilder()
18+
{
19+
return WebHost.CreateDefaultBuilder().UseStartup<Startup>();
20+
}
21+
22+
private HttpClient Client =>
23+
this.CreateDefaultClient(new Uri("http://example.com"));
24+
25+
[Fact]
26+
public async Task Ok()
27+
{
28+
HttpResponseMessage resp = await Client.GetAsync("ok");
29+
await AssertResponse(resp, HttpStatusCode.OK, "ok");
30+
}
31+
32+
[Fact]
33+
public async Task HandledWithoutResponse()
34+
{
35+
HttpResponseMessage resp = await Client.GetAsync("handled");
36+
var str = await AssertResponse(resp, HttpStatusCode.OK, null);
37+
Assert.Equal(string.Empty, str);
38+
}
39+
40+
[Fact]
41+
public async Task CustomResponse()
42+
{
43+
HttpResponseMessage resp = await Client.GetAsync("custom");
44+
await AssertResponse(resp, HttpStatusCode.BadRequest, "customResponse",
45+
new KeyValuePair<string, string>("X-Custom", "val"));
46+
}
47+
48+
[Fact]
49+
public async Task CustomObjectResponse()
50+
{
51+
HttpResponseMessage resp = await Client.GetAsync("object");
52+
await AssertResponse(resp, HttpStatusCode.NotFound, "message");
53+
}
54+
55+
[Fact]
56+
public async Task CustomJsonResponse()
57+
{
58+
HttpResponseMessage resp = await Client.GetAsync("json");
59+
await AssertResponse(resp, HttpStatusCode.Forbidden, "message");
60+
}
61+
62+
[Fact]
63+
public async Task CommonResponse()
64+
{
65+
HttpResponseMessage resp = await Client.GetAsync("common");
66+
await AssertResponse(resp, HttpStatusCode.InternalServerError, "commonResponse",
67+
new KeyValuePair<string, string>("X-Common", "val"));
68+
}
69+
70+
[Fact]
71+
public async Task ReThrow()
72+
{
73+
HttpResponseMessage resp = await Client.GetAsync("rethrow");
74+
await AssertUnhandledException<RethrowException>(resp);
75+
}
76+
77+
[Fact]
78+
public async Task NextHandlerNotAwailable()
79+
{
80+
HttpResponseMessage resp = await Client.GetAsync("nextempty");
81+
await AssertUnhandledException<NextEmptyException>(resp);
82+
}
83+
84+
[Fact]
85+
public async Task NextPolicyNotAwailable()
86+
{
87+
HttpResponseMessage resp = await Client.GetAsync("nextpolicyempty");
88+
await AssertUnhandledException<NotBaseException>(resp);
89+
}
90+
91+
[Fact]
92+
public async Task EmptyChain()
93+
{
94+
HttpResponseMessage resp = await Client.GetAsync("emptychain");
95+
await AssertUnhandledException<EmptyChainException>(resp);
96+
}
97+
98+
private static async Task AssertUnhandledException<TException>(HttpResponseMessage response)
99+
{
100+
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
101+
string str = await response.Content.ReadAsStringAsync();
102+
Assert.Contains("An unhandled exception occurred while processing the request", str);
103+
Assert.Contains(typeof(TException).Name, str);
104+
}
105+
106+
private static async Task<string> AssertResponse(HttpResponseMessage response, HttpStatusCode statusCode, string body, params KeyValuePair<string,string>[] headers)
107+
{
108+
Assert.Equal(statusCode, response.StatusCode);
109+
string str = await response.Content.ReadAsStringAsync();
110+
foreach (var header in headers)
111+
{
112+
Assert.True(response.Headers.Contains(header.Key), $"Header {header.Key} not awailable");
113+
Assert.Equal(header.Value, response.Headers.GetValues(header.Key).First());
114+
}
115+
116+
if (body != null)
117+
{
118+
Assert.Contains(body, str);
119+
}
120+
121+
return str;
122+
}
123+
}
124+
}

0 commit comments

Comments
 (0)