diff --git a/Directory.Packages.props b/Directory.Packages.props
index 1540677b..af6c6dfe 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -6,8 +6,8 @@
-
-
+
+
diff --git a/src/Facility.Core/Http/HttpClientService.cs b/src/Facility.Core/Http/HttpClientService.cs
index 034c2c00..c89a6d8f 100644
--- a/src/Facility.Core/Http/HttpClientService.cs
+++ b/src/Facility.Core/Http/HttpClientService.cs
@@ -193,8 +193,15 @@ protected virtual bool ShouldCreateErrorFromException(Exception exception)
///
protected virtual ServiceErrorDto CreateErrorFromException(Exception exception) => ServiceErrorUtility.CreateInternalErrorForException(exception);
+ ///
+ /// Called right before the request is sent, before aspects are applied.
+ ///
+ protected virtual Task RequestReadyAsync(HttpRequestMessage httpRequest, ServiceDto requestDto, CancellationToken cancellationToken) => Task.CompletedTask;
+
private async Task SendRequestAsync(HttpRequestMessage httpRequest, ServiceDto requestDto, CancellationToken cancellationToken)
{
+ await AdaptTask(RequestReadyAsync(httpRequest, requestDto, cancellationToken)).ConfigureAwait(true);
+
if (m_aspects != null)
{
foreach (var aspect in m_aspects)
diff --git a/tools/EdgeCases.fsd b/tools/EdgeCases.fsd
index 96c159bc..c8226f3a 100644
--- a/tools/EdgeCases.fsd
+++ b/tools/EdgeCases.fsd
@@ -15,6 +15,21 @@ service EdgeCases
{
}
+ /// Custom HTTP method.
+ method customHttp
+ {
+ value: string;
+
+ [http(from: "custom")]
+ extras: map;
+ }:
+ {
+ value: string;
+
+ [http(from: "custom")]
+ extras: map;
+ }
+
/// An old DTO.
[obsolete]
data OldEmptyData
diff --git a/tools/EdgeCases/CustomHttpRequestDto.g.cs b/tools/EdgeCases/CustomHttpRequestDto.g.cs
new file mode 100644
index 00000000..83703128
--- /dev/null
+++ b/tools/EdgeCases/CustomHttpRequestDto.g.cs
@@ -0,0 +1,47 @@
+//
+// DO NOT EDIT: generated by fsdgencsharp
+//
+#nullable enable
+using System;
+using System.Collections.Generic;
+using Facility.Core;
+using Facility.Core.MessagePack;
+
+namespace EdgeCases
+{
+ ///
+ /// Request for CustomHttp.
+ ///
+ [System.CodeDom.Compiler.GeneratedCode("fsdgencsharp", "")]
+ [MessagePack.MessagePackObject]
+ public sealed partial class CustomHttpRequestDto : ServiceDto
+ {
+ ///
+ /// Creates an instance.
+ ///
+ public CustomHttpRequestDto()
+ {
+ }
+
+ [MessagePack.Key("value")]
+ public string? Value { get; set; }
+
+ [MessagePack.Key("extras")]
+ public IReadOnlyDictionary? Extras { get; set; }
+
+ ///
+ /// The JSON serializer.
+ ///
+ protected override JsonServiceSerializer JsonSerializer => SystemTextJsonServiceSerializer.Instance;
+
+ ///
+ /// Determines if two DTOs are equivalent.
+ ///
+ public override bool IsEquivalentTo(CustomHttpRequestDto? other)
+ {
+ return other != null &&
+ Value == other.Value &&
+ ServiceDataUtility.AreEquivalentFieldValues(Extras, other.Extras);
+ }
+ }
+}
diff --git a/tools/EdgeCases/CustomHttpResponseDto.g.cs b/tools/EdgeCases/CustomHttpResponseDto.g.cs
new file mode 100644
index 00000000..a65b4b74
--- /dev/null
+++ b/tools/EdgeCases/CustomHttpResponseDto.g.cs
@@ -0,0 +1,47 @@
+//
+// DO NOT EDIT: generated by fsdgencsharp
+//
+#nullable enable
+using System;
+using System.Collections.Generic;
+using Facility.Core;
+using Facility.Core.MessagePack;
+
+namespace EdgeCases
+{
+ ///
+ /// Response for CustomHttp.
+ ///
+ [System.CodeDom.Compiler.GeneratedCode("fsdgencsharp", "")]
+ [MessagePack.MessagePackObject]
+ public sealed partial class CustomHttpResponseDto : ServiceDto
+ {
+ ///
+ /// Creates an instance.
+ ///
+ public CustomHttpResponseDto()
+ {
+ }
+
+ [MessagePack.Key("value")]
+ public string? Value { get; set; }
+
+ [MessagePack.Key("extras")]
+ public IReadOnlyDictionary? Extras { get; set; }
+
+ ///
+ /// The JSON serializer.
+ ///
+ protected override JsonServiceSerializer JsonSerializer => SystemTextJsonServiceSerializer.Instance;
+
+ ///
+ /// Determines if two DTOs are equivalent.
+ ///
+ public override bool IsEquivalentTo(CustomHttpResponseDto? other)
+ {
+ return other != null &&
+ Value == other.Value &&
+ ServiceDataUtility.AreEquivalentFieldValues(Extras, other.Extras);
+ }
+ }
+}
diff --git a/tools/EdgeCases/DelegatingEdgeCases.g.cs b/tools/EdgeCases/DelegatingEdgeCases.g.cs
index 23377800..f2d47892 100644
--- a/tools/EdgeCases/DelegatingEdgeCases.g.cs
+++ b/tools/EdgeCases/DelegatingEdgeCases.g.cs
@@ -28,6 +28,12 @@ public DelegatingEdgeCases(ServiceDelegator delegator) =>
public virtual async Task> OldMethodAsync(OldMethodRequestDto request, CancellationToken cancellationToken = default) =>
(await m_delegator(EdgeCasesMethods.OldMethod, request, cancellationToken).ConfigureAwait(false)).Cast();
+ ///
+ /// Custom HTTP method.
+ ///
+ public virtual async Task> CustomHttpAsync(CustomHttpRequestDto request, CancellationToken cancellationToken = default) =>
+ (await m_delegator(EdgeCasesMethods.CustomHttp, request, cancellationToken).ConfigureAwait(false)).Cast();
+
public virtual async Task> SnakeMethodAsync(SnakeMethodRequestDto request, CancellationToken cancellationToken = default) =>
(await m_delegator(EdgeCasesMethods.SnakeMethod, request, cancellationToken).ConfigureAwait(false)).Cast();
diff --git a/tools/EdgeCases/EdgeCasesMethods.g.cs b/tools/EdgeCases/EdgeCasesMethods.g.cs
index ae882209..9deb0c0c 100644
--- a/tools/EdgeCases/EdgeCasesMethods.g.cs
+++ b/tools/EdgeCases/EdgeCasesMethods.g.cs
@@ -15,6 +15,10 @@ internal static class EdgeCasesMethods
ServiceMethodInfo.Create(
"oldMethod", "EdgeCases", x => x.OldMethodAsync);
+ public static readonly IServiceMethodInfo CustomHttp =
+ ServiceMethodInfo.Create(
+ "customHttp", "EdgeCases", x => x.CustomHttpAsync);
+
public static readonly IServiceMethodInfo SnakeMethod =
ServiceMethodInfo.Create(
"snake_method", "EdgeCases", x => x.SnakeMethodAsync);
diff --git a/tools/EdgeCases/Http/EdgeCasesHttpHandler.g.cs b/tools/EdgeCases/Http/EdgeCasesHttpHandler.g.cs
index 235f272c..3f772260 100644
--- a/tools/EdgeCases/Http/EdgeCasesHttpHandler.g.cs
+++ b/tools/EdgeCases/Http/EdgeCasesHttpHandler.g.cs
@@ -40,7 +40,8 @@ public EdgeCasesHttpHandler(Func getService, Ser
///
public override async Task TryHandleHttpRequestAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken = default)
{
- return await AdaptTask(TryHandleOldMethodAsync(httpRequest, cancellationToken)).ConfigureAwait(true) ??
+ return await AdaptTask(TryHandleCustomHttpAsync(httpRequest, cancellationToken)).ConfigureAwait(true) ??
+ await AdaptTask(TryHandleOldMethodAsync(httpRequest, cancellationToken)).ConfigureAwait(true) ??
await AdaptTask(TryHandleSnakeMethodAsync(httpRequest, cancellationToken)).ConfigureAwait(true);
}
@@ -51,6 +52,12 @@ public EdgeCasesHttpHandler(Func getService, Ser
public Task TryHandleOldMethodAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken = default) =>
TryHandleServiceMethodAsync(EdgeCasesHttpMapping.OldMethodMapping, httpRequest, GetService(httpRequest).OldMethodAsync, cancellationToken);
+ ///
+ /// Custom HTTP method.
+ ///
+ public Task TryHandleCustomHttpAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken = default) =>
+ TryHandleServiceMethodAsync(EdgeCasesHttpMapping.CustomHttpMapping, httpRequest, GetService(httpRequest).CustomHttpAsync, cancellationToken);
+
public Task TryHandleSnakeMethodAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken = default) =>
TryHandleServiceMethodAsync(EdgeCasesHttpMapping.SnakeMethodMapping, httpRequest, GetService(httpRequest).SnakeMethodAsync, cancellationToken);
diff --git a/tools/EdgeCases/Http/EdgeCasesHttpMapping.g.cs b/tools/EdgeCases/Http/EdgeCasesHttpMapping.g.cs
index 639268cc..5b607a60 100644
--- a/tools/EdgeCases/Http/EdgeCasesHttpMapping.g.cs
+++ b/tools/EdgeCases/Http/EdgeCasesHttpMapping.g.cs
@@ -35,6 +35,45 @@ public static partial class EdgeCasesHttpMapping
},
}.Build();
+ ///
+ /// Custom HTTP method.
+ ///
+ public static readonly HttpMethodMapping CustomHttpMapping =
+ new HttpMethodMapping.Builder
+ {
+ HttpMethod = HttpMethod.Post,
+ Path = "/customHttp",
+ RequestBodyType = typeof(CustomHttpRequestDto),
+ GetRequestBody = request =>
+ new CustomHttpRequestDto
+ {
+ Value = request.Value,
+ },
+ CreateRequest = body =>
+ new CustomHttpRequestDto
+ {
+ Value = ((CustomHttpRequestDto) body!).Value,
+ },
+ ResponseMappings =
+ {
+ new HttpResponseMapping.Builder
+ {
+ StatusCode = (HttpStatusCode) 200,
+ ResponseBodyType = typeof(CustomHttpResponseDto),
+ GetResponseBody = response =>
+ new CustomHttpResponseDto
+ {
+ Value = response.Value,
+ },
+ CreateResponse = body =>
+ new CustomHttpResponseDto
+ {
+ Value = ((CustomHttpResponseDto) body!).Value,
+ },
+ }.Build(),
+ },
+ }.Build();
+
public static readonly HttpMethodMapping SnakeMethodMapping =
new HttpMethodMapping.Builder
{
diff --git a/tools/EdgeCases/Http/HttpClientEdgeCases.cs b/tools/EdgeCases/Http/HttpClientEdgeCases.cs
new file mode 100644
index 00000000..14690e7b
--- /dev/null
+++ b/tools/EdgeCases/Http/HttpClientEdgeCases.cs
@@ -0,0 +1,19 @@
+using System.Web;
+using Facility.Core;
+
+namespace EdgeCases.Http;
+
+public sealed partial class HttpClientEdgeCases
+{
+ protected override Task RequestReadyAsync(HttpRequestMessage httpRequest, ServiceDto requestDto, CancellationToken cancellationToken)
+ {
+ if (requestDto is CustomHttpRequestDto customDto)
+ {
+ const string prefix = "extra-";
+ var query = HttpUtility.ParseQueryString(httpRequest.RequestUri.Query);
+ customDto.Extras = query.AllKeys.Where(x => x?.StartsWith(prefix, StringComparison.Ordinal) is true).ToDictionary(x => x.Substring(prefix.Length), x => query[x]);
+ }
+
+ return Task.CompletedTask;
+ }
+}
diff --git a/tools/EdgeCases/Http/HttpClientEdgeCases.g.cs b/tools/EdgeCases/Http/HttpClientEdgeCases.g.cs
index 55f04659..de1f69ab 100644
--- a/tools/EdgeCases/Http/HttpClientEdgeCases.g.cs
+++ b/tools/EdgeCases/Http/HttpClientEdgeCases.g.cs
@@ -28,6 +28,12 @@ public HttpClientEdgeCases(HttpClientServiceSettings? settings = null)
public Task> OldMethodAsync(OldMethodRequestDto request, CancellationToken cancellationToken = default) =>
TrySendRequestAsync(EdgeCasesHttpMapping.OldMethodMapping, request, cancellationToken);
+ ///
+ /// Custom HTTP method.
+ ///
+ public Task> CustomHttpAsync(CustomHttpRequestDto request, CancellationToken cancellationToken = default) =>
+ TrySendRequestAsync(EdgeCasesHttpMapping.CustomHttpMapping, request, cancellationToken);
+
public Task> SnakeMethodAsync(SnakeMethodRequestDto request, CancellationToken cancellationToken = default) =>
TrySendRequestAsync(EdgeCasesHttpMapping.SnakeMethodMapping, request, cancellationToken);
diff --git a/tools/EdgeCases/IEdgeCases.g.cs b/tools/EdgeCases/IEdgeCases.g.cs
index fa3440a6..0ae2e732 100644
--- a/tools/EdgeCases/IEdgeCases.g.cs
+++ b/tools/EdgeCases/IEdgeCases.g.cs
@@ -18,6 +18,11 @@ public partial interface IEdgeCases
[Obsolete]
Task> OldMethodAsync(OldMethodRequestDto request, CancellationToken cancellationToken = default);
+ ///
+ /// Custom HTTP method.
+ ///
+ Task> CustomHttpAsync(CustomHttpRequestDto request, CancellationToken cancellationToken = default);
+
Task> SnakeMethodAsync(SnakeMethodRequestDto request, CancellationToken cancellationToken = default);
}
}