diff --git a/src/Dapr.Client/DaprClient.cs b/src/Dapr.Client/DaprClient.cs
index 94a43b759..9f107578f 100644
--- a/src/Dapr.Client/DaprClient.cs
+++ b/src/Dapr.Client/DaprClient.cs
@@ -58,7 +58,7 @@ public abstract class DaprClient : IDisposable
/// The client will read the property, and
/// interpret the hostname as the destination app-id. The
/// property will be replaced with a new URI with the authority section replaced by
- /// and the path portion of the URI rewitten to follow the format of a Dapr service invocation request.
+ /// and the path portion of the URI rewritten to follow the format of a Dapr service invocation request.
///
///
///
@@ -448,6 +448,30 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, stri
/// A that will return the value when the operation has completed.
public abstract Task InvokeMethodWithResponseAsync(HttpRequestMessage request, CancellationToken cancellationToken = default);
+#nullable enable
+ ///
+ ///
+ /// Creates an that can be used to perform Dapr service invocation using
+ /// objects.
+ ///
+ ///
+ /// The client will read the property, and
+ /// interpret the hostname as the destination app-id. The
+ /// property will be replaced with a new URI with the authority section replaced by the HTTP endpoint value
+ /// and the path portion of the URI rewritten to follow the format of a Dapr service invocation request.
+ ///
+ ///
+ ///
+ /// An optional app-id. If specified, the app-id will be configured as the value of
+ /// so that relative URIs can be used. It is mandatory to set this parameter if your app-id contains at least one upper letter.
+ /// If some requests use absolute URL with an app-id which contains at least one upper letter, it will not work, the workaround is to create one HttpClient for each app-id with the app-ip parameter set.
+ ///
+ /// An that can be used to perform service invocation requests.
+ ///
+ ///
+ public abstract HttpClient CreateInvokableHttpClient(string? appId = null);
+#nullable disable
+
///
/// Perform service invocation using the request provided by . If the response has a non-success
/// status an exception will be thrown.
diff --git a/src/Dapr.Client/DaprClientGrpc.cs b/src/Dapr.Client/DaprClientGrpc.cs
index c0c0015e0..c70aef77b 100644
--- a/src/Dapr.Client/DaprClientGrpc.cs
+++ b/src/Dapr.Client/DaprClientGrpc.cs
@@ -450,6 +450,31 @@ public override async Task InvokeMethodWithResponseAsync(Ht
}
}
+ ///
+ ///
+ /// Creates an that can be used to perform Dapr service invocation using
+ /// objects.
+ ///
+ ///
+ /// The client will read the property, and
+ /// interpret the hostname as the destination app-id. The
+ /// property will be replaced with a new URI with the authority section replaced by the instance's value
+ /// and the path portion of the URI rewritten to follow the format of a Dapr service invocation request.
+ ///
+ ///
+ ///
+ /// An optional app-id. If specified, the app-id will be configured as the value of
+ /// so that relative URIs can be used. It is mandatory to set this parameter if your app-id contains at least one upper letter.
+ /// If some requests use absolute URL with an app-id which contains at least one upper letter, it will not work, the workaround is to create one HttpClient for each app-id with the app-ip parameter set.
+ ///
+ /// An that can be used to perform service invocation requests.
+ ///
+ ///
+#nullable enable
+ public override HttpClient CreateInvokableHttpClient(string? appId = null) =>
+ DaprClient.CreateInvokeHttpClient(appId, this.httpEndpoint?.AbsoluteUri, this.apiTokenHeader?.Value);
+ #nullable disable
+
public async override Task InvokeMethodAsync(HttpRequestMessage request, CancellationToken cancellationToken = default)
{
ArgumentVerifier.ThrowIfNull(request, nameof(request));
diff --git a/test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs b/test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs
new file mode 100644
index 000000000..99fbd4972
--- /dev/null
+++ b/test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs
@@ -0,0 +1,52 @@
+// ------------------------------------------------------------------------
+// Copyright 2024 The Dapr Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ------------------------------------------------------------------------
+
+using System;
+using Xunit;
+
+namespace Dapr.Client
+{
+ public partial class DaprClientTest
+ {
+ [Fact]
+ public void CreateInvokableHttpClient_WithAppId_FromDaprClient()
+ {
+ var daprClient = new MockClient().DaprClient;
+ var client = daprClient.CreateInvokableHttpClient(appId: "bank");
+ Assert.Equal("http://bank/", client.BaseAddress.AbsoluteUri);
+ }
+
+ [Fact]
+ public void CreateInvokableHttpClient_InvalidAppId_FromDaprClient()
+ {
+ var daprClient = new MockClient().DaprClient;
+ var ex = Assert.Throws(() =>
+ {
+ // The appId needs to be something that can be used as hostname in a URI.
+ _ = daprClient.CreateInvokableHttpClient(appId: "");
+ });
+
+ Assert.Contains("The appId must be a valid hostname.", ex.Message);
+ Assert.IsType(ex.InnerException);
+ }
+
+ [Fact]
+ public void CreateInvokableHttpClient_WithoutAppId_FromDaprClient()
+ {
+ var daprClient = new MockClient().DaprClient;
+
+ var client = daprClient.CreateInvokableHttpClient();
+ Assert.Null(client.BaseAddress);
+ }
+ }
+}