diff --git a/JsonRpc.Standard/Contracts/JsonRpcMethod.cs b/JsonRpc.Standard/Contracts/JsonRpcMethod.cs
index fbed72f..605a87d 100644
--- a/JsonRpc.Standard/Contracts/JsonRpcMethod.cs
+++ b/JsonRpc.Standard/Contracts/JsonRpcMethod.cs
@@ -92,7 +92,9 @@ internal object[] UnmarshalArguments(MarshaledRequest request)
continue;
}
// Resolve other parameters, considering the optional
- var jarg = request.Message.Parameters?[this.Parameters[i].ParameterName];
+ var jarg = request.Message.Parameters.Type == JTokenType.Object
+ ? request.Message.Parameters?[this.Parameters[i].ParameterName]
+ : request.Message.Parameters?[i];
if (jarg == null)
{
if (this.Parameters[i].IsOptional)
diff --git a/JsonRpc.Standard/Contracts/RpcMethodBinder.cs b/JsonRpc.Standard/Contracts/RpcMethodBinder.cs
index 1956b18..7489440 100644
--- a/JsonRpc.Standard/Contracts/RpcMethodBinder.cs
+++ b/JsonRpc.Standard/Contracts/RpcMethodBinder.cs
@@ -1,4 +1,6 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Reflection;
using JsonRpc.Standard.Server;
@@ -31,15 +33,42 @@ internal class JsonRpcMethodBinder : IJsonRpcMethodBinder
///
public JsonRpcMethod TryBindToMethod(ICollection candidates, RequestContext context)
{
- //TODO Support array as params
// context.Request.Parameters can be: {}, [], null (JValue), null
- var paramsObj = context.Request.Parameters as JObject;
- if (context.Request.Parameters is JArray) return null;
+ // Parameters MAY be omitted.
+ if (context.Request.Parameters == null || context.Request.Parameters.Type == JTokenType.Null)
+ return TryBindToParameterlessMethod(candidates);
+ // by-name
+ if (context.Request.Parameters is JObject paramsObj)
+ return TryBindToMethod(candidates, paramsObj);
+ // by-position
+ if (context.Request.Parameters is JArray paramsArray)
+ return TryBindToMethod(candidates, paramsArray);
+ // Other invalid cases, e.g., naked JValue.
+ return null;
+ }
+
+ private JsonRpcMethod TryBindToParameterlessMethod(ICollection candidates)
+ {
+ JsonRpcMethod firstMatch = null;
+ foreach (var m in candidates)
+ {
+ if (m.Parameters.Count == 0 || m.Parameters.All(p => p.IsOptional))
+ {
+ if (firstMatch != null) throw new AmbiguousMatchException();
+ firstMatch = m;
+ }
+ }
+ return firstMatch;
+ }
+
+ private JsonRpcMethod TryBindToMethod(ICollection candidates, JObject paramsObj)
+ {
+ Debug.Assert(paramsObj != null);
JsonRpcMethod firstMatch = null;
Dictionary requestProp = null;
foreach (var m in candidates)
{
- if (!m.AllowExtensionData && paramsObj != null)
+ if (!m.AllowExtensionData)
{
// Strict match
requestProp = paramsObj.Properties().ToDictionary(p => p.Name, p => p.Value);
@@ -64,5 +93,31 @@ public JsonRpcMethod TryBindToMethod(ICollection candidates, Requ
}
return firstMatch;
}
+
+ private JsonRpcMethod TryBindToMethod(ICollection candidates, JArray paramsArray)
+ {
+ Debug.Assert(paramsArray != null);
+ JsonRpcMethod firstMatch = null;
+ foreach (var m in candidates)
+ {
+ if (!m.AllowExtensionData && paramsArray.Count > m.Parameters.Count) goto NEXT;
+ for (var i = 0; i < m.Parameters.Count; i++)
+ {
+ var param = m.Parameters[i];
+ var jparam = i < paramsArray.Count ? paramsArray[i] : null;
+ if (jparam == null)
+ {
+ if (!param.IsOptional) goto NEXT;
+ else continue;
+ }
+ if (!param.MatchJTokenType(jparam.Type)) goto NEXT;
+ }
+ if (firstMatch != null) throw new AmbiguousMatchException();
+ firstMatch = m;
+ NEXT:
+ ;
+ }
+ return firstMatch;
+ }
}
}
diff --git a/JsonRpc.Standard/Server/JsonRpcServiceHost.cs b/JsonRpc.Standard/Server/JsonRpcServiceHost.cs
index 83aeba5..e19a597 100644
--- a/JsonRpc.Standard/Server/JsonRpcServiceHost.cs
+++ b/JsonRpc.Standard/Server/JsonRpcServiceHost.cs
@@ -127,12 +127,6 @@ private async Task DispatchRpcMethod(RequestContext context)
{
args = method.UnmarshalArguments(new MarshaledRequest(context.Request, context.CancellationToken));
}
- catch (ArgumentException ex)
- {
- // Signature not match. This is not likely to happen. Still there might be problem with binder.
- TrySetErrorResponse(context, JsonRpcErrorCode.InvalidParams, ex.Message);
- return;
- }
catch (Exception ex)
{
TrySetErrorResponse(context, JsonRpcErrorCode.InvalidParams, ex.Message);
diff --git a/UnitTestProject1/ServiceHostTests.cs b/UnitTestProject1/ServiceHostTests.cs
index 14049aa..db139b3 100644
--- a/UnitTestProject1/ServiceHostTests.cs
+++ b/UnitTestProject1/ServiceHostTests.cs
@@ -17,8 +17,7 @@ public class ServiceHostTests : UnitTestBase
public ServiceHostTests(ITestOutputHelper output) : base(output)
{
}
-
-
+
[Fact]
public async Task BasicServiceHostTest()
{
@@ -50,5 +49,26 @@ public async Task BasicServiceHostTest()
Assert.Equal(JsonRpcErrorCode.UnhandledClrException, (JsonRpcErrorCode) response.Error.Code);
}
+ [Fact]
+ public async Task InvokeWithPositionalArgumentsTest()
+ {
+ var host = Utility.CreateJsonRpcServiceHost(this);
+ var response = await host.InvokeAsync(
+ new RequestMessage(123, "add", JToken.FromObject(new[] {20, 35})),
+ null,
+ CancellationToken.None);
+ Assert.NotNull(response);
+ Assert.Equal(123, response.Id);
+ Assert.Null(response.Error);
+ Assert.Equal(55, (int)response.Result);
+ response = await host.InvokeAsync(
+ new RequestMessage("TEST", "add", JToken.FromObject(new[] {"abc", "def"})),
+ null,
+ CancellationToken.None);
+ Assert.NotNull(response);
+ Assert.Equal("TEST", response.Id);
+ Assert.Null(response.Error);
+ Assert.Equal("abcdef", (string)response.Result);
+ }
}
}