refactor exception handler

This commit is contained in:
meysamhadeli 2022-05-26 00:27:57 +04:30
parent ccb7ffa035
commit e2a03daf46
22 changed files with 140 additions and 154 deletions

View File

@ -1,6 +1,5 @@
using BuildingBlocks.Jwt;
using BuildingBlocks.Logging;
using BuildingBlocks.Utils;
using BuildingBlocks.Web;
using Figgle;
using Microsoft.AspNetCore.Authentication;

View File

@ -2,14 +2,10 @@
"AppOptions": {
"Name": "ApiGateway"
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
"LogOptions": {
"Level": "Information",
"LogTemplate": "{Timestamp:HH:mm:ss} [{Level:u4}] {Message:lj}{NewLine}{Exception}",
"ElasticUri": "http://localhost:9200"
},
"Yarp": {
"routes": {

View File

@ -5,7 +5,7 @@ namespace BuildingBlocks.Exception;
public class AppException : CustomException
{
public AppException(string message, string code = null) : base(message, code: code)
public AppException(string message, int? code = null) : base(message, code: code)
{
}
@ -13,11 +13,11 @@ public class AppException : CustomException
{
}
public AppException(string message, HttpStatusCode statusCode, string code = null) : base(message, statusCode, code)
public AppException(string message, HttpStatusCode statusCode, int? code = null) : base(message, statusCode, code)
{
}
public AppException(string message, System.Exception innerException, string code = null) : base(message, innerException, code: code)
public AppException(string message, System.Exception innerException, int? code = null) : base(message, innerException, code: code)
{
}
}

View File

@ -4,7 +4,7 @@ namespace BuildingBlocks.Exception
{
public class BadRequestException : CustomException
{
public BadRequestException(string message, string code = null) : base(message, code: code)
public BadRequestException(string message, int? code = null) : base(message, code: code)
{
}

View File

@ -2,7 +2,7 @@ namespace BuildingBlocks.Exception
{
public class ConflictException : CustomException
{
public ConflictException(string message, string code = null) : base(message, code: code)
public ConflictException(string message, int? code = null) : base(message, code: code)
{
}
}

View File

@ -7,7 +7,7 @@ public class CustomException : System.Exception
public CustomException(
string message,
HttpStatusCode statusCode = HttpStatusCode.BadRequest,
string code = null) : base(message)
int? code = null) : base(message)
{
StatusCode = statusCode;
Code = code;
@ -17,7 +17,7 @@ public class CustomException : System.Exception
string message,
System.Exception innerException,
HttpStatusCode statusCode = HttpStatusCode.BadRequest,
string code = null) : base(message, innerException)
int? code = null) : base(message, innerException)
{
StatusCode = statusCode;
Code = code;
@ -25,7 +25,7 @@ public class CustomException : System.Exception
public CustomException(
HttpStatusCode statusCode = HttpStatusCode.BadRequest,
string code = null) : base()
int? code = null) : base()
{
StatusCode = statusCode;
Code = code;
@ -33,5 +33,5 @@ public class CustomException : System.Exception
public HttpStatusCode StatusCode { get; }
public string Code { get; }
public int? Code { get; }
}

View File

@ -1,11 +0,0 @@
using System.Net;
namespace BuildingBlocks.Exception;
public class IdentityException : CustomException
{
public IdentityException(string message = default, HttpStatusCode statusCode = default, string code = null)
: base(message, statusCode, code)
{
}
}

View File

@ -8,9 +8,9 @@ namespace BuildingBlocks.Exception
{
public InternalServerException() : base() { }
public InternalServerException(string message, string code) : base(message, HttpStatusCode.InternalServerError, code: code) { }
public InternalServerException(string message, int? code) : base(message, HttpStatusCode.InternalServerError, code: code) { }
public InternalServerException(string message, string code = null, params object[] args)
public InternalServerException(string message, int? code = null, params object[] args)
: base(message:String.Format(CultureInfo.CurrentCulture, message, args, HttpStatusCode.InternalServerError, code))
{
}

View File

@ -2,7 +2,7 @@ namespace BuildingBlocks.Exception
{
public class NotFoundException : CustomException
{
public NotFoundException(string message, string code = null) : base(message, code: code)
public NotFoundException(string message, int? code = null) : base(message, code: code)
{
}
}

View File

@ -1,8 +1,10 @@
using Microsoft.AspNetCore.Mvc;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Mvc;
namespace BuildingBlocks.Exception;
public class ProblemDetailsWithCode : ProblemDetails
{
public string Code { get; set; }
[JsonPropertyName("code")]
public int? Code { get; set; }
}

View File

@ -4,11 +4,8 @@ namespace BuildingBlocks.Exception
{
public class ValidationException : CustomException
{
public ValidationException(ValidationResultModel validationResultModel)
public ValidationException(string message, int? code = null) : base(message, code: code)
{
ValidationResultModel = validationResultModel;
}
public ValidationResultModel ValidationResultModel { get; }
}
}

View File

@ -1,7 +1,5 @@
using BuildingBlocks.Exception;
using FluentValidation;
using FluentValidation.Results;
using ValidationException = BuildingBlocks.Exception.ValidationException;
namespace BuildingBlocks.Validation
{
@ -15,7 +13,7 @@ namespace BuildingBlocks.Validation
var validationResult = await validator.ValidateAsync(request);
if (!validationResult.IsValid)
{
throw new BadRequestException(validationResult.Errors.FirstOrDefault()?.ErrorMessage);
throw new Exception.ValidationException(validationResult.Errors?.First()?.ErrorMessage);
}
}
}

View File

@ -8,21 +8,14 @@ namespace BuildingBlocks.Validation
{
public class ValidationResultModel
{
public int StatusCode { get; set; } = (int) HttpStatusCode.BadRequest;
public int StatusCode { get; set; } = (int)HttpStatusCode.BadRequest;
public string Message { get; set; } = "Validation Failed.";
public List<ValidationError> Errors { get; }
public ValidationResultModel(ValidationResult validationResult = null)
{
Errors = validationResult.Errors
.Select(error => new ValidationError(error.PropertyName, error.ErrorMessage))
.ToList();
}
public List<ValidationFailure> Errors { get; set; }
public override string ToString()
{
return JsonSerializer.Serialize(this);
}
}
}
}

View File

@ -4,7 +4,7 @@ namespace Booking.Booking.Exceptions;
public class BookingAlreadyExistException : ConflictException
{
public BookingAlreadyExistException(string code = default) : base("Booking already exist!", code)
public BookingAlreadyExistException(int? code = default) : base("Booking already exist!", code)
{
}
}

View File

@ -22,56 +22,49 @@ public static class ProblemDetailsExtensions
var env = ctx.RequestServices.GetRequiredService<IHostEnvironment>();
return env.IsDevelopment() || env.IsStaging();
};
x.Map<ConflictException>(ex => new ProblemDetails
x.Map<ConflictException>(ex => new ProblemDetailsWithCode
{
Title = "Application rule broken",
Status = StatusCodes.Status409Conflict,
Detail = ex.Message,
Type = "https://somedomain/application-rule-validation-error"
Type = "https://somedomain/application-rule-validation-error",
});
// Exception will produce and returns from our FluentValidation RequestValidationBehavior
x.Map<ValidationException>(ex => new ProblemDetails
x.Map<ValidationException>(ex => new ProblemDetailsWithCode
{
Title = "input validation rules broken",
Status = StatusCodes.Status400BadRequest,
Detail = JsonConvert.SerializeObject(ex.ValidationResultModel.Errors),
Type = "https://somedomain/input-validation-rules-error"
Status = (int)ex.StatusCode,
Detail = ex.Message,
Type = "https://somedomain/input-validation-rules-error",
});
x.Map<BadRequestException>(ex => new ProblemDetails
x.Map<BadRequestException>(ex => new ProblemDetailsWithCode
{
Title = "bad request exception",
Status = StatusCodes.Status400BadRequest,
Detail = ex.Message,
Type = "https://somedomain/bad-request-error"
Type = "https://somedomain/bad-request-error",
});
x.Map<NotFoundException>(ex => new ProblemDetails
x.Map<NotFoundException>(ex => new ProblemDetailsWithCode
{
Title = "not found exception",
Status = StatusCodes.Status404NotFound,
Detail = ex.Message,
Type = "https://somedomain/not-found-error"
Type = "https://somedomain/not-found-error",
});
x.Map<InternalServerException>(ex => new ProblemDetails
x.Map<InternalServerException>(ex => new ProblemDetailsWithCode
{
Title = "api server exception",
Status = StatusCodes.Status400BadRequest,
Detail = ex.Message,
Type = "https://somedomain/api-server-error"
});
x.Map<AppException>(ex => new ProblemDetails
{
Title = "application exception",
Status = StatusCodes.Status500InternalServerError,
Detail = ex.Message,
Type = "https://somedomain/application-error"
Type = "https://somedomain/api-server-error",
});
x.Map<IdentityException>(ex => new ProblemDetails
x.Map<AppException>(ex => new ProblemDetailsWithCode
{
Status = (int)ex.StatusCode,
Title = "identity exception",
Title = "application exception",
Status = StatusCodes.Status400BadRequest,
Detail = ex.Message,
Type = "https://somedomain/identity-error"
Type = "https://somedomain/application-error",
});
x.Map<RpcException>(ex => new ProblemDetails
@ -83,6 +76,22 @@ public static class ProblemDetailsExtensions
});
x.MapToStatusCode<ArgumentNullException>(StatusCodes.Status400BadRequest);
x.MapStatusCode = context =>
{
return context.Response.StatusCode switch
{
StatusCodes.Status401Unauthorized => new ProblemDetailsWithCode
{
Status = context.Response.StatusCode,
Title = "identity exception",
Detail = "You are not Authorized",
Type = "https://somedomain/identity-error",
},
_ => new StatusCodeProblemDetails(context.Response.StatusCode)
};
};
});
return services;
}

View File

@ -4,7 +4,7 @@ namespace Flight.Airports.Exceptions;
public class AirportAlreadyExistException : ConflictException
{
public AirportAlreadyExistException(string code = default) : base("Airport already exist!", code)
public AirportAlreadyExistException(int? code = default) : base("Airport already exist!", code)
{
}
}

View File

@ -2,10 +2,8 @@ using System;
using BuildingBlocks.Exception;
using Hellang.Middleware.ProblemDetails;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
namespace Flight.Extensions;
@ -27,64 +25,63 @@ public static class ProblemDetailsExtensions
Title = "Application rule broken",
Status = StatusCodes.Status409Conflict,
Detail = ex.Message,
Type = "https://somedomain/application-rule-validation-error",
Code = ex.Code
Type = "https://somedomain/application-rule-validation-error"
});
// Exception will produce and returns from our FluentValidation RequestValidationBehavior
x.Map<ValidationException>(ex => new ProblemDetailsWithCode
{
Title = "input validation rules broken",
Status = StatusCodes.Status400BadRequest,
Detail = JsonConvert.SerializeObject(ex.ValidationResultModel.Errors),
Type = "https://somedomain/input-validation-rules-error",
Code = ex.Code
Status = (int)ex.StatusCode,
Detail = ex.Message,
Type = "https://somedomain/input-validation-rules-error"
});
x.Map<BadRequestException>(ex => new ProblemDetailsWithCode
{
Title = "bad request exception",
Status = StatusCodes.Status400BadRequest,
Detail = ex.Message,
Type = "https://somedomain/bad-request-error",
Code = ex.Code
Type = "https://somedomain/bad-request-error"
});
x.Map<NotFoundException>(ex => new ProblemDetailsWithCode
{
Title = "not found exception",
Status = StatusCodes.Status404NotFound,
Detail = ex.Message,
Type = "https://somedomain/not-found-error",
Code = ex.Code
Type = "https://somedomain/not-found-error"
});
x.Map<InternalServerException>(ex => new ProblemDetailsWithCode
{
Title = "api server exception",
Status = StatusCodes.Status500InternalServerError,
Detail = ex.Message,
Type = "https://somedomain/api-server-error",
Code = ex.Code
Type = "https://somedomain/api-server-error"
});
x.Map<AppException>(ex => new ProblemDetailsWithCode
{
Title = "application exception",
Status = StatusCodes.Status500InternalServerError,
Status = StatusCodes.Status400BadRequest,
Detail = ex.Message,
Type = "https://somedomain/application-error",
Code = ex.Code
});
x.Map<IdentityException>(ex =>
{
var pd = new ProblemDetailsWithCode
{
Status = (int)ex.StatusCode,
Title = "identity exception",
Detail = ex.Message,
Type = "https://somedomain/identity-error",
Code = ex.Code
};
return pd;
Type = "https://somedomain/application-error"
});
x.MapToStatusCode<ArgumentNullException>(StatusCodes.Status400BadRequest);
x.MapStatusCode = context =>
{
return context.Response.StatusCode switch
{
StatusCodes.Status401Unauthorized => new ProblemDetailsWithCode
{
Status = context.Response.StatusCode,
Title = "identity exception",
Detail = "You are not Authorized",
Type = "https://somedomain/identity-error"
},
_ => new StatusCodeProblemDetails(context.Response.StatusCode)
};
};
});
return services;
}

