Skip to content

Commit 84adbf4

Browse files
author
Vadim Belov
committed
Add 'extra' details support to WebApi exceptions
Extended all WebApi-related exception constructors to accept an optional 'extra' parameter for additional error context. Updated XML docs accordingly. Added Extra property to WebApiException and included traceId in ProblemDetails. Made AddExtra a public extension method with improved documentation. Minor doc and parameter naming improvements.
1 parent 67153db commit 84adbf4

8 files changed

Lines changed: 81 additions & 23 deletions

File tree

Sources/EasyExtensions.AspNetCore/Exceptions/AccessDeniedException.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ namespace EasyExtensions.AspNetCore.Exceptions
1313
/// access violation.</param>
1414
/// <param name="message">The error message that explains the reason for the exception. If not specified, a default message of "Access
1515
/// denied" is used.</param>
16-
public class AccessDeniedException(string objectName, string message = "Access denied")
17-
: WebApiException(HttpStatusCode.Forbidden, objectName, message)
16+
/// <param name="extra">Optional additional error details. This parameter can be used to provide extra information about the error, such as specific permission
17+
/// requirements, user roles, or any other relevant context that may help in understanding the access denial.</param>
18+
public class AccessDeniedException(string objectName, string message = "Access denied", object? extra = null)
19+
: WebApiException(HttpStatusCode.Forbidden, objectName, message, extra)
1820
{ }
1921

2022
/// <summary>
@@ -23,7 +25,9 @@ public class AccessDeniedException(string objectName, string message = "Access d
2325
/// </summary>
2426
/// <typeparam name="T">The type of the resource for which access was denied.</typeparam>
2527
/// <param name="message">The error message that describes the reason for the access denial.</param>
26-
public class AccessDeniedException<T>(string message = "Access denied")
27-
: AccessDeniedException(typeof(T).Name, message)
28+
/// <param name="extra">Optional additional error details. This parameter can be used to provide extra information about the error, such as specific permission
29+
/// requirements, user roles, or any other relevant context that may help in understanding the access denial.</param>
30+
public class AccessDeniedException<T>(string message = "Access denied", object? extra = null)
31+
: AccessDeniedException(typeof(T).Name, message, extra)
2832
{ }
2933
}

Sources/EasyExtensions.AspNetCore/Exceptions/BadRequestException.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,20 @@ namespace EasyExtensions.AspNetCore.Exceptions
1111
/// <param name="objectName">The name of the object or entity associated with the bad request. This value is used to provide context for the
1212
/// error.</param>
1313
/// <param name="message">The error message that describes the reason for the bad request. The default is "Bad request".</param>
14-
public class BadRequestException(string objectName, string message = "Bad request")
15-
: WebApiException(HttpStatusCode.BadRequest, objectName, message)
14+
/// <param name="extra">Optional additional error details. This parameter can be used to provide extra information about the error, such as specific permission
15+
/// requirements, user roles, or any other relevant context that may help in understanding the access denial.</param>
16+
public class BadRequestException(string objectName, string message = "Bad request", object? extra = null)
17+
: WebApiException(HttpStatusCode.BadRequest, objectName, message, extra)
1618
{ }
1719

1820
/// <summary>
1921
/// Represents an exception that is thrown to indicate a bad request error associated with a specific resource type.
2022
/// </summary>
2123
/// <typeparam name="T">The type of the resource or entity related to the bad request.</typeparam>
2224
/// <param name="message">The error message that describes the reason for the bad request. The default is "Bad request".</param>
23-
public class BadRequestException<T>(string message = "Bad request")
24-
: BadRequestException(typeof(T).Name, message)
25+
/// <param name="extra">Optional additional error details. This parameter can be used to provide extra information about the error, such as specific permission
26+
/// requirements, user roles, or any other relevant context that may help in understanding the access denial.</param>
27+
public class BadRequestException<T>(string message = "Bad request", object? extra = null)
28+
: BadRequestException(typeof(T).Name, message, extra)
2529
{ }
2630
}

Sources/EasyExtensions.AspNetCore/Exceptions/DuplicateException.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ namespace EasyExtensions.AspNetCore.Exceptions
1313
/// creation attempts in resource management scenarios.</remarks>
1414
/// <param name="objectName">The name of the object that caused the conflict.</param>
1515
/// <param name="message">The error message that describes the reason for the conflict. The default is "Object already exists."</param>
16-
public class DuplicateException(string objectName, string message = "Object already exists")
17-
: WebApiException(HttpStatusCode.Conflict, objectName, message)
16+
/// <param name="extra">Optional additional error details. This parameter can be used to provide extra information about the error, such as specific permission
17+
/// requirements, user roles, or any other relevant context that may help in understanding the access denial.</param>
18+
public class DuplicateException(string objectName, string message = "Object already exists", object? extra = null)
19+
: WebApiException(HttpStatusCode.Conflict, objectName, message, extra)
1820
{ }
1921

