Skip to content

Commit

Permalink
#3: Server-side by-position argument support.
Browse files Browse the repository at this point in the history
  • Loading branch information
CXuesong committed Sep 21, 2017
1 parent 01f295b commit 810a909
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 14 deletions.
4 changes: 3 additions & 1 deletion JsonRpc.Standard/Contracts/JsonRpcMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
65 changes: 60 additions & 5 deletions JsonRpc.Standard/Contracts/RpcMethodBinder.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -31,15 +33,42 @@ internal class JsonRpcMethodBinder : IJsonRpcMethodBinder
/// <inheritdoc />
public JsonRpcMethod TryBindToMethod(ICollection<JsonRpcMethod> 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<JsonRpcMethod> 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<JsonRpcMethod> candidates, JObject paramsObj)
{
Debug.Assert(paramsObj != null);
JsonRpcMethod firstMatch = null;
Dictionary<string, JToken> 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);
Expand All @@ -64,5 +93,31 @@ public JsonRpcMethod TryBindToMethod(ICollection<JsonRpcMethod> candidates, Requ
}
return firstMatch;
}

private JsonRpcMethod TryBindToMethod(ICollection<JsonRpcMethod> 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;
}
}
}
6 changes: 0 additions & 6 deletions JsonRpc.Standard/Server/JsonRpcServiceHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
24 changes: 22 additions & 2 deletions UnitTestProject1/ServiceHostTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ public class ServiceHostTests : UnitTestBase
public ServiceHostTests(ITestOutputHelper output) : base(output)
{
}



[Fact]
public async Task BasicServiceHostTest()
{
Expand Down Expand Up @@ -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);
}
}
}

0 comments on commit 810a909

Please sign in to comment.