Skip to content
This repository was archived by the owner on Nov 25, 2025. It is now read-only.

Commit a562de5

Browse files
author
Ihar Yakimush
committed
mark handled concept
1 parent 25c39df commit a562de5

19 files changed

+247
-128
lines changed

Commmunity.AspNetCore.ExceptionHandling.Integration/Controllers/ValuesController.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ public IEnumerable<string> Get()
2121
[HttpGet("{id}")]
2222
public string Get(int id)
2323
{
24+
25+
if (id > 25)
26+
{
27+
throw new DuplicateWaitObjectException();
28+
}
29+
30+
if (id > 20)
31+
{
32+
throw new DuplicateNameException();
33+
}
34+
2435
if (id > 15)
2536
{
2637
throw new InvalidConstraintException();

Commmunity.AspNetCore.ExceptionHandling.Integration/Startup.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Data;
34
using System.IO;
45
using System.Linq;
56
using System.Text;
@@ -29,9 +30,13 @@ public void ConfigureServices(IServiceCollection services)
2930

3031
services.AddExceptionHandlingPolicies(options =>
3132
{
33+
options.For<DuplicateNameException>().Retry().NextChain();
34+
options.For<DuplicateWaitObjectException>().Retry();
35+
3236
options.For<ArgumentOutOfRangeException>().Log().Rethrow();
37+
3338
options.For<InvalidCastException>()
34-
.NewResponse(e => 400)
39+
.Response(e => 400)
3540
.WithHeaders((h, e) => h["X-qwe"] = e.Message)
3641
.WithBody((stream, exception) =>
3742
{
@@ -41,18 +46,17 @@ public void ConfigureServices(IServiceCollection services)
4146
}
4247
})
4348
.NextChain();
44-
options.For<Exception>().Log(lo =>
45-
{
46-
lo.Formatter = (o, e) => "qwe";
47-
})
48-
.NewResponse(e => 500, RequestStartedBehaviour.Ignore).WithBody(
49+
options.For<Exception>()
50+
.Log(lo => { lo.Formatter = (o, e) => "qwe"; })
51+
.Response(e => 500, RequestStartedBehaviour.SkipHandler).WithBody(
4952
async (stream, exception) =>
5053
{
5154
using (StreamWriter sw = new StreamWriter(stream))
5255
{
5356
await sw.WriteAsync("unhandled exception");
5457
}
55-
});
58+
})
59+
.Handled();
5660
});
5761

5862
services.AddLogging();

Commmunity.AspNetCore.ExceptionHandling/Events.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,9 @@ public static class Events
88
public static readonly EventId PolicyNotFound = new EventId(101, "PolicyForExceptionNotRegistered");
99
public static readonly EventId HandlersNotFound = new EventId(102, "HandlersCollectionEmpty");
1010
public static readonly EventId HandlerNotCreated = new EventId(103, "HandlersCanNotBeCreated");
11+
public static readonly EventId RetryForStartedResponce = new EventId(104, "RetryForStartedResponce");
12+
public static readonly EventId Retry = new EventId(105, "Retry");
13+
public static readonly EventId UnhandledResult = new EventId(106, "UnhandledResult");
14+
public static readonly EventId RetryIterationExceedLimit = new EventId(107, "RetryIterationExceedLimit");
1115
}
1216
}