View File

@ -4,7 +4,7 @@ namespace Flight.Flights.Exceptions;
public class FlightAlreadyExistException : ConflictException
{
public FlightAlreadyExistException(string code = default) : base("Flight already exist!", code)
public FlightAlreadyExistException(int? code = default) : base("Flight already exist!", code)
{
}
}

View File

@ -4,7 +4,7 @@ namespace Flight.Seats.Exceptions;
public class SeatAlreadyChosenException : ConflictException
{
public SeatAlreadyChosenException(string code = default) : base("Seat already chosen!", code)
public SeatAlreadyChosenException(int? code = default) : base("Seat already chosen!", code)
{
}
}

View File

@ -4,7 +4,7 @@ namespace Flight.Seats.Exceptions;
public class SeatAlreadyExistException : ConflictException
{
public SeatAlreadyExistException(string code = default) : base("Seat already exist!", code)
public SeatAlreadyExistException(int? code = default) : base("Seat already exist!", code)
{
}
}

View File

@ -2,10 +2,8 @@ using System;
using BuildingBlocks.Exception;
using Hellang.Middleware.ProblemDetails;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
namespace Identity.Extensions;
@ -22,7 +20,7 @@ public static class ProblemDetailsExtensions
var env = ctx.RequestServices.GetRequiredService<IHostEnvironment>();
return env.IsDevelopment() || env.IsStaging();
};
x.Map<ConflictException>(ex => new ProblemDetails
x.Map<ConflictException>(ex => new ProblemDetailsWithCode
{
Title = "Application rule broken",
Status = StatusCodes.Status409Conflict,
@ -31,55 +29,60 @@ public static class ProblemDetailsExtensions
});
// Exception will produce and returns from our FluentValidation RequestValidationBehavior
x.Map<ValidationException>(ex => new ProblemDetails
x.Map<ValidationException>(ex => new ProblemDetailsWithCode
{
Title = "input validation rules broken",
Status = StatusCodes.Status400BadRequest,
Detail = JsonConvert.SerializeObject(ex.ValidationResultModel.Errors),
Status = (int)ex.StatusCode,
Detail = ex.Message,
Type = "https://somedomain/input-validation-rules-error"
});
x.Map<BadRequestException>(ex => new ProblemDetails
x.Map<BadRequestException>(ex => new ProblemDetailsWithCode
{
Title = "bad request exception",
Status = StatusCodes.Status400BadRequest,
Detail = ex.Message,
Type = "https://somedomain/bad-request-error"
});
x.Map<NotFoundException>(ex => new ProblemDetails
x.Map<NotFoundException>(ex => new ProblemDetailsWithCode
{
Title = "not found exception",
Status = StatusCodes.Status404NotFound,
Detail = ex.Message,
Type = "https://somedomain/not-found-error"
});
x.Map<InternalServerException>(ex => new ProblemDetails
x.Map<InternalServerException>(ex => new ProblemDetailsWithCode
{
Title = "api server exception",
Status = StatusCodes.Status500InternalServerError,
Detail = ex.Message,
Type = "https://somedomain/api-server-error"
});
x.Map<AppException>(ex => new ProblemDetails
x.Map<AppException>(ex => new ProblemDetailsWithCode
{
Title = "application exception",
Status = StatusCodes.Status500InternalServerError,
Status = StatusCodes.Status400BadRequest,
Detail = ex.Message,
Type = "https://somedomain/application-error"
});
x.Map<IdentityException>(ex =>
{
var pd = new ProblemDetails
{
Status = (int)ex.StatusCode,
Title = "identity exception",
Detail = ex.Message,
Type = "https://somedomain/identity-error"
};
return pd;
});
x.MapToStatusCode<ArgumentNullException>(StatusCodes.Status400BadRequest);
x.MapStatusCode = context =>
{
return context.Response.StatusCode switch
{
StatusCodes.Status401Unauthorized => new ProblemDetailsWithCode
{
Status = context.Response.StatusCode,
Title = "identity exception",
Detail = "You are not Authorized",
Type = "https://somedomain/identity-error"
},
_ => new StatusCodeProblemDetails(context.Response.StatusCode)
};
};
});
return services;
}
}
}

View File

@ -1,10 +1,8 @@
using BuildingBlocks.Exception;
using Hellang.Middleware.ProblemDetails;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
namespace Passenger.Extensions;
@ -21,7 +19,7 @@ public static class ProblemDetailsExtensions
var env = ctx.RequestServices.GetRequiredService<IHostEnvironment>();
return env.IsDevelopment() || env.IsStaging();
};
x.Map<ConflictException>(ex => new ProblemDetails
x.Map<ConflictException>(ex => new ProblemDetailsWithCode
{
Title = "Application rule broken",
Status = StatusCodes.Status409Conflict,
@ -30,55 +28,60 @@ public static class ProblemDetailsExtensions
});
// Exception will produce and returns from our FluentValidation RequestValidationBehavior
x.Map<ValidationException>(ex => new ProblemDetails
x.Map<ValidationException>(ex => new ProblemDetailsWithCode
{
Title = "input validation rules broken",
Status = StatusCodes.Status400BadRequest,
Detail = JsonConvert.SerializeObject(ex.ValidationResultModel.Errors),
Status = (int)ex.StatusCode,
Detail = ex.Message,
Type = "https://somedomain/input-validation-rules-error"
});
x.Map<BadRequestException>(ex => new ProblemDetails
x.Map<BadRequestException>(ex => new ProblemDetailsWithCode
{
Title = "bad request exception",
Status = StatusCodes.Status400BadRequest,
Detail = ex.Message,
Type = "https://somedomain/bad-request-error"
});
x.Map<NotFoundException>(ex => new ProblemDetails
x.Map<NotFoundException>(ex => new ProblemDetailsWithCode
{
Title = "not found exception",
Status = StatusCodes.Status404NotFound,
Detail = ex.Message,
Type = "https://somedomain/not-found-error"
});
x.Map<InternalServerException>(ex => new ProblemDetails
x.Map<InternalServerException>(ex => new ProblemDetailsWithCode
{
Title = "api server exception",
Status = StatusCodes.Status500InternalServerError,
Detail = ex.Message,
Type = "https://somedomain/api-server-error"
});
x.Map<AppException>(ex => new ProblemDetails
x.Map<AppException>(ex => new ProblemDetailsWithCode
{
Title = "application exception",
Status = StatusCodes.Status500InternalServerError,
Status = StatusCodes.Status400BadRequest,
Detail = ex.Message,
Type = "https://somedomain/application-error"
});
x.Map<IdentityException>(ex =>
{
var pd = new ProblemDetails
{
Status = (int)ex.StatusCode,
Title = "identity exception",
Detail = ex.Message,
Type = "https://somedomain/identity-error"
};
return pd;
});
x.MapToStatusCode<ArgumentNullException>(StatusCodes.Status400BadRequest);
x.MapStatusCode = context =>
{
return context.Response.StatusCode switch
{
StatusCodes.Status401Unauthorized => new ProblemDetailsWithCode
{
Status = context.Response.StatusCode,
Title = "identity exception",
Detail = "You are not Authorized",
Type = "https://somedomain/identity-error"
},
_ => new StatusCodeProblemDetails(context.Response.StatusCode)
};
};
});
return services;
}
}
}