mirror of
https://github.com/meysamhadeli/booking-microservices.git
synced 2026-04-26 15:51:08 +08:00
feat: Use built-in problem-details .Net 7 instead of Hellang.Middleware.ProblemDetails
This commit is contained in:
parent
021e218671
commit
7c3e650467
@ -44,7 +44,6 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Hellang.Middleware.ProblemDetails" Version="6.5.1" />
|
||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||
<PackageReference Include="IdGen" Version="3.0.3" />
|
||||
<PackageReference Include="Mapster" Version="7.3.0" />
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
namespace Booking.Booking.Features.CreatingBook.Commands.V1;
|
||||
|
||||
using BuildingBlocks.Web;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@ -18,25 +17,10 @@ public class CreateBookingEndpoint : IMinimalEndpoint
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/booking", CreateBooking)
|
||||
.RequireAuthorization()
|
||||
.WithTags("Booking")
|
||||
.WithName("CreateBooking")
|
||||
.WithMetadata(new SwaggerOperationAttribute("Create Booking", "Create Booking"))
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Booking").Build())
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status200OK,
|
||||
"Booking Created",
|
||||
typeof(CreateBookingResponseDto)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status400BadRequest,
|
||||
"BadRequest",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status401Unauthorized,
|
||||
"UnAuthorized",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.Produces<CreateBookingResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
|
||||
@ -3,7 +3,6 @@ using Booking.Data;
|
||||
using BuildingBlocks.Core;
|
||||
using BuildingBlocks.EventStoreDB;
|
||||
using BuildingBlocks.HealthCheck;
|
||||
using BuildingBlocks.IdsGenerator;
|
||||
using BuildingBlocks.Jwt;
|
||||
using BuildingBlocks.Logging;
|
||||
using BuildingBlocks.Mapster;
|
||||
@ -15,7 +14,6 @@ using BuildingBlocks.Swagger;
|
||||
using BuildingBlocks.Web;
|
||||
using Figgle;
|
||||
using FluentValidation;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -72,7 +70,7 @@ public static class InfrastructureExtensions
|
||||
builder.Services.AddCustomVersioning();
|
||||
builder.Services.AddCustomMediatR();
|
||||
builder.Services.AddValidatorsFromAssembly(typeof(BookingRoot).Assembly);
|
||||
builder.Services.AddCustomProblemDetails();
|
||||
builder.Services.AddProblemDetails();
|
||||
builder.Services.AddCustomMapster(typeof(BookingRoot).Assembly);
|
||||
builder.Services.AddCustomHealthCheck();
|
||||
builder.Services.AddCustomMassTransit(env, typeof(BookingRoot).Assembly);
|
||||
@ -94,7 +92,7 @@ public static class InfrastructureExtensions
|
||||
var env = app.Environment;
|
||||
var appOptions = app.GetOptions<AppOptions>(nameof(AppOptions));
|
||||
|
||||
app.UseProblemDetails();
|
||||
app.UseCustomProblemDetails();
|
||||
app.UseSerilogRequestLogging(options =>
|
||||
{
|
||||
options.EnrichDiagnosticContext = LogEnrichHelper.EnrichFromRequest;
|
||||
|
||||
@ -1,107 +1,106 @@
|
||||
using BuildingBlocks.Exception;
|
||||
using Grpc.Core;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Booking.Extensions.Infrastructure;
|
||||
|
||||
using BuildingBlocks.Exception;
|
||||
using Grpc.Core;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
public static class ProblemDetailsExtensions
|
||||
{
|
||||
public static IServiceCollection AddCustomProblemDetails(this IServiceCollection services)
|
||||
public static WebApplication UseCustomProblemDetails(this WebApplication app)
|
||||
{
|
||||
services.AddProblemDetails(x =>
|
||||
app.UseExceptionHandler(exceptionHandlerApp =>
|
||||
{
|
||||
// Control when an exception is included
|
||||
x.IncludeExceptionDetails = (ctx, _) =>
|
||||
exceptionHandlerApp.Run(async context =>
|
||||
{
|
||||
// Fetch services from HttpContext.RequestServices
|
||||
var env = ctx.RequestServices.GetRequiredService<IHostEnvironment>();
|
||||
return env.IsDevelopment() || env.IsStaging();
|
||||
};
|
||||
x.Map<ConflictException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status409Conflict,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/application-rule-validation-error",
|
||||
});
|
||||
context.Response.ContentType = "application/problem+json";
|
||||
|
||||
// Exception will produce and returns from our FluentValidation RequestValidationBehavior
|
||||
x.Map<ValidationException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = (int)ex.StatusCode,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/input-validation-rules-error",
|
||||
});
|
||||
x.Map<BadRequestException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/bad-request-error",
|
||||
});
|
||||
x.Map<NotFoundException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status404NotFound,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/not-found-error",
|
||||
});
|
||||
x.Map<InternalServerException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status500InternalServerError,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/api-server-error",
|
||||
});
|
||||
x.Map<AppException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/application-error",
|
||||
});
|
||||
|
||||
x.Map<RpcException>(ex => new ProblemDetails
|
||||
{
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Title = ex.GetType().Name,
|
||||
Detail = ex.Status.Detail,
|
||||
Type = "https://somedomain/grpc-error"
|
||||
});
|
||||
|
||||
x.Map<DbUpdateConcurrencyException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status409Conflict,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/db-update-concurrency-error"
|
||||
});
|
||||
|
||||
x.MapToStatusCode<ArgumentNullException>(StatusCodes.Status400BadRequest);
|
||||
|
||||
x.MapStatusCode = context =>
|
||||
{
|
||||
return context.Response.StatusCode switch
|
||||
if (context.RequestServices.GetService<IProblemDetailsService>() is { } problemDetailsService)
|
||||
{
|
||||
StatusCodes.Status401Unauthorized => new ProblemDetailsWithCode
|
||||
{
|
||||
Status = context.Response.StatusCode,
|
||||
Title = "identity exception",
|
||||
Detail = "You are not Authorized",
|
||||
Type = "https://somedomain/identity-error",
|
||||
},
|
||||
var exceptionHandlerFeature = context.Features.Get<IExceptionHandlerFeature>();
|
||||
var exceptionType = exceptionHandlerFeature?.Error;
|
||||
|
||||
_ => new StatusCodeProblemDetails(context.Response.StatusCode)
|
||||
};
|
||||
};
|
||||
if (exceptionType is not null)
|
||||
{
|
||||
(string Detail, string Type, string Title, int StatusCode) details = exceptionType switch
|
||||
{
|
||||
ConflictException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status409Conflict
|
||||
),
|
||||
ValidationException validationException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = (int)validationException.StatusCode
|
||||
),
|
||||
BadRequestException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest
|
||||
),
|
||||
NotFoundException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status404NotFound
|
||||
),
|
||||
AppException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest
|
||||
),
|
||||
DbUpdateConcurrencyException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status409Conflict
|
||||
),
|
||||
RpcException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest
|
||||
),
|
||||
_ =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status500InternalServerError
|
||||
)
|
||||
};
|
||||
|
||||
await problemDetailsService.WriteAsync(new ProblemDetailsContext
|
||||
{
|
||||
HttpContext = context,
|
||||
ProblemDetails =
|
||||
{
|
||||
Title = details.Title,
|
||||
Detail = details.Detail,
|
||||
Type = details.Type,
|
||||
Status = details.StatusCode
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return services;
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,8 +3,6 @@ namespace Flight.Aircrafts.Features.CreatingAircraft.V1;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using Dtos;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@ -21,25 +19,10 @@ public class CreateAircraftEndpoint : IMinimalEndpoint
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight/aircraft", CreateAircraft)
|
||||
.RequireAuthorization()
|
||||
.WithTags("Flight")
|
||||
.WithName("CreateAircraft")
|
||||
.WithMetadata(new SwaggerOperationAttribute("Create Aircraft", "Create Aircraft"))
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status200OK,
|
||||
"Aircraft Created",
|
||||
typeof(CreateAircraftResponseDto)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status400BadRequest,
|
||||
"BadRequest",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status401Unauthorized,
|
||||
"UnAuthorized",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.Produces<CreateAircraftResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
|
||||
@ -3,8 +3,6 @@ namespace Flight.Airports.Features.CreatingAirport.V1;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using Dtos;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@ -21,25 +19,10 @@ public class CreateAirportEndpoint : IMinimalEndpoint
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight/airport", CreateAirport)
|
||||
.RequireAuthorization()
|
||||
.WithTags("Flight")
|
||||
.WithName("CreateAirport")
|
||||
.WithMetadata(new SwaggerOperationAttribute("Create Airport", "Create Airport"))
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status200OK,
|
||||
"Airport Created",
|
||||
typeof(CreateAirportResponseDto)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status400BadRequest,
|
||||
"BadRequest",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status401Unauthorized,
|
||||
"UnAuthorized",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.Produces<CreateAirportResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
|
||||
@ -19,7 +19,6 @@ using Flight.Data;
|
||||
using Flight.Data.Seed;
|
||||
using Flight.GrpcServer.Services;
|
||||
using FluentValidation;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -51,7 +50,7 @@ public static class InfrastructureExtensions
|
||||
});
|
||||
|
||||
builder.Services.AddCustomMediatR();
|
||||
builder.Services.AddCustomProblemDetails();
|
||||
builder.Services.AddProblemDetails();
|
||||
|
||||
var appOptions = builder.Services.GetOptions<AppOptions>(nameof(AppOptions));
|
||||
Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name));
|
||||
@ -99,7 +98,7 @@ public static class InfrastructureExtensions
|
||||
var env = app.Environment;
|
||||
var appOptions = app.GetOptions<AppOptions>(nameof(AppOptions));
|
||||
|
||||
app.UseProblemDetails();
|
||||
app.UseCustomProblemDetails();
|
||||
app.UseSerilogRequestLogging(options =>
|
||||
{
|
||||
options.EnrichDiagnosticContext = LogEnrichHelper.EnrichFromRequest;
|
||||
|
||||
@ -1,98 +1,107 @@
|
||||
using System;
|
||||
using BuildingBlocks.Exception;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Flight.Extensions.Infrastructure;
|
||||
|
||||
using Grpc.Core;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public static class ProblemDetailsExtensions
|
||||
{
|
||||
public static IServiceCollection AddCustomProblemDetails(this IServiceCollection services)
|
||||
public static WebApplication UseCustomProblemDetails(this WebApplication app)
|
||||
{
|
||||
services.AddProblemDetails(x =>
|
||||
app.UseExceptionHandler(exceptionHandlerApp =>
|
||||
{
|
||||
// Control when an exception is included
|
||||
x.IncludeExceptionDetails = (ctx, _) =>
|
||||
exceptionHandlerApp.Run(async context =>
|
||||
{
|
||||
// Fetch services from HttpContext.RequestServices
|
||||
var env = ctx.RequestServices.GetRequiredService<IHostEnvironment>();
|
||||
return env.IsDevelopment() || env.IsStaging();
|
||||
};
|
||||
x.Map<ConflictException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status409Conflict,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/application-rule-validation-error"
|
||||
});
|
||||
context.Response.ContentType = "application/problem+json";
|
||||
|
||||
// Exception will produce and returns from our FluentValidation RequestValidationBehavior
|
||||
x.Map<ValidationException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = (int)ex.StatusCode,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/input-validation-rules-error"
|
||||
});
|
||||
x.Map<BadRequestException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/bad-request-error"
|
||||
});
|
||||
x.Map<NotFoundException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status404NotFound,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/not-found-error"
|
||||
});
|
||||
x.Map<InternalServerException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status500InternalServerError,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/api-server-error"
|
||||
});
|
||||
x.Map<AppException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/application-error"
|
||||
});
|
||||
|
||||
x.Map<DbUpdateConcurrencyException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status409Conflict,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/db-update-concurrency-error"
|
||||
});
|
||||
|
||||
x.MapToStatusCode<ArgumentNullException>(StatusCodes.Status400BadRequest);
|
||||
|
||||
x.MapStatusCode = context =>
|
||||
{
|
||||
return context.Response.StatusCode switch
|
||||
if (context.RequestServices.GetService<IProblemDetailsService>() is { } problemDetailsService)
|
||||
{
|
||||
StatusCodes.Status401Unauthorized => new ProblemDetailsWithCode
|
||||
{
|
||||
Status = context.Response.StatusCode,
|
||||
Title = "identity exception",
|
||||
Detail = "You are not Authorized",
|
||||
Type = "https://somedomain/identity-error"
|
||||
},
|
||||
var exceptionHandlerFeature = context.Features.Get<IExceptionHandlerFeature>();
|
||||
var exceptionType = exceptionHandlerFeature?.Error;
|
||||
|
||||
_ => new StatusCodeProblemDetails(context.Response.StatusCode)
|
||||
};
|
||||
};
|
||||
if (exceptionType is not null)
|
||||
{
|
||||
(string Detail, string Type, string Title, int StatusCode) details = exceptionType switch
|
||||
{
|
||||
ConflictException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status409Conflict
|
||||
),
|
||||
ValidationException validationException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = (int)validationException.StatusCode
|
||||
),
|
||||
BadRequestException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest
|
||||
),
|
||||
NotFoundException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status404NotFound
|
||||
),
|
||||
AppException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest
|
||||
),
|
||||
DbUpdateConcurrencyException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status409Conflict
|
||||
),
|
||||
RpcException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest
|
||||
),
|
||||
_ =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status500InternalServerError
|
||||
)
|
||||
};
|
||||
|
||||
await problemDetailsService.WriteAsync(new ProblemDetailsContext
|
||||
{
|
||||
HttpContext = context,
|
||||
ProblemDetails =
|
||||
{
|
||||
Title = details.Title,
|
||||
Detail = details.Detail,
|
||||
Type = details.Type,
|
||||
Status = details.StatusCode
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return services;
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@ -24,25 +23,10 @@ public class CreateFlightEndpoint : IMinimalEndpoint
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight", CreateFlight)
|
||||
.RequireAuthorization()
|
||||
.WithTags("Flight")
|
||||
.WithName("CreateFlight")
|
||||
.WithMetadata(new SwaggerOperationAttribute("Create Flight", "Create Flight"))
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status201Created,
|
||||
"Flight Created",
|
||||
typeof(CreateFlightResponseDto)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status400BadRequest,
|
||||
"BadRequest",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status401Unauthorized,
|
||||
"UnAuthorized",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.Produces<CreateFlightResponseDto>(StatusCodes.Status201Created)
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
|
||||
@ -3,8 +3,6 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using Flight.Flights.Dtos;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@ -17,24 +15,10 @@ public class DeleteFlightEndpoint : IMinimalEndpoint
|
||||
{
|
||||
builder.MapDelete($"{EndpointConfig.BaseApiPath}/flight/{{id}}", DeleteFlight)
|
||||
.RequireAuthorization()
|
||||
.WithTags("Flight")
|
||||
.WithName("DeleteFlight")
|
||||
.WithMetadata(new SwaggerOperationAttribute("Delete Flight", "Delete Flight"))
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status204NoContent,
|
||||
"Flight Deleted"))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status400BadRequest,
|
||||
"BadRequest",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status401Unauthorized,
|
||||
"UnAuthorized",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
|
||||
@ -5,7 +5,6 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using Dtos;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@ -20,25 +19,10 @@ public class GetAvailableFlightsEndpoint : IMinimalEndpoint
|
||||
{
|
||||
builder.MapGet($"{EndpointConfig.BaseApiPath}/flight/get-available-flights", GetAvailableFlights)
|
||||
.RequireAuthorization()
|
||||
.WithTags("Flight")
|
||||
.WithName("GetAvailableFlights")
|
||||
.WithMetadata(new SwaggerOperationAttribute("Get Available Flights", "Get Available Flights"))
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status200OK,
|
||||
"GetAvailableFlights",
|
||||
typeof(GetAvailableFlightsResult)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status400BadRequest,
|
||||
"BadRequest",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status401Unauthorized,
|
||||
"UnAuthorized",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.Produces<GetAvailableFlightsResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
|
||||
@ -4,7 +4,6 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using Dtos;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@ -19,28 +18,10 @@ public class GetFlightByIdEndpoint : IMinimalEndpoint
|
||||
{
|
||||
builder.MapGet($"{EndpointConfig.BaseApiPath}/flight/{{id}}", GetById)
|
||||
.RequireAuthorization()
|
||||
.WithTags("Flight")
|
||||
.WithName("GetFlightById")
|
||||
.WithMetadata(new SwaggerOperationAttribute("Get Flight By Id", "Get Flight By Id"))
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<FlightDto>()
|
||||
.Produces(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status400BadRequest)
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status200OK,
|
||||
"GetFlightById",
|
||||
typeof(GetFlightByIdResponseDto)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status400BadRequest,
|
||||
"BadRequest",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status401Unauthorized,
|
||||
"UnAuthorized",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.Produces<GetFlightByIdResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
|
||||
@ -4,8 +4,6 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using Dtos;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@ -22,27 +20,10 @@ public class UpdateFlightEndpoint : IMinimalEndpoint
|
||||
{
|
||||
builder.MapPut($"{EndpointConfig.BaseApiPath}/flight", UpdateFlight)
|
||||
.RequireAuthorization()
|
||||
.WithTags("Flight")
|
||||
.WithName("UpdateFlight")
|
||||
.WithMetadata(new SwaggerOperationAttribute("Update Flight", "Update Flight"))
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.Produces<FlightDto>()
|
||||
.Produces(StatusCodes.Status204NoContent)
|
||||
.Produces(StatusCodes.Status400BadRequest)
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status204NoContent,
|
||||
"Flight Updated"))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status400BadRequest,
|
||||
"BadRequest",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status401Unauthorized,
|
||||
"UnAuthorized",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
|
||||
@ -3,8 +3,6 @@ namespace Flight.Seats.Features.CreatingSeat.V1;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using Flight.Seats.Dtos;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@ -21,25 +19,10 @@ public class CreateSeatEndpoint : IMinimalEndpoint
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight/seat", CreateSeat)
|
||||
.RequireAuthorization()
|
||||
.WithTags("Flight")
|
||||
.WithName("CreateSeat")
|
||||
.WithMetadata(new SwaggerOperationAttribute("Create Seat", "Create Seat"))
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status200OK,
|
||||
"Seat Created",
|
||||
typeof(CreateSeatResponseDto)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status400BadRequest,
|
||||
"BadRequest",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status401Unauthorized,
|
||||
"UnAuthorized",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.Produces<CreateSeatResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
|
||||
@ -4,8 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using Flight.Seats.Dtos;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using Dtos;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@ -20,25 +19,10 @@ public class GetAvailableSeatsEndpoint : IMinimalEndpoint
|
||||
{
|
||||
builder.MapGet($"{EndpointConfig.BaseApiPath}/flight/get-available-seats/{{id}}", GetAvailableSeats)
|
||||
.RequireAuthorization()
|
||||
.WithTags("Flight")
|
||||
.WithName("GetAvailableSeats")
|
||||
.WithMetadata(new SwaggerOperationAttribute("Get Available Seats", "Get Available Seats"))
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status200OK,
|
||||
"GetAvailableSeats",
|
||||
typeof(GetAvailableSeatsResponseDto)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status400BadRequest,
|
||||
"BadRequest",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status401Unauthorized,
|
||||
"UnAuthorized",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.Produces<GetAvailableSeatsResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
|
||||
@ -3,8 +3,6 @@ namespace Flight.Seats.Features.ReservingSeat.Commands.V1;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using Flight.Seats.Dtos;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@ -21,25 +19,10 @@ public class ReserveSeatEndpoint : IMinimalEndpoint
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/flight/reserve-seat", ReserveSeat)
|
||||
.RequireAuthorization()
|
||||
.WithTags("Flight")
|
||||
.WithName("ReserveSeat")
|
||||
.WithMetadata(new SwaggerOperationAttribute("Reserve Seat", "Reserve Seat"))
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status200OK,
|
||||
"ReserveSeat",
|
||||
typeof(ReserveSeatResponseDto)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status400BadRequest,
|
||||
"BadRequest",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status401Unauthorized,
|
||||
"UnAuthorized",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.Produces<ReserveSeatResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
|
||||
@ -3,7 +3,6 @@ using System.Threading.RateLimiting;
|
||||
using BuildingBlocks.Core;
|
||||
using BuildingBlocks.EFCore;
|
||||
using BuildingBlocks.HealthCheck;
|
||||
using BuildingBlocks.IdsGenerator;
|
||||
using BuildingBlocks.Logging;
|
||||
using BuildingBlocks.Mapster;
|
||||
using BuildingBlocks.MassTransit;
|
||||
@ -13,7 +12,6 @@ using BuildingBlocks.Swagger;
|
||||
using BuildingBlocks.Web;
|
||||
using Figgle;
|
||||
using FluentValidation;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using Identity.Data;
|
||||
using Identity.Data.Seed;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@ -72,7 +70,7 @@ public static class InfrastructureExtensions
|
||||
builder.Services.AddCustomVersioning();
|
||||
builder.Services.AddCustomMediatR();
|
||||
builder.Services.AddValidatorsFromAssembly(typeof(IdentityRoot).Assembly);
|
||||
builder.Services.AddCustomProblemDetails();
|
||||
builder.Services.AddProblemDetails();
|
||||
builder.Services.AddCustomMapster(typeof(IdentityRoot).Assembly);
|
||||
builder.Services.AddCustomHealthCheck();
|
||||
|
||||
@ -98,7 +96,7 @@ public static class InfrastructureExtensions
|
||||
|
||||
app.UseForwardedHeaders();
|
||||
|
||||
app.UseProblemDetails();
|
||||
app.UseCustomProblemDetails();
|
||||
app.UseSerilogRequestLogging(options =>
|
||||
{
|
||||
options.EnrichDiagnosticContext = LogEnrichHelper.EnrichFromRequest;
|
||||
@ -107,7 +105,6 @@ public static class InfrastructureExtensions
|
||||
app.UseMigration<IdentityContext>(env);
|
||||
app.UseCorrelationId();
|
||||
app.UseHttpMetrics();
|
||||
app.UseProblemDetails();
|
||||
app.UseCustomHealthCheck();
|
||||
app.UseIdentityServer();
|
||||
app.MapMetrics();
|
||||
|
||||
@ -1,98 +1,106 @@
|
||||
using System;
|
||||
using BuildingBlocks.Exception;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Identity.Extensions.Infrastructure;
|
||||
|
||||
using BuildingBlocks.Exception;
|
||||
using Grpc.Core;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
public static class ProblemDetailsExtensions
|
||||
{
|
||||
public static IServiceCollection AddCustomProblemDetails(this IServiceCollection services)
|
||||
public static WebApplication UseCustomProblemDetails(this WebApplication app)
|
||||
{
|
||||
services.AddProblemDetails(x =>
|
||||
app.UseExceptionHandler(exceptionHandlerApp =>
|
||||
{
|
||||
// Control when an exception is included
|
||||
x.IncludeExceptionDetails = (ctx, _) =>
|
||||
exceptionHandlerApp.Run(async context =>
|
||||
{
|
||||
// Fetch services from HttpContext.RequestServices
|
||||
var env = ctx.RequestServices.GetRequiredService<IHostEnvironment>();
|
||||
return env.IsDevelopment() || env.IsStaging();
|
||||
};
|
||||
x.Map<ConflictException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status409Conflict,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/application-rule-validation-error"
|
||||
});
|
||||
context.Response.ContentType = "application/problem+json";
|
||||
|
||||
// Exception will produce and returns from our FluentValidation RequestValidationBehavior
|
||||
x.Map<ValidationException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = (int)ex.StatusCode,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/input-validation-rules-error"
|
||||
});
|
||||
x.Map<BadRequestException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/bad-request-error"
|
||||
});
|
||||
x.Map<NotFoundException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status404NotFound,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/not-found-error"
|
||||
});
|
||||
x.Map<InternalServerException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status500InternalServerError,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/api-server-error"
|
||||
});
|
||||
x.Map<AppException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/application-error"
|
||||
});
|
||||
|
||||
x.Map<DbUpdateConcurrencyException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status409Conflict,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/db-update-concurrency-error"
|
||||
});
|
||||
|
||||
x.MapToStatusCode<ArgumentNullException>(StatusCodes.Status400BadRequest);
|
||||
|
||||
x.MapStatusCode = context =>
|
||||
{
|
||||
return context.Response.StatusCode switch
|
||||
if (context.RequestServices.GetService<IProblemDetailsService>() is { } problemDetailsService)
|
||||
{
|
||||
StatusCodes.Status401Unauthorized => new ProblemDetailsWithCode
|
||||
{
|
||||
Status = context.Response.StatusCode,
|
||||
Title = "identity exception",
|
||||
Detail = "You are not Authorized",
|
||||
Type = "https://somedomain/identity-error"
|
||||
},
|
||||
var exceptionHandlerFeature = context.Features.Get<IExceptionHandlerFeature>();
|
||||
var exceptionType = exceptionHandlerFeature?.Error;
|
||||
|
||||
_ => new StatusCodeProblemDetails(context.Response.StatusCode)
|
||||
};
|
||||
};
|
||||
if (exceptionType is not null)
|
||||
{
|
||||
(string Detail, string Type, string Title, int StatusCode) details = exceptionType switch
|
||||
{
|
||||
ConflictException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status409Conflict
|
||||
),
|
||||
ValidationException validationException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = (int)validationException.StatusCode
|
||||
),
|
||||
BadRequestException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest
|
||||
),
|
||||
NotFoundException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status404NotFound
|
||||
),
|
||||
AppException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest
|
||||
),
|
||||
DbUpdateConcurrencyException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status409Conflict
|
||||
),
|
||||
RpcException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest
|
||||
),
|
||||
_ =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status500InternalServerError
|
||||
)
|
||||
};
|
||||
|
||||
await problemDetailsService.WriteAsync(new ProblemDetailsContext
|
||||
{
|
||||
HttpContext = context,
|
||||
ProblemDetails =
|
||||
{
|
||||
Title = details.Title,
|
||||
Detail = details.Detail,
|
||||
Type = details.Type,
|
||||
Status = details.StatusCode
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return services;
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ namespace Identity.Identity.Features.RegisteringNewUser.V1;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BuildingBlocks.Web;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@ -21,20 +20,10 @@ public class RegisterNewUserEndpoint : IMinimalEndpoint
|
||||
public IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/identity/register-user", RegisterNewUser)
|
||||
.WithTags("Identity")
|
||||
.WithName("RegisterUser")
|
||||
.WithMetadata(new SwaggerOperationAttribute("Register User", "Register User"))
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Identity").Build())
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status200OK,
|
||||
"User Registered",
|
||||
typeof(RegisterNewUserResponseDto)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status400BadRequest,
|
||||
"BadRequest",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.Produces<RegisterNewUserResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
|
||||
@ -3,7 +3,6 @@ using BuildingBlocks.Core;
|
||||
using BuildingBlocks.EFCore;
|
||||
using BuildingBlocks.Exception;
|
||||
using BuildingBlocks.HealthCheck;
|
||||
using BuildingBlocks.IdsGenerator;
|
||||
using BuildingBlocks.Jwt;
|
||||
using BuildingBlocks.Logging;
|
||||
using BuildingBlocks.Mapster;
|
||||
@ -15,7 +14,6 @@ using BuildingBlocks.Swagger;
|
||||
using BuildingBlocks.Web;
|
||||
using Figgle;
|
||||
using FluentValidation;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -73,7 +71,7 @@ public static class InfrastructureExtensions
|
||||
builder.Services.AddCustomVersioning();
|
||||
builder.Services.AddCustomMediatR();
|
||||
builder.Services.AddValidatorsFromAssembly(typeof(PassengerRoot).Assembly);
|
||||
builder.Services.AddCustomProblemDetails();
|
||||
builder.Services.AddProblemDetails();
|
||||
builder.Services.AddCustomMapster(typeof(PassengerRoot).Assembly);
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
builder.Services.AddCustomHealthCheck();
|
||||
@ -93,7 +91,7 @@ public static class InfrastructureExtensions
|
||||
var env = app.Environment;
|
||||
var appOptions = app.GetOptions<AppOptions>(nameof(AppOptions));
|
||||
|
||||
app.UseProblemDetails();
|
||||
app.UseCustomProblemDetails();
|
||||
app.UseSerilogRequestLogging(options =>
|
||||
{
|
||||
options.EnrichDiagnosticContext = LogEnrichHelper.EnrichFromRequest;
|
||||
|
||||
@ -1,97 +1,106 @@
|
||||
using BuildingBlocks.Exception;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Passenger.Extensions.Infrastructure;
|
||||
|
||||
using BuildingBlocks.Exception;
|
||||
using Grpc.Core;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
public static class ProblemDetailsExtensions
|
||||
{
|
||||
public static IServiceCollection AddCustomProblemDetails(this IServiceCollection services)
|
||||
public static WebApplication UseCustomProblemDetails(this WebApplication app)
|
||||
{
|
||||
services.AddProblemDetails(x =>
|
||||
app.UseExceptionHandler(exceptionHandlerApp =>
|
||||
{
|
||||
// Control when an exception is included
|
||||
x.IncludeExceptionDetails = (ctx, _) =>
|
||||
exceptionHandlerApp.Run(async context =>
|
||||
{
|
||||
// Fetch services from HttpContext.RequestServices
|
||||
var env = ctx.RequestServices.GetRequiredService<IHostEnvironment>();
|
||||
return env.IsDevelopment() || env.IsStaging();
|
||||
};
|
||||
x.Map<ConflictException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status409Conflict,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/application-rule-validation-error"
|
||||
});
|
||||
context.Response.ContentType = "application/problem+json";
|
||||
|
||||
// Exception will produce and returns from our FluentValidation RequestValidationBehavior
|
||||
x.Map<ValidationException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = (int)ex.StatusCode,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/input-validation-rules-error"
|
||||
});
|
||||
x.Map<BadRequestException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/bad-request-error"
|
||||
});
|
||||
x.Map<NotFoundException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status404NotFound,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/not-found-error"
|
||||
});
|
||||
x.Map<InternalServerException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status500InternalServerError,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/api-server-error"
|
||||
});
|
||||
x.Map<AppException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status400BadRequest,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/application-error"
|
||||
});
|
||||
|
||||
x.Map<DbUpdateConcurrencyException>(ex => new ProblemDetailsWithCode
|
||||
{
|
||||
Title = ex.GetType().Name,
|
||||
Status = StatusCodes.Status409Conflict,
|
||||
Detail = ex.Message,
|
||||
Type = "https://somedomain/db-update-concurrency-error"
|
||||
});
|
||||
|
||||
x.MapToStatusCode<ArgumentNullException>(StatusCodes.Status400BadRequest);
|
||||
|
||||
x.MapStatusCode = context =>
|
||||
{
|
||||
return context.Response.StatusCode switch
|
||||
if (context.RequestServices.GetService<IProblemDetailsService>() is { } problemDetailsService)
|
||||
{
|
||||
StatusCodes.Status401Unauthorized => new ProblemDetailsWithCode
|
||||
{
|
||||
Status = context.Response.StatusCode,
|
||||
Title = "identity exception",
|
||||
Detail = "You are not Authorized",
|
||||
Type = "https://somedomain/identity-error"
|
||||
},
|
||||
var exceptionHandlerFeature = context.Features.Get<IExceptionHandlerFeature>();
|
||||
var exceptionType = exceptionHandlerFeature?.Error;
|
||||
|
||||
_ => new StatusCodeProblemDetails(context.Response.StatusCode)
|
||||
};
|
||||
};
|
||||
if (exceptionType is not null)
|
||||
{
|
||||
(string Detail, string Type, string Title, int StatusCode) details = exceptionType switch
|
||||
{
|
||||
ConflictException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status409Conflict
|
||||
),
|
||||
ValidationException validationException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = (int)validationException.StatusCode
|
||||
),
|
||||
BadRequestException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest
|
||||
),
|
||||
NotFoundException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status404NotFound
|
||||
),
|
||||
AppException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest
|
||||
),
|
||||
DbUpdateConcurrencyException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status409Conflict
|
||||
),
|
||||
RpcException =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest
|
||||
),
|
||||
_ =>
|
||||
(
|
||||
exceptionType.Message,
|
||||
exceptionType.GetType().ToString(),
|
||||
exceptionType.GetType().Name,
|
||||
context.Response.StatusCode = StatusCodes.Status500InternalServerError
|
||||
)
|
||||
};
|
||||
|
||||
await problemDetailsService.WriteAsync(new ProblemDetailsContext
|
||||
{
|
||||
HttpContext = context,
|
||||
ProblemDetails =
|
||||
{
|
||||
Title = details.Title,
|
||||
Detail = details.Detail,
|
||||
Type = details.Type,
|
||||
Status = details.StatusCode
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return services;
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
namespace Passenger.Passengers.Features.CompletingRegisterPassenger.V1;
|
||||
|
||||
using BuildingBlocks.Web;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using MapsterMapper;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@ -19,25 +18,10 @@ public class CompleteRegisterPassengerEndpoint : IMinimalEndpoint
|
||||
{
|
||||
builder.MapPost($"{EndpointConfig.BaseApiPath}/passenger/complete-registration", CompleteRegisterPassenger)
|
||||
.RequireAuthorization()
|
||||
.WithTags("Passenger")
|
||||
.WithName("CompleteRegisterPassenger")
|
||||
.WithMetadata(new SwaggerOperationAttribute("Complete Register Passenger", "Complete Register Passenger"))
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Passenger").Build())
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status200OK,
|
||||
"Register Passenger Completed",
|
||||
typeof(CompleteRegisterPassengerResponseDto)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status400BadRequest,
|
||||
"BadRequest",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status401Unauthorized,
|
||||
"UnAuthorized",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.Produces<CompleteRegisterPassengerResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
namespace Passenger.Passengers.Features.GettingPassengerById.Queries.V1;
|
||||
|
||||
using BuildingBlocks.Web;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Passenger.Passengers.Dtos;
|
||||
using Dtos;
|
||||
using Swashbuckle.AspNetCore.Annotations;
|
||||
|
||||
public record GetPassengerByIdResponseDto(PassengerDto PassengerDto);
|
||||
@ -17,25 +16,10 @@ public class GetPassengerByIdEndpoint : IMinimalEndpoint
|
||||
{
|
||||
builder.MapGet($"{EndpointConfig.BaseApiPath}/passenger/{{id}}", GetById)
|
||||
.RequireAuthorization()
|
||||
.WithTags("Passenger")
|
||||
.WithName("GetPassengerById")
|
||||
.WithMetadata(new SwaggerOperationAttribute("Get Passenger By Id", "Get Passenger By Id"))
|
||||
.WithApiVersionSet(builder.NewApiVersionSet("Passenger").Build())
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status200OK,
|
||||
"GetPassengerById",
|
||||
typeof(GetPassengerByIdResponseDto)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status400BadRequest,
|
||||
"BadRequest",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.WithMetadata(
|
||||
new SwaggerResponseAttribute(
|
||||
StatusCodes.Status401Unauthorized,
|
||||
"UnAuthorized",
|
||||
typeof(StatusCodeProblemDetails)))
|
||||
.Produces<GetPassengerByIdResponseDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.HasApiVersion(1.0);
|
||||
|
||||
return builder;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user