Commmunity.AspNetCore.ExceptionHandling/ExceptionHandlingPolicyMiddleware.cs

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,22 @@ namespace Commmunity.AspNetCore.ExceptionHandling
1212
{
1313
public class ExceptionHandlingPolicyMiddleware : IMiddleware
1414
{
15+
public const int MaxRetryIterations = 10;
16+
1517
private readonly IOptions<ExceptionHandlingPolicyOptions> options;
1618

1719
public ExceptionHandlingPolicyMiddleware(IOptions<ExceptionHandlingPolicyOptions> options)
1820
{
1921
this.options = options ?? throw new ArgumentNullException(nameof(options));
2022
}
2123

22-
private static async Task<bool> EnumerateExceptionMapping(
24+
private static async Task<HandlerResult> EnumerateExceptionMapping(
2325
HttpContext context,
2426
ExceptionHandlingPolicyOptions policyOptions,
25-
Exception exception)
27+
Exception exception,
28+
ILogger logger)
2629
{
27-
Type exceptionType = exception.GetType();
28-
29-
ILogger logger = context.RequestServices.GetService<ILogger<ExceptionHandlingPolicyMiddleware>>() ??
30-
NullLoggerFactory.Instance.CreateLogger(Const.Category);
30+
Type exceptionType = exception.GetType();
3131

3232
bool mappingExists = false;
3333
HandlerResult handleResult = HandlerResult.ReThrow;
@@ -38,12 +38,7 @@ private static async Task<bool> EnumerateExceptionMapping(
3838
{
3939
mappingExists = true;
4040
handleResult = await EnumerateHandlers(context, type, exception, policyOptions, logger);
41-
42-
if (handleResult == HandlerResult.ReThrow)
43-
{
44-
return true;
45-
}
46-
41+
4742
if (handleResult != HandlerResult.NextChain)
4843
{
4944
break;
@@ -57,10 +52,10 @@ private static async Task<bool> EnumerateExceptionMapping(
5752
"Handlers mapping for exception type {exceptionType} not exists. Exception will be re-thrown. RequestId: {RequestId}",
5853
exceptionType, context.TraceIdentifier);
5954

60-
return false;
55+
return HandlerResult.ReThrow;
6156
}
6257

63-
return handleResult == HandlerResult.ReThrow;
58+
return handleResult;
6459
}
6560

6661
private static async Task<HandlerResult> EnumerateHandlers(
@@ -123,6 +118,14 @@ private static async Task<HandlerResult> EnumerateHandlers(
123118
}
124119

125120
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
121+
{
122+
ILogger logger = context.RequestServices.GetService<ILogger<ExceptionHandlingPolicyMiddleware>>() ??
123+
NullLoggerFactory.Instance.CreateLogger(Const.Category);
124+
125+
await InvokeWithRetryAsync(context, next, logger, 0);
126+
}
127+
128+
private async Task InvokeWithRetryAsync(HttpContext context, RequestDelegate next, ILogger logger, int iteration)
126129
{
127130
try
128131
{
@@ -132,10 +135,53 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next)
132135
{
133136
ExceptionHandlingPolicyOptions policyOptions = this.options.Value;
134137

135-
bool throwRequired = await EnumerateExceptionMapping(context, policyOptions, exception);
138+
var result = await EnumerateExceptionMapping(context, policyOptions, exception, logger);
139+
140+
if (result == HandlerResult.ReThrow)
141+
{
142+
throw;
143+
}
144+
145+
if (result == HandlerResult.Retry)
146+
{
147+
// We can't do anything if the response has already started, just abort.
148+
if (context.Response.HasStarted)
149+
{
150+
logger.LogWarning(Events.RetryForStartedResponce,
151+
exception,
152+
"Retry requested when responce already started. Exception will be re-thrown. RequestId: {RequestId}",
153+
context.TraceIdentifier);
154+
155+
throw;
156+
}
136157

137-
if (throwRequired)
158+
if (iteration > MaxRetryIterations)
159+
{
160+
logger.LogCritical(Events.RetryIterationExceedLimit,
161+
exception,
162+
"Retry iterations count exceed limit of {limit}. Possible issues with retry policy configuration. Exception will be re-thrown. RequestId: {RequestId}",
163+
MaxRetryIterations,
164+
context.TraceIdentifier);
165+
166+
throw;
167+
}
168+
169+
logger.LogWarning(Events.Retry,
170+
exception,
171+
"Retry requested. Iteration {iteration} RequestId: {RequestId}",
172+
iteration,
173+
context.TraceIdentifier);
174+
175+
await InvokeWithRetryAsync(context, next, logger, iteration + 1);
176+
}
177+
178+
if (result != HandlerResult.Handled)
138179
{
180+
logger.LogWarning(Events.UnhandledResult,
181+
exception,
182+
"After execution of all handlers exception was not marked as handled and will be re thrown. RequestId: {RequestId}",
183+
context.TraceIdentifier);
184+
139185
throw;
140186
}
141187
}

Commmunity.AspNetCore.ExceptionHandling/Handlers/HandlerOptions.cs

Lines changed: 0 additions & 7 deletions
This file was deleted.

Commmunity.AspNetCore.ExceptionHandling/Handlers/HandlerResult.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ public enum HandlerResult
88

99
NextChain = 2,
1010

11-
Terminate = 3
11+
Retry = 3,
12+
13+
Handled = 4
1214
}
1315
}

Commmunity.AspNetCore.ExceptionHandling/Handlers/HandlerWithLoggerOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
namespace Commmunity.AspNetCore.ExceptionHandling.Handlers
22
{
3-
public class HandlerWithLoggerOptions : HandlerOptions
3+
public class HandlerWithLoggerOptions
44
{
55
private string _loggerCategory = null;
66

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using System.Threading.Tasks;
5+
using Microsoft.AspNetCore.Http;
6+
7+
namespace Commmunity.AspNetCore.ExceptionHandling.Handlers
8+
{
9+
public class MarkHandledHandler : IExceptionHandler
10+
{
11+
public Task<HandlerResult> Handle(HttpContext httpContext, Exception exception)
12+
{
13+
return Task.FromResult(HandlerResult.Handled);
14+
}
15+
}
16+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using System.Threading.Tasks;
5+
using Commmunity.AspNetCore.ExceptionHandling.Handlers;
6+
using Microsoft.AspNetCore.Http;
7+
8+
namespace Commmunity.AspNetCore.ExceptionHandling.Logs
9+
{
10+
class DisableLoggingHandler : IExceptionHandler
11+
{
12+
public const string DisableLoggingFlagKey = "427EDE68BE9A";
13+
14+
public Task<HandlerResult> Handle(HttpContext httpContext, Exception exception)
15+
{
16+
exception.Data[DisableLoggingFlagKey] = string.Empty;
17+
18+
return Task.FromResult(HandlerResult.NextHandler);
19+
}
20+
}
21+
}

Commmunity.AspNetCore.ExceptionHandling/Logs/LogExceptionHandler.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ public LogExceptionHandler(IOptions<LogHandlerOptions<TException>> settings)
2323
}
2424
public Task<HandlerResult> Handle(HttpContext httpContext, Exception exception)
2525
{
26+
if (exception.Data.Contains(DisableLoggingHandler.DisableLoggingFlagKey))
27+
{
28+
return Task.FromResult(HandlerResult.NextHandler);
29+
}
30+
2631
ILoggerFactory loggerFactory =
2732
httpContext.RequestServices.GetService(typeof(ILoggerFactory)) as ILoggerFactory;
2833

0 commit comments

Comments
 (0)