2022
/// <summary>
@@ -23,7 +25,9 @@ public class DuplicateException(string objectName, string message = "Object alre
2325
/// </summary>
2426
/// <typeparam name="T">The type of the object that caused the duplication error.</typeparam>
2527
/// <param name="message">The error message that explains the reason for the exception. The default is "Object already exists."</param>
26-
public class DuplicateException<T>(string message = "Object already exists")
27-
: DuplicateException(typeof(T).Name, message)
28+
/// <param name="extra">Optional additional error details. This parameter can be used to provide extra information about the error, such as specific permission
29+
/// requirements, user roles, or any other relevant context that may help in understanding the access denial.</param>
30+
public class DuplicateException<T>(string message = "Object already exists", object? extra = null)
31+
: DuplicateException(typeof(T).Name, message, extra)
2832
{ }
2933
}

Sources/EasyExtensions.AspNetCore/Exceptions/EntityInvalidTypeException.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ public class EntityInvalidTypeException(string message) : Exception(message)
1414
/// <summary>
1515
/// Throws an exception indicating that the entity type is invalid.
1616
/// </summary>
17-
/// <exception cref="EntityInvalidTypeException">Thrown when the entity type is invalid.</exception>
17+
/// <exception cref="EntityInvalidTypeException">Thrown when the entity type is invalid.</exception>'
18+
/// <param name="value">The value that is being checked for the expected type.</param>
19+
/// <param name="paramName">Optional name of the parameter being validated.</param>
1820
public static TEntity ThrowIfInvalidType<TEntity>(object? value, string? paramName = null)
1921
{
2022
if (value is not TEntity typed)

Sources/EasyExtensions.AspNetCore/Exceptions/EntityNotFoundException.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,20 @@ namespace EasyExtensions.AspNetCore.Exceptions
1111
/// <param name="objectName">The name of the entity that was not found. This value is included in the exception details to identify the
1212
/// missing entity.</param>
1313
/// <param name="message">The error message that describes the reason for the exception. The default is "Entity was not found".</param>
14-
public class EntityNotFoundException(string objectName, string message = "Entity was not found")
15-
: WebApiException(HttpStatusCode.NotFound, objectName, message)
14+
/// <param name="extra">Optional additional error details. This parameter can be used to provide extra information about the error, such as specific permission
15+
/// requirements, user roles, or any other relevant context that may help in understanding the access denial.</param>
16+
public class EntityNotFoundException(string objectName, string message = "Entity was not found", object? extra = null)
17+
: WebApiException(HttpStatusCode.NotFound, objectName, message, extra)
1618
{ }
1719

1820
/// <summary>
1921
/// Represents an exception that is thrown when an entity of the specified type cannot be found.
2022
/// </summary>
2123
/// <typeparam name="T">The type of the entity that was not found.</typeparam>
2224
/// <param name="message">The error message that explains the reason for the exception. If not specified, a default message is used.</param>
23-
public class EntityNotFoundException<T>(string message = "Entity was not found")
24-
: EntityNotFoundException(typeof(T).Name, message)
25+
/// <param name="extra">Optional additional error details. This parameter can be used to provide extra information about the error, such as specific permission
26+
/// requirements, user roles, or any other relevant context that may help in understanding the access denial.</param>
27+
public class EntityNotFoundException<T>(string message = "Entity was not found", object? extra = null)
28+
: EntityNotFoundException(typeof(T).Name, message, extra)
2529
{ }
2630
}

Sources/EasyExtensions.AspNetCore/Exceptions/UnauthorizedException.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ namespace EasyExtensions.AspNetCore.Exceptions
1313
/// resource.</remarks>
1414
/// <param name="objectName">The name of the object or resource for which access was denied.</param>
1515
/// <param name="message">The error message that describes the reason for the unauthorized access. The default is "Unathorized".</param>
16-
public class UnauthorizedException(string objectName, string message = "Unathorized")
17-
: WebApiException(HttpStatusCode.Unauthorized, objectName, message)
16+
/// <param name="extra">Optional additional error details. This parameter can be used to provide extra information about the error, such as specific permission
17+
/// requirements, user roles, or any other relevant context that may help in understanding the access denial.</param>
18+
public class UnauthorizedException(string objectName, string message = "Unathorized", object? extra = null)
19+
: WebApiException(HttpStatusCode.Unauthorized, objectName, message, extra)
1820
{ }
1921

2022
/// <summary>
@@ -23,7 +25,9 @@ public class UnauthorizedException(string objectName, string message = "Unathori
2325
/// </summary>
2426
/// <typeparam name="T">The type of the resource or entity for which authorization failed.</typeparam>
2527
/// <param name="message">The error message that explains the reason for the exception. The default is "Unathorized".</param>
26-
public class UnauthorizedException<T>(string message = "Unathorized")
27-
: UnauthorizedException(typeof(T).Name, message)
28+
/// <param name="extra">Optional additional error details. This parameter can be used to provide extra information about the error, such as specific permission
29+
/// requirements, user roles, or any other relevant context that may help in understanding the access denial.</param>
30+
public class UnauthorizedException<T>(string message = "Unathorized", object? extra = null)
31+
: UnauthorizedException(typeof(T).Name, message, extra)
2832
{ }
2933
}

Sources/EasyExtensions.AspNetCore/Exceptions/WebApiException.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using EasyExtensions.Abstractions;
55
using EasyExtensions.Models;
6+
using Microsoft.AspNetCore.Mvc;
67
using System;
78
using System.Collections.Generic;
89
using System.Diagnostics;
@@ -21,7 +22,14 @@ namespace EasyExtensions.AspNetCore.Exceptions
2122
/// protocol.</param>
2223
/// <param name="objectName">The name of the object or entity related to the error. Used to identify the source of the error in the response.</param>
2324
/// <param name="message">The error message that describes the reason for the exception.</param>
24-
public class WebApiException(HttpStatusCode statusCode, string objectName, string message) : Exception(message), IHttpError
25+
/// <param name="extra">Optional additional error details. This parameter can be used to provide extra information about the error, such as specific permission
26+
/// requirements, user roles, or any other relevant context that may help in understanding the access denial.</param>
27+
public class WebApiException(
28+
HttpStatusCode statusCode,
29+
string objectName,
30+
string message,
31+
object? extra = null)
32+
: Exception(message), IHttpError
2533
{
2634
/// <summary>
2735
/// HTTP status code.
@@ -33,12 +41,29 @@ public class WebApiException(HttpStatusCode statusCode, string objectName, strin
3341
/// </summary>
3442
public string ObjectName { get; } = objectName;
3543

44+
/// <summary>
45+
/// Additional error details. This property can be used to provide extra
46+
/// information about the error, such as validation errors, stack traces, or any
47+
/// </summary>
48+
public object? Extra { get; } = extra;
49+
3650
/// <summary>
3751
/// Get error model.
3852
/// </summary>
3953
/// <returns> Error model. </returns>
4054
public ErrorModel GetErrorModel()
4155
{
56+
ProblemDetails details = new ProblemDetails()
57+
{
58+
Status = (int)StatusCode,
59+
Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1",
60+
Title = "Web API actions errors occurred.",
61+
};
62+
string? traceId = Activity.Current?.Id ?? "-";
63+
if (!string.IsNullOrWhiteSpace(traceId))
64+
{
65+
details.Extensions["traceId"] = traceId;
66+
}
4267
return new()
4368
{
4469
Status = (int)StatusCode,

Sources/EasyExtensions.AspNetCore/Extensions/ControllerBaseExtensions.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,18 @@ private static void AddTraceId(ControllerBase controller, ProblemDetails details
195195
}
196196
}
197197

198-
private static void AddExtra(ProblemDetails details, object? extra)
198+
/// <summary>
199+
/// Adds additional information to the specified <see cref="ProblemDetails"/> instance by populating its <see
200+
/// cref="ProblemDetails.Extensions"/> property with key-value pairs from the provided object.
201+
/// </summary>
202+
/// <remarks>If <paramref name="extra"/> is a dictionary, its key-value pairs are added directly
203+
/// to the <see cref="ProblemDetails.Extensions"/> property. If it is an object, its public properties
204+
/// (excluding indexers) are added as key-value pairs. Existing keys in <see cref="ProblemDetails.Extensions"/>
205+
/// may be overwritten.</remarks>
206+
/// <param name="details">The <see cref="ProblemDetails"/> instance to which extra information will be added. Cannot be null.</param>
207+
/// <param name="extra">An object containing additional data to add to the <see cref="ProblemDetails.Extensions"/> property. This
208+
/// can be a dictionary with string keys or an object with public properties. If null, no changes are made.</param>
209+
public static void AddExtra(this ProblemDetails details, object? extra)
199210
{
200211
if (extra is null)
201212
{

0 commit comments

Comments
 (0)