• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

AndreuCodina / CrossValidation / 4271095502

pending completion
4271095502

push

github

Andreu Codina
Update exception middleware

316 of 346 branches covered (91.33%)

Branch coverage included in aggregate %.

8 of 8 new or added lines in 1 file covered. (100.0%)

938 of 1004 relevant lines covered (93.43%)

118.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.93
/src/CrossValidation/DependencyInjection/CrossValidationMiddleware.cs
1
using System.Net;
2
using System.Text.Json;
3
using CrossValidation.Errors;
4
using CrossValidation.Exceptions;
5
using CrossValidation.Utils;
6
using Microsoft.AspNetCore.Hosting;
7
using Microsoft.AspNetCore.Http;
8
using Microsoft.Extensions.Logging;
9

10
namespace CrossValidation.DependencyInjection;
11

12
public class CrossValidationMiddleware : IMiddleware
13
{
14
    private bool _isExceptionHandled = false;
15
    private readonly ILogger<CrossValidationMiddleware> _logger;
16
    private readonly IHostingEnvironment _environment;
17

18
    public CrossValidationMiddleware(ILoggerFactory loggerFactory, IHostingEnvironment environment)
20✔
19
    {
20
        _logger = loggerFactory.CreateLogger<CrossValidationMiddleware>();
20✔
21
        _environment = environment;
20✔
22
    }
20✔
23
    
24
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
25
    {
26
        try
27
        {
28
            await next(context);
20✔
29
        }
×
30
        catch (Exception e)
20✔
31
        {
32
            await HandleException(e, context);
20✔
33

34
            if (!_isExceptionHandled)
20!
35
            {
36
                throw;
×
37
            }
38
        }
39
    }
20✔
40

41
    private async Task HandleException(Exception exception, HttpContext context)
42
    {
43
        var problemDetails = new CrossProblemDetails();
20✔
44
        var httpStatusCode = HttpStatusCode.InternalServerError;
20✔
45
        string? title = null;
20✔
46
        string? details = null;
20✔
47
        List<CrossProblemDetailsError> errors = new();
20✔
48

49
        if (exception is CrossException crossException)
20✔
50
        {
51
            _isExceptionHandled = true;
18✔
52
            httpStatusCode = crossException.Error.HttpStatusCode ?? HttpStatusCode.UnprocessableEntity;
18✔
53
            title = "A validation error occurred";
18✔
54
            var error = CreateCrossProblemDetailsError(crossException.Error);
18✔
55
            
56
            var allErrorCustomizationsAreNotSet =
18✔
57
                error.Code is null
18✔
58
                && error.Message is null
18✔
59
                && error.Details is null
18✔
60
                && error.Placeholders is null;
18✔
61

62
            if (!allErrorCustomizationsAreNotSet)
18✔
63
            {
64
                errors.Add(error);
15✔
65
            }
66
        }
67
        else if (exception is ValidationListException validationListException)
2✔
68
        {
69
            _isExceptionHandled = true;
1✔
70
            httpStatusCode = HttpStatusCode.UnprocessableEntity;
1✔
71
            title = "Several validation errors occurred";
1✔
72

73
            foreach (var error in validationListException.Errors)
2!
74
            {
75
                errors.Add(CreateCrossProblemDetailsError(error));
×
76
            }
77
        }
78
        else if (exception is NonNullablePropertyIsNullException nonNullablePropertyIsNullException)
1!
79
        {
80
            _isExceptionHandled = true;
×
81
            httpStatusCode = HttpStatusCode.BadRequest;
×
82
            title = "Nullability error";
×
83
            details = $"Non nullable property is null: {nonNullablePropertyIsNullException.PropertyName}";
×
84
        }
85
        else if (exception is NonNullableItemCollectionWithNullItemException
1!
86
                 nonNullableItemCollectionWithNullItemException)
1✔
87
        {
88
            _isExceptionHandled = true;
×
89
            httpStatusCode = HttpStatusCode.BadRequest;
×
90
            title = "Nullability error";
×
91
            details = $"Non nullable item collection with null item: {nonNullableItemCollectionWithNullItemException.CollectionName}";
×
92
        }
93
        else
94
        {
95
            if (!CrossValidationOptions.HandleUnknownException)
1!
96
            {
97
                return;
×
98
            }
99
            
100
            _isExceptionHandled = true;
1✔
101
            _logger.LogError(exception, exception.Message);
1✔
102
            title = "An error occurred";;
1✔
103
            problemDetails.Detail = _environment.IsDevelopment() ? exception.Message : null;
1!
104
        }
105

106
        problemDetails.Status = (int)httpStatusCode;
20✔
107
        problemDetails.Title = title;
20✔
108
        problemDetails.Detail = details;
20✔
109
        problemDetails.Errors = errors.Any() ? errors : null;
20✔
110
        var response = JsonSerializer.Serialize(problemDetails);
20✔
111
        context.Response.StatusCode = problemDetails.Status.Value;
20✔
112
        context.Response.ContentType = "application/problem+json";
20✔
113
        context.Response.Headers.Add("X-Trace-Id", context.Request.Headers["X-Trace-Id"]);
20✔
114
        await context.Response.WriteAsync(response);
20✔
115
    }
20✔
116

117
    private CrossProblemDetailsError CreateCrossProblemDetailsError(ICrossError crossError)
118
    {
119
        var error = new CrossProblemDetailsError
18✔
120
        {
18✔
121
            Code = crossError.Code,
18✔
122
            Message = crossError.Message,
18✔
123
            Details = crossError.Details
18✔
124
        };
18✔
125

126
        if (CrossValidationOptions.LocalizeErrorInClient
18!
127
            && crossError.PlaceholderValues is not null)
18✔
128
        {
129
            var placeholders = new Dictionary<string, object>();
×
130
            
131
            foreach (var placeholder in crossError.PlaceholderValues)
×
132
            {
133
                placeholders.Add(placeholder.Key, placeholder.Value);
×
134
            }
135

136
            error.Placeholders = placeholders;
×
137
        }
138

139
        return error;
18✔
140
    }
141
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc