Skip to content

Commit aa193e2

Browse files
committed
limits
1 parent f1096a4 commit aa193e2

File tree

7 files changed

+65
-32
lines changed

7 files changed

+65
-32
lines changed

src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/ServiceOwnerOnBehalfOfPersonMiddleware.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public Task InvokeAsync(HttpContext context)
3737
if (!NorwegianPersonIdentifier.TryParse(endUserIdQuery.First(), out var endUserId))
3838
{
3939
context.Response.StatusCode = StatusCodes.Status400BadRequest;
40-
context.Response.WriteAsJsonAsync(context.ResponseBuilder(
40+
context.Response.WriteAsJsonAsync(context.GetResponseOrDefault(
4141
context.Response.StatusCode,
4242
[
4343
new("EndUserId",

src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/UserTypeValidationMiddleware.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public async Task InvokeAsync(HttpContext context)
2121
if (userType == UserIdType.Unknown)
2222
{
2323
context.Response.StatusCode = StatusCodes.Status403Forbidden;
24-
await context.Response.WriteAsJsonAsync(context.ResponseBuilder(
24+
await context.Response.WriteAsJsonAsync(context.GetResponseOrDefault(
2525
context.Response.StatusCode,
2626
[
2727
new("Type",

src/Digdir.Domain.Dialogporten.WebApi/Common/Constants.cs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ internal static class Constants
55
internal const string IfMatch = "If-Match";
66
internal const string Authorization = "Authorization";
77
internal const string CurrentTokenIssuer = "CurrentIssuer";
8+
internal const int MaxRequestBodySize = 100_000;
89

910
internal static class SwaggerSummary
1011
{

src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/ErrorResponseBuilderExtensions.cs

+30-13
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,41 @@ namespace Digdir.Domain.Dialogporten.WebApi.Common.Extensions;
66

77
internal static class ErrorResponseBuilderExtensions
88
{
9-
public static object ResponseBuilder(this HttpContext ctx, int statusCode, List<ValidationFailure>? failures = null) =>
10-
ResponseBuilder(failures ?? [], ctx, statusCode);
9+
public static ProblemDetails DefaultResponse(this HttpContext ctx, int? statusCode = null) => new()
10+
{
11+
Title = "An error occurred while processing the request.",
12+
Detail = "Something went wrong during the request.",
13+
Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1",
14+
Status = statusCode ?? ctx.Response.StatusCode,
15+
Instance = ctx.Request.Path,
16+
Extensions = { { "traceId", Activity.Current?.Id ?? ctx.TraceIdentifier } }
17+
};
18+
19+
public static ProblemDetails GetResponseOrDefault(this HttpContext ctx, int statusCode,
20+
List<ValidationFailure>? failures = null) =>
21+
ctx.ResponseBuilder(failures, statusCode) ?? ctx.DefaultResponse(statusCode);
1122

1223
public static object ResponseBuilder(List<ValidationFailure> failures, HttpContext ctx, int statusCode)
24+
=> ctx.ResponseBuilder(failures, statusCode) ?? ctx.DefaultResponse(statusCode);
25+
26+
public static ProblemDetails? ResponseBuilder(this HttpContext ctx, List<ValidationFailure>? failures = null, int? statusCode = null)
1327
{
14-
var errors = failures
28+
var errors = failures?
1529
.GroupBy(f => f.PropertyName)
16-
.ToDictionary(x => x.Key, x => x.Select(m => m.ErrorMessage).ToArray());
30+
.ToDictionary(x => x.Key, x => x.Select(m => m.ErrorMessage).ToArray())
31+
?? [];
32+
33+
statusCode ??= ctx.Response.StatusCode;
1734
return statusCode switch
1835
{
36+
StatusCodes.Status413PayloadTooLarge => new ProblemDetails
37+
{
38+
Title = $"Payload too large. The maximum allowed size is {Constants.MaxRequestBodySize} bytes.",
39+
Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.11",
40+
Status = statusCode,
41+
Instance = ctx.Request.Path,
42+
Extensions = { { "traceId", Activity.Current?.Id ?? ctx.TraceIdentifier } }
43+
},
1944
StatusCodes.Status400BadRequest => new ValidationProblemDetails(errors)
2045
{
2146
Title = "One or more validation errors occurred.",
@@ -73,15 +98,7 @@ public static object ResponseBuilder(List<ValidationFailure> failures, HttpConte
7398
Instance = ctx.Request.Path,
7499
Extensions = { { "traceId", Activity.Current?.Id ?? ctx.TraceIdentifier } }
75100
},
76-
_ => new ProblemDetails
77-
{
78-
Title = "An error occurred while processing the request.",
79-
Detail = "Something went wrong during the request.",
80-
Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1",
81-
Status = ctx.Response.StatusCode,
82-
Instance = ctx.Request.Path,
83-
Extensions = { { "traceId", Activity.Current?.Id ?? ctx.TraceIdentifier } }
84-
}
101+
_ => null
85102
};
86103
}
87104
}

src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/GlobalExceptionHandler.cs

+19-9
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,26 @@ internal sealed class GlobalExceptionHandler : IExceptionHandler
99
public async ValueTask<bool> TryHandleAsync(HttpContext ctx, Exception exception,
1010
CancellationToken cancellationToken)
1111
{
12-
var http = $"{ctx.Request.Scheme}: {ctx.Request.Method} {ctx.Request.Path}";
13-
var type = exception.GetType().Name;
14-
var error = exception.Message;
15-
var logger = ctx.Resolve<ILogger<GlobalExceptionHandler>>();
16-
logger.LogError(exception, "{@Http}{@Type}{@Reason}", http, type, error);
17-
ctx.Response.StatusCode = exception is IUpstreamServiceError
18-
? StatusCodes.Status502BadGateway
19-
: StatusCodes.Status500InternalServerError;
12+
ctx.Response.StatusCode = exception switch
13+
{
14+
BadHttpRequestException badHttpRequestException => badHttpRequestException.StatusCode,
15+
IUpstreamServiceError => StatusCodes.Status502BadGateway,
16+
_ => StatusCodes.Status500InternalServerError
17+
};
18+
2019
ctx.Response.ContentType = "application/problem+json";
21-
await ctx.Response.WriteAsJsonAsync(ctx.ResponseBuilder(ctx.Response.StatusCode), cancellationToken);
20+
var response = ctx.ResponseBuilder();
21+
22+
if (ctx.Response.StatusCode >= 500 || response is null)
23+
{
24+
var http = $"{ctx.Request.Scheme}: {ctx.Request.Method} {ctx.Request.Path}";
25+
var type = exception.GetType().Name;
26+
var error = exception.Message;
27+
var logger = ctx.Resolve<ILogger<GlobalExceptionHandler>>();
28+
logger.LogError(exception, "{@Http}{@Type}{@Reason}", http, type, error);
29+
}
30+
31+
await ctx.Response.WriteAsJsonAsync(response ?? ctx.DefaultResponse(), cancellationToken);
2232
return true;
2333
}
2434
}

src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/PatchDialogsController.cs

+8-8
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ public async Task<IActionResult> Patch(
7070
if (!dialogQueryResult.TryPickT0(out var dialog, out var errors))
7171
{
7272
return errors.Match<IActionResult>(
73-
notFound => NotFound(HttpContext.ResponseBuilder(StatusCodes.Status404NotFound,
73+
notFound => NotFound(HttpContext.GetResponseOrDefault(StatusCodes.Status404NotFound,
7474
notFound.ToValidationResults())),
7575
validationFailed =>
76-
BadRequest(HttpContext.ResponseBuilder(StatusCodes.Status400BadRequest,
76+
BadRequest(HttpContext.GetResponseOrDefault(StatusCodes.Status400BadRequest,
7777
validationFailed.Errors.ToList())));
7878
}
7979

@@ -88,12 +88,12 @@ public async Task<IActionResult> Patch(
8888
var result = await _sender.Send(command, ct);
8989
return result.Match(
9090
success => (IActionResult)NoContent(),
91-
notFound => NotFound(HttpContext.ResponseBuilder(StatusCodes.Status404NotFound, notFound.ToValidationResults())),
92-
badRequest => BadRequest(HttpContext.ResponseBuilder(StatusCodes.Status400BadRequest, badRequest.ToValidationResults())),
93-
validationFailed => BadRequest(HttpContext.ResponseBuilder(StatusCodes.Status400BadRequest, validationFailed.Errors.ToList())),
94-
forbidden => new ObjectResult(HttpContext.ResponseBuilder(StatusCodes.Status403Forbidden, forbidden.ToValidationResults())),
95-
domainError => UnprocessableEntity(HttpContext.ResponseBuilder(StatusCodes.Status422UnprocessableEntity, domainError.ToValidationResults())),
96-
concurrencyError => new ObjectResult(HttpContext.ResponseBuilder(StatusCodes.Status412PreconditionFailed)) { StatusCode = StatusCodes.Status412PreconditionFailed }
91+
notFound => NotFound(HttpContext.GetResponseOrDefault(StatusCodes.Status404NotFound, notFound.ToValidationResults())),
92+
badRequest => BadRequest(HttpContext.GetResponseOrDefault(StatusCodes.Status400BadRequest, badRequest.ToValidationResults())),
93+
validationFailed => BadRequest(HttpContext.GetResponseOrDefault(StatusCodes.Status400BadRequest, validationFailed.Errors.ToList())),
94+
forbidden => new ObjectResult(HttpContext.GetResponseOrDefault(StatusCodes.Status403Forbidden, forbidden.ToValidationResults())),
95+
domainError => UnprocessableEntity(HttpContext.GetResponseOrDefault(StatusCodes.Status422UnprocessableEntity, domainError.ToValidationResults())),
96+
concurrencyError => new ObjectResult(HttpContext.GetResponseOrDefault(StatusCodes.Status412PreconditionFailed)) { StatusCode = StatusCodes.Status412PreconditionFailed }
9797
);
9898
}
9999
}

src/Digdir.Domain.Dialogporten.WebApi/Program.cs

+5
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ static void BuildAndRun(string[] args, TelemetryConfiguration telemetryConfigura
5252
{
5353
var builder = WebApplication.CreateBuilder(args);
5454

55+
builder.WebHost.ConfigureKestrel(kestrelOptions =>
56+
{
57+
kestrelOptions.Limits.MaxRequestBodySize = Constants.MaxRequestBodySize;
58+
});
59+
5560
builder.Host.UseSerilog((context, services, configuration) => configuration
5661
.MinimumLevel.Warning()
5762
.ReadFrom.Configuration(context.Configuration)

0 commit comments

Comments
 (0)