-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(webapi): Add ETag to response headers #1645
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using Digdir.Domain.Dialogporten.WebApi.Common; | ||
using FastEndpoints; | ||
|
||
namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Headers; | ||
|
||
public static class HttpResponseHeaderExamples | ||
{ | ||
public static ResponseHeader NewDialogETagHeader(int statusCode) | ||
=> new(statusCode, Constants.ETag) | ||
{ | ||
Description = "The new UUID ETag of the dialog", | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.Dialogs.Patch; | ||
|
||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] | ||
public sealed class ProducesResponseHeaderAttribute : Attribute | ||
{ | ||
public ProducesResponseHeaderAttribute(int statusCode, string headerName, string description) | ||
{ | ||
HeaderName = headerName; | ||
StatusCode = statusCode; | ||
Description = description; | ||
} | ||
|
||
public string HeaderName { get; } | ||
public int StatusCode { get; } | ||
public string Description { get; } | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,29 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
using System.Globalization; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
using System.Reflection; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
using NSwag; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
using NSwag.Generation.Processors; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
using NSwag.Generation.Processors.Contexts; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.Dialogs.Patch; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public sealed class ProducesResponseHeaderOperationProcessor : IOperationProcessor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public bool Process(OperationProcessorContext context) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var headerAttribute = context.MethodInfo.GetCustomAttribute<ProducesResponseHeaderAttribute>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (headerAttribute == null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var statusCode = headerAttribute.StatusCode.ToString(CultureInfo.InvariantCulture); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var response = context.OperationDescription.Operation.Responses[statusCode]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var header = new OpenApiHeader | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Description = headerAttribute.Description, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
response.Headers.Add(headerAttribute.HeaderName, header); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+11
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Support multiple header attributes and improve error handling The processor needs to handle multiple attributes since public bool Process(OperationProcessorContext context)
{
- var headerAttribute = context.MethodInfo.GetCustomAttribute<ProducesResponseHeaderAttribute>();
- if (headerAttribute == null)
+ var headerAttributes = context.MethodInfo.GetCustomAttributes<ProducesResponseHeaderAttribute>();
+ if (!headerAttributes.Any())
{
return true;
}
- var statusCode = headerAttribute.StatusCode.ToString(CultureInfo.InvariantCulture);
- var response = context.OperationDescription.Operation.Responses[statusCode];
- var header = new OpenApiHeader
+ foreach (var headerAttribute in headerAttributes)
{
- Description = headerAttribute.Description,
- };
+ var statusCode = headerAttribute.StatusCode.ToString(CultureInfo.InvariantCulture);
+
+ if (!context.OperationDescription.Operation.Responses.TryGetValue(statusCode, out var response))
+ {
+ throw new InvalidOperationException(
+ $"Response with status code {statusCode} not found for header {headerAttribute.HeaderName}");
+ }
+
+ var header = new OpenApiHeader
+ {
+ Description = headerAttribute.Description,
+ };
- response.Headers.Add(headerAttribute.HeaderName, header);
+ response.Headers.Add(headerAttribute.HeaderName, header);
+ }
return true;
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add strong ETag validator prefix.
Per RFC 7232, ETags should be prefixed with a strong validator (double quotes) or weak validator (W/).
📝 Committable suggestion