Skip to content

Commit b3fdae9

Browse files
authored
feat: Add AddServerFromRequest method (#133)
1 parent 8f93790 commit b3fdae9

File tree

7 files changed

+55
-62
lines changed

7 files changed

+55
-62
lines changed

demo/APIWeaver.Demo.Shared/ServiceCollectionExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ public static IServiceCollection AddDemoServices(this IServiceCollection service
2121
});
2222
});
2323

24+
services.AddHttpContextAccessor();
2425
services.AddOpenApi(options =>
2526
{
2627
options
28+
.AddServerFromRequest()
2729
.AddResponseDescriptions()
2830
.AddRequestBodyParameterName()
2931
.AddDocumentTransformer((document, _) => document.Info.Title = "Book Store API");

docs/Getting-Started.md

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -124,24 +124,15 @@ For Minimal APIs, you can add descriptions with the `ResponseDescription` extens
124124

125125
### Server URL
126126

127-
You can set the server URL for your OpenAPI document:
127+
Set server URLs for your OpenAPI document:
128128

129129
```csharp
130130
builder.Services.AddOpenApi(options =>
131131
{
132-
// Adds a server URL to the OpenAPI document
133-
options.AddServer(new OpenApiServer
134-
{
135-
Url = "https://api.example.com",
136-
Description = "The production API server."
137-
});
138-
139-
// Replaces all existing server URLs with the specified URL
140-
options.AddServer("https://api.example.com", true);
141-
142-
// Add multiple server URLs
132+
// Add server URLs directly to the OpenAPI document
143133
options.AddServers("https://api.example.com", "https://api2.example.com");
144-
// or
134+
135+
// Or add servers using OpenApiServer
145136
options.AddServers(
146137
{
147138
new OpenApiServer
@@ -154,11 +145,17 @@ builder.Services.AddOpenApi(options =>
154145
}
155146
});
156147

148+
// Adds the server URL dynamically using the HttpContext.
149+
options.AddServerFromRequest();
150+
157151
});
158152
```
159153

160154
> [!NOTE]
161-
> The `AddServers` **always** replaces all existing server URLs with the specified URLs.
155+
> Any `AddServers` method replaces all existing server URLs in the OpenAPI document
156+
157+
> [!NOTE]
158+
> To use `AddServerFromRequest`, ensure `IHttpContextAccessor` is registered in the service collection.
162159
163160
### Other extensions
164161

src/APIWeaver.OpenApi/Attributes/ResponseDescriptionAttribute.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace APIWeaver;
99
public sealed class ResponseDescriptionAttribute : Attribute
1010
{
1111
/// <summary>
12-
/// Initializes a new instance of the <see cref="ResponseDescriptionAttribute"/> class.
12+
/// Initializes a new instance of the <see cref="ResponseDescriptionAttribute" /> class.
1313
/// </summary>
1414
/// <param name="description">The description of the response.</param>
1515
/// <param name="statusCode">The HTTP status code of the response. Default is <c>200</c>.</param>
@@ -18,8 +18,8 @@ public ResponseDescriptionAttribute(string description, int statusCode = StatusC
1818
StatusCode = statusCode;
1919
Description = description;
2020
}
21-
21+
2222
internal int StatusCode { get; }
23-
23+
2424
internal string Description { get; }
2525
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using Microsoft.AspNetCore.Http;
2+
3+
namespace APIWeaver;
4+
5+
internal static class HttpRequestExtensions
6+
{
7+
internal static string GetRequestPath(this HttpRequest request) => $"{request.Scheme}://{request.Host}{request.PathBase}";
8+
}

src/APIWeaver.OpenApi/Extensions/OpenApiOptionsExtensions.cs

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Diagnostics.CodeAnalysis;
22
using Microsoft.AspNetCore.Http;
3+
using Microsoft.Extensions.DependencyInjection;
34

45
namespace APIWeaver;
56

@@ -148,74 +149,60 @@ public static OpenApiOptions AddAuthResponse(this OpenApiOptions options)
148149
public static OpenApiOptions AddResponseDescriptions(this OpenApiOptions options) => options.AddOperationTransformer<ResponseDescriptionTransformer>();
149150

150151
/// <summary>
151-
/// Adds the name of the request body parameter to the <see cref="OpenApiOperation"/> by adding the <c>x-name</c> key to the operation.
152+
/// Adds the name of the request body parameter to the <see cref="OpenApiOperation" /> by adding the <c>x-name</c> key to the operation.
152153
/// </summary>
153154
/// <param name="options"><see cref="OpenApiOptions" />.</param>
154155
public static OpenApiOptions AddRequestBodyParameterName(this OpenApiOptions options) => options.AddOperationTransformer<RequestBodyParameterNameTransformer>();
155156

156-
/// <summary>
157-
/// Adds a server to the OpenAPI document with the specified URL.
158-
/// </summary>
159-
/// <param name="options"><see cref="OpenApiOptions" />.</param>
160-
/// <param name="url">The URL of the server to add.</param>
161-
/// <param name="replace">If true, replaces the existing servers; otherwise, adds to the existing servers.</param>
162-
public static OpenApiOptions AddServer(this OpenApiOptions options, [StringSyntax(StringSyntaxAttribute.Uri)] string url, bool replace = false)
163-
{
164-
var server = new OpenApiServer
165-
{
166-
Url = url
167-
};
168-
return options.AddServer(server, replace);
169-
}
170-
171157
/// <summary>
172158
/// Adds a server to the OpenAPI document.
173159
/// </summary>
174160
/// <param name="options"><see cref="OpenApiOptions" />.</param>
175-
/// <param name="server">The <see cref="OpenApiServer"/> instance to add.</param>
176-
/// <param name="replace">If true, replaces the existing servers; otherwise, adds to the existing servers.</param>
177-
public static OpenApiOptions AddServer(this OpenApiOptions options, OpenApiServer server, bool replace = false)
161+
/// <param name="urls">The list of server URLs to add.</param>
162+
/// <remarks>Existing servers are replaced.</remarks>
163+
public static OpenApiOptions AddServer(this OpenApiOptions options, [StringSyntax(StringSyntaxAttribute.Uri)] params IEnumerable<string> urls)
178164
{
179-
options.AddDocumentTransformer((document, _) =>
165+
var servers = urls.Select(url => new OpenApiServer
180166
{
181-
if (replace || document.Servers is null)
182-
{
183-
document.Servers = [];
184-
}
185-
186-
document.Servers.Add(server);
167+
Url = url
187168
});
188-
return options;
169+
return options.AddServer(servers);
189170
}
190171

191172
/// <summary>
192173
/// Adds a server to the OpenAPI document.
193174
/// </summary>
194175
/// <param name="options"><see cref="OpenApiOptions" />.</param>
195-
/// <param name="urls">The list of server URLs to add.</param>
176+
/// <param name="servers">The list of <see cref="OpenApiServer" /> instances to add.</param>
196177
/// <remarks>Existing servers are replaced.</remarks>
197-
public static OpenApiOptions AddServers(this OpenApiOptions options, [StringSyntax(StringSyntaxAttribute.Uri)] params IEnumerable<string> urls)
178+
public static OpenApiOptions AddServer(this OpenApiOptions options, params IEnumerable<OpenApiServer> servers)
198179
{
199-
var servers = urls.Select(u => new OpenApiServer
200-
{
201-
Url = u
202-
});
203-
return options.AddServers(servers);
180+
options.AddDocumentTransformer((document, _) => { document.Servers = [..servers]; });
181+
return options;
204182
}
205183

206184
/// <summary>
207-
/// Adds a server to the OpenAPI document.
185+
/// Adds a server to the OpenAPI document based on the current HTTP request.
208186
/// </summary>
209187
/// <param name="options"><see cref="OpenApiOptions" />.</param>
210-
/// <param name="servers">The list of <see cref="OpenApiServer"/> instances to add.</param>
211-
/// <remarks>Existing servers are replaced.</remarks>
212-
public static OpenApiOptions AddServers(this OpenApiOptions options, params IEnumerable<OpenApiServer> servers)
188+
/// <param name="description">An optional description for the server.</param>
189+
/// <remarks>Ensure that the <see cref="IHttpContextAccessor" /> is registered in the service collection.</remarks>
190+
public static OpenApiOptions AddServerFromRequest(this OpenApiOptions options, string? description = null)
213191
{
214-
options.AddDocumentTransformer((document, _) =>
192+
options.AddDocumentTransformer((document, context) =>
215193
{
216-
document.Servers ??= [];
217-
218-
document.Servers = servers.ToList();
194+
var contextAccessor = context.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
195+
var httpContext = contextAccessor.HttpContext;
196+
if (httpContext is not null)
197+
{
198+
var url = httpContext.Request.GetRequestPath();
199+
var server = new OpenApiServer
200+
{
201+
Url = url,
202+
Description = description
203+
};
204+
document.Servers = [server];
205+
}
219206
});
220207
return options;
221208
}

src/APIWeaver.OpenApi/Transformers/Operation/AdditionalDescriptionTransformer.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ namespace APIWeaver;
77
/// </summary>
88
internal sealed class AdditionalDescriptionTransformer : IOpenApiOperationTransformer
99
{
10-
1110
/// <inheritdoc />
1211
public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken)
1312
{

src/APIWeaver.OpenApi/Transformers/Operation/RequestBodyParameterNameTransformer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33

44
namespace APIWeaver;
55

6-
internal sealed class RequestBodyParameterNameTransformer: IOpenApiOperationTransformer
6+
internal sealed class RequestBodyParameterNameTransformer : IOpenApiOperationTransformer
77
{
88
private const string NameKey = "x-name";
9-
9+
1010
public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken)
1111
{
1212
if (operation.RequestBody is not null)

0 commit comments

Comments
 (0)