diff --git a/Directory.Packages.props b/Directory.Packages.props
index 1397c7567..6e0da532b 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -35,6 +35,7 @@
+
diff --git a/src/MagicOnion.Server.JsonTranscoding/MagicOnionJsonTranscodingGrpcMethodBinder.cs b/src/MagicOnion.Server.JsonTranscoding/MagicOnionJsonTranscodingGrpcMethodBinder.cs
index 7f54a5df2..910942702 100644
--- a/src/MagicOnion.Server.JsonTranscoding/MagicOnionJsonTranscodingGrpcMethodBinder.cs
+++ b/src/MagicOnion.Server.JsonTranscoding/MagicOnionJsonTranscodingGrpcMethodBinder.cs
@@ -44,7 +44,11 @@ public void BindUnary(IMagicOnio
var memStream = new MemoryStream();
await context.Request.BodyReader.CopyToAsync(memStream);
- var request = grpcMethod.RequestMarshaller.ContextualDeserializer(new DeserializationContextImpl(memStream.ToArray()));
+ // If the request type is `Nil` (parameter-less method), we always ignore the request body.
+ TRawRequest request = (typeof(TRequest) == typeof(Nil))
+ ? (TRawRequest)(object)Box.Create(Nil.Default)
+ : grpcMethod.RequestMarshaller.ContextualDeserializer(new DeserializationContextImpl(memStream.ToArray()));
+
var response = await unaryMethodHandler(handle.Instance, request, serverCallContext);
context.Response.ContentType = "application/json";
diff --git a/tests/MagicOnion.Server.InternalTesting/MagicOnionApplicationFactory.cs b/tests/MagicOnion.Server.InternalTesting/MagicOnionApplicationFactory.cs
index 22c7ebddb..14f6600c7 100644
--- a/tests/MagicOnion.Server.InternalTesting/MagicOnionApplicationFactory.cs
+++ b/tests/MagicOnion.Server.InternalTesting/MagicOnionApplicationFactory.cs
@@ -35,7 +35,7 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
builder.ConfigureServices(services =>
{
services.AddKeyedSingleton>(ItemsKey);
- services.AddMagicOnion();
+ OnConfigureMagicOnionBuilder(services.AddMagicOnion());
});
builder.Configure(app =>
{
@@ -47,6 +47,8 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
});
}
+ protected virtual void OnConfigureMagicOnionBuilder(IMagicOnionServerBuilder builder){}
+
protected abstract IEnumerable GetServiceImplementationTypes();
public WebApplicationFactory WithMagicOnionOptions(Action configure)
diff --git a/tests/MagicOnion.Server.JsonTranscoding.Tests/JsonTranscodingEnabledMagicOnionApplicationFactory.cs b/tests/MagicOnion.Server.JsonTranscoding.Tests/JsonTranscodingEnabledMagicOnionApplicationFactory.cs
new file mode 100644
index 000000000..a742d6f67
--- /dev/null
+++ b/tests/MagicOnion.Server.JsonTranscoding.Tests/JsonTranscodingEnabledMagicOnionApplicationFactory.cs
@@ -0,0 +1,11 @@
+using Microsoft.Extensions.DependencyInjection;
+
+namespace MagicOnion.Server.JsonTranscoding.Tests;
+
+public class JsonTranscodingEnabledMagicOnionApplicationFactory : MagicOnionApplicationFactory
+{
+ protected override void OnConfigureMagicOnionBuilder(IMagicOnionServerBuilder builder)
+ {
+ builder.AddJsonTranscoding();
+ }
+}
diff --git a/tests/MagicOnion.Server.JsonTranscoding.Tests/MagicOnion.Server.JsonTranscoding.Tests.csproj b/tests/MagicOnion.Server.JsonTranscoding.Tests/MagicOnion.Server.JsonTranscoding.Tests.csproj
index a2b37b466..d0d2b65e9 100644
--- a/tests/MagicOnion.Server.JsonTranscoding.Tests/MagicOnion.Server.JsonTranscoding.Tests.csproj
+++ b/tests/MagicOnion.Server.JsonTranscoding.Tests/MagicOnion.Server.JsonTranscoding.Tests.csproj
@@ -12,10 +12,16 @@
+
+
+
+
+
+
diff --git a/tests/MagicOnion.Server.JsonTranscoding.Tests/UnaryFunctionalTests.cs b/tests/MagicOnion.Server.JsonTranscoding.Tests/UnaryFunctionalTests.cs
new file mode 100644
index 000000000..c60214a96
--- /dev/null
+++ b/tests/MagicOnion.Server.JsonTranscoding.Tests/UnaryFunctionalTests.cs
@@ -0,0 +1,130 @@
+using System.Collections.Concurrent;
+using System.Net;
+using System.Net.Http.Headers;
+using System.Text.Json;
+using MessagePack;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace MagicOnion.Server.JsonTranscoding.Tests;
+
+public class UnaryFunctionalTests(JsonTranscodingEnabledMagicOnionApplicationFactory factory) : IClassFixture>
+{
+ [Fact]
+ public async Task NotImplemented()
+ {
+ // Arrange
+ var httpClient = factory.CreateDefaultClient();
+
+ // Act
+ var response = await httpClient.PostAsync($"http://localhost/_/ITestService/NotImplemented", new StringContent(string.Empty, new MediaTypeHeaderValue("application/json")));
+
+ // Assert
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task Method_NoParameter_NoResult()
+ {
+ // Arrange
+ var httpClient = factory.CreateDefaultClient();
+
+ // Act
+ var response = await httpClient.PostAsync($"http://localhost/_/ITestService/Method_NoParameter_NoResult", new StringContent(string.Empty, new MediaTypeHeaderValue("application/json")));
+ var content = await response.Content.ReadAsStringAsync();
+
+ // Assert
+ var result = default(object); // null
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal(result, JsonSerializer.Deserialize