Make System.Text.Json the default for SignalR and remove Newtonsoft from shared framework (#9476)

This commit is contained in:
BrennanConroy 2019-04-18 13:49:24 -07:00 committed by GitHub
parent 56c064bd53
commit c84e37f30d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 239 additions and 157 deletions

View File

@ -68,7 +68,6 @@
If these are needed as direct dependencies, it is okay to change them to ExternalAspNetCoreAppReference and move up into sections above. If these are needed as direct dependencies, it is okay to change them to ExternalAspNetCoreAppReference and move up into sections above.
--> -->
<_TransitiveExternalAspNetCoreAppReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
<_TransitiveExternalAspNetCoreAppReference Include="System.Security.Cryptography.Pkcs" Version="$(SystemSecurityCryptographyPkcsPackageVersion)" /> <_TransitiveExternalAspNetCoreAppReference Include="System.Security.Cryptography.Pkcs" Version="$(SystemSecurityCryptographyPkcsPackageVersion)" />
<_TransitiveExternalAspNetCoreAppReference Include="System.Security.Permissions" Version="$(SystemSecurityPermissionsPackageVersion)" /> <_TransitiveExternalAspNetCoreAppReference Include="System.Security.Permissions" Version="$(SystemSecurityPermissionsPackageVersion)" />
</ItemGroup> </ItemGroup>

View File

@ -12,7 +12,6 @@
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Connections.Abstractions" /> <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Connections.Abstractions" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Http.Connections.Common" /> <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Http.Connections.Common" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.SignalR.Protocols.Json" /> <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.SignalR.Protocols.Json" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.SignalR.Common" /> <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.SignalR.Common" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components.Browser" /> <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components.Browser" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components" /> <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components" />

View File

@ -79,13 +79,11 @@
<_InvalidReferenceToNonSharedFxAssembly Condition="'$(IsAspNetCoreApp)' == 'true'" <_InvalidReferenceToNonSharedFxAssembly Condition="'$(IsAspNetCoreApp)' == 'true'"
Include="@(Reference)" Include="@(Reference)"
Exclude=" Exclude="
Newtonsoft.Json;
@(AspNetCoreAppReference); @(AspNetCoreAppReference);
@(AspNetCoreAppReferenceAndPackage); @(AspNetCoreAppReferenceAndPackage);
@(ExternalAspNetCoreAppReference); @(ExternalAspNetCoreAppReference);
@(_CompilationOnlyReference); @(_CompilationOnlyReference);
@(Reference->WithMetadataValue('IsSharedSource', 'true'))" /> @(Reference->WithMetadataValue('IsSharedSource', 'true'))" />
<!-- TODO: remove Newtonsoft.Json from this list once https://github.com/aspnet/AspNetCore/issues/4260 is resolved -->
<_OriginalReferences Include="@(Reference)" /> <_OriginalReferences Include="@(Reference)" />
<!-- <!--
Turn Reference items into a ProjectReference when UseProjectReferences is true. Turn Reference items into a ProjectReference when UseProjectReferences is true.

View File

@ -6,7 +6,7 @@
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<Compile Include="Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs" /> <Compile Include="Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs" />
<Reference Include="Microsoft.AspNetCore.SignalR.Common" /> <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
<Reference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" /> <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" />
<Reference Include="Microsoft.Extensions.DependencyInjection" /> <Reference Include="Microsoft.Extensions.DependencyInjection" />
<Reference Include="Microsoft.Extensions.Logging" /> <Reference Include="Microsoft.Extensions.Logging" />
<Reference Include="System.Threading.Channels" /> <Reference Include="System.Threading.Channels" />
@ -14,7 +14,7 @@
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
<Compile Include="Microsoft.AspNetCore.SignalR.Client.Core.netcoreapp3.0.cs" /> <Compile Include="Microsoft.AspNetCore.SignalR.Client.Core.netcoreapp3.0.cs" />
<Reference Include="Microsoft.AspNetCore.SignalR.Common" /> <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
<Reference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" /> <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" />
<Reference Include="Microsoft.Extensions.DependencyInjection" /> <Reference Include="Microsoft.Extensions.DependencyInjection" />
<Reference Include="Microsoft.Extensions.Logging" /> <Reference Include="Microsoft.Extensions.Logging" />
<Reference Include="System.Threading.Channels" /> <Reference Include="System.Threading.Channels" />

View File

@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
Services = new ServiceCollection(); Services = new ServiceCollection();
Services.AddSingleton<HubConnection>(); Services.AddSingleton<HubConnection>();
Services.AddLogging(); Services.AddLogging();
this.AddNewtonsoftJsonProtocol(); this.AddJsonProtocol();
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>Client for ASP.NET Core SignalR</Description> <Description>Client for ASP.NET Core SignalR</Description>
@ -22,7 +22,7 @@
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.AspNetCore.SignalR.Common" /> <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
<Reference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" /> <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" />
<Reference Include="Microsoft.Extensions.DependencyInjection" /> <Reference Include="Microsoft.Extensions.DependencyInjection" />
<Reference Include="Microsoft.Extensions.Logging" /> <Reference Include="Microsoft.Extensions.Logging" />
<Reference Include="System.Threading.Channels" /> <Reference Include="System.Threading.Channels" />

View File

@ -1014,7 +1014,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
} }
} }
[Theory] [Theory(Skip = "Will be fixed by https://github.com/dotnet/corefx/issues/36901")]
[MemberData(nameof(HubProtocolsAndTransportsAndHubPaths))] [MemberData(nameof(HubProtocolsAndTransportsAndHubPaths))]
public async Task ServerThrowsHubExceptionOnHubMethodArgumentTypeMismatch(string hubProtocolName, HttpTransportType transportType, string hubPath) public async Task ServerThrowsHubExceptionOnHubMethodArgumentTypeMismatch(string hubProtocolName, HttpTransportType transportType, string hubPath)
{ {

View File

@ -10,7 +10,6 @@ namespace FunctionalTests
public string String { get; set; } public string String { get; set; }
public int[] IntArray { get; set; } public int[] IntArray { get; set; }
public byte[] ByteArray { get; set; } public byte[] ByteArray { get; set; }
public Guid GUID { get; set; }
public DateTime DateTime { get;set; } public DateTime DateTime { get;set; }
} }
} }

View File

@ -41,12 +41,6 @@ namespace FunctionalTests
{ {
options.EnableDetailedErrors = true; options.EnableDetailedErrors = true;
}) })
.AddNewtonsoftJsonProtocol(options =>
{
// we are running the same tests with JSON and MsgPack protocols and having
// consistent casing makes it cleaner to verify results
options.PayloadSerializerSettings.ContractResolver = new DefaultContractResolver();
})
.AddMessagePackProtocol(); .AddMessagePackProtocol();
services.AddCors(); services.AddCors();

View File

@ -125,7 +125,6 @@ namespace FunctionalTests
{ {
ByteArray = new byte[] { 0x1, 0x2, 0x3 }, ByteArray = new byte[] { 0x1, 0x2, 0x3 },
DateTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc), DateTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc),
GUID = new Guid("00010203-0405-0607-0706-050403020100"),
IntArray = new int[] { 1, 2, 3 }, IntArray = new int[] { 1, 2, 3 },
String = "hello world", String = "hello world",
}; };

View File

@ -457,12 +457,11 @@ describe("hubConnection", () => {
const complexObject = { const complexObject = {
ByteArray: protocol.name === "json" ByteArray: protocol.name === "json"
? "aGVsbG8=" ? new Array(0x68, 0x65, 0x6c, 0x6c, 0x6f)
: new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x6f]), : new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x6f]),
DateTime: protocol.name === "json" DateTime: protocol.name === "json"
? "2002-04-01T10:20:15Z" ? "2002-04-01T10:20:15Z"
: new Date(Date.UTC(2002, 3, 1, 10, 20, 15)), // Apr 1, 2002, 10:20:15am UTC : new Date(Date.UTC(2002, 3, 1, 10, 20, 15)), // Apr 1, 2002, 10:20:15am UTC
GUID: "00010203-0405-0607-0706-050403020100",
IntArray: [0x01, 0x02, 0x03, 0xff], IntArray: [0x01, 0x02, 0x03, 0xff],
String: "Hello, World!", String: "Hello, World!",
}; };
@ -500,12 +499,11 @@ describe("hubConnection", () => {
const complexObject = { const complexObject = {
ByteArray: protocol.name === "json" ByteArray: protocol.name === "json"
? "AQID" ? new Array(0x1, 0x2, 0x3)
: new Uint8Array([0x1, 0x2, 0x3]), : new Uint8Array([0x1, 0x2, 0x3]),
DateTime: protocol.name === "json" DateTime: protocol.name === "json"
? "2000-01-01T00:00:00Z" ? "2000-01-01T00:00:00Z"
: new Date(Date.UTC(2000, 0, 1)), : new Date(Date.UTC(2000, 0, 1)),
GUID: "00010203-0405-0607-0706-050403020100",
IntArray: [0x01, 0x02, 0x03], IntArray: [0x01, 0x02, 0x03],
String: "hello world", String: "hello world",
}; };

View File

@ -1,11 +1,22 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.SignalR
{
public partial class JsonHubProtocolOptions
{
public JsonHubProtocolOptions() { }
public bool AllowTrailingCommas { get { throw null; } set { } }
public bool IgnoreNullValues { get { throw null; } set { } }
public bool WriteIndented { get { throw null; } set { } }
}
}
namespace Microsoft.AspNetCore.SignalR.Protocol namespace Microsoft.AspNetCore.SignalR.Protocol
{ {
public sealed partial class JsonHubProtocol : Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol public sealed partial class JsonHubProtocol : Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol
{ {
public JsonHubProtocol() { } public JsonHubProtocol() { }
public JsonHubProtocol(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.SignalR.JsonHubProtocolOptions> options) { }
public int MinorVersion { get { throw null; } } public int MinorVersion { get { throw null; } }
public string Name { get { throw null; } } public string Name { get { throw null; } }
public Microsoft.AspNetCore.Connections.TransferFormat TransferFormat { get { throw null; } } public Microsoft.AspNetCore.Connections.TransferFormat TransferFormat { get { throw null; } }
@ -21,5 +32,6 @@ namespace Microsoft.Extensions.DependencyInjection
public static partial class JsonProtocolDependencyInjectionExtensions public static partial class JsonProtocolDependencyInjectionExtensions
{ {
public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder) where TBuilder : Microsoft.AspNetCore.SignalR.ISignalRBuilder { throw null; } public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder) where TBuilder : Microsoft.AspNetCore.SignalR.ISignalRBuilder { throw null; }
public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder, System.Action<Microsoft.AspNetCore.SignalR.JsonHubProtocolOptions> configure) where TBuilder : Microsoft.AspNetCore.SignalR.ISignalRBuilder { throw null; }
} }
} }

View File

@ -1,11 +1,22 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.SignalR
{
public partial class JsonHubProtocolOptions
{
public JsonHubProtocolOptions() { }
public bool AllowTrailingCommas { get { throw null; } set { } }
public bool IgnoreNullValues { get { throw null; } set { } }
public bool WriteIndented { get { throw null; } set { } }
}
}
namespace Microsoft.AspNetCore.SignalR.Protocol namespace Microsoft.AspNetCore.SignalR.Protocol
{ {
public sealed partial class JsonHubProtocol : Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol public sealed partial class JsonHubProtocol : Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol
{ {
public JsonHubProtocol() { } public JsonHubProtocol() { }
public JsonHubProtocol(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.SignalR.JsonHubProtocolOptions> options) { }
public int MinorVersion { get { throw null; } } public int MinorVersion { get { throw null; } }
public string Name { get { throw null; } } public string Name { get { throw null; } }
public Microsoft.AspNetCore.Connections.TransferFormat TransferFormat { get { throw null; } } public Microsoft.AspNetCore.Connections.TransferFormat TransferFormat { get { throw null; } }
@ -21,5 +32,6 @@ namespace Microsoft.Extensions.DependencyInjection
public static partial class JsonProtocolDependencyInjectionExtensions public static partial class JsonProtocolDependencyInjectionExtensions
{ {
public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder) where TBuilder : Microsoft.AspNetCore.SignalR.ISignalRBuilder { throw null; } public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder) where TBuilder : Microsoft.AspNetCore.SignalR.ISignalRBuilder { throw null; }
public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder, System.Action<Microsoft.AspNetCore.SignalR.JsonHubProtocolOptions> configure) where TBuilder : Microsoft.AspNetCore.SignalR.ISignalRBuilder { throw null; }
} }
} }

View File

@ -0,0 +1,25 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.SignalR.Protocol;
namespace Microsoft.AspNetCore.SignalR
{
/// <summary>
/// Options used to configure a <see cref="JsonHubProtocolOptions"/> instance.
/// </summary>
public class JsonHubProtocolOptions
{
internal readonly JsonSerializerOptions _serializerOptions;
public JsonHubProtocolOptions()
{
_serializerOptions = JsonHubProtocol.CreateDefaultSerializerSettings();
}
public bool IgnoreNullValues { get => _serializerOptions.IgnoreNullValues; set => _serializerOptions.IgnoreNullValues = value; }
public bool WriteIndented { get => _serializerOptions.WriteIndented; set => _serializerOptions.WriteIndented = value; }
public bool AllowTrailingCommas { get => _serializerOptions.AllowTrailingCommas; set => _serializerOptions.AllowTrailingCommas = value; }
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Protocol;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
@ -21,8 +22,21 @@ namespace Microsoft.Extensions.DependencyInjection
/// <param name="builder">The <see cref="ISignalRBuilder"/> representing the SignalR server to add JSON protocol support to.</param> /// <param name="builder">The <see cref="ISignalRBuilder"/> representing the SignalR server to add JSON protocol support to.</param>
/// <returns>The value of <paramref name="builder"/></returns> /// <returns>The value of <paramref name="builder"/></returns>
public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder) where TBuilder : ISignalRBuilder public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder) where TBuilder : ISignalRBuilder
=> AddJsonProtocol(builder, _ => { });
/// <summary>
/// Enables the JSON protocol for SignalR and allows options for the JSON protocol to be configured.
/// </summary>
/// <remarks>
/// Any options configured here will be applied, even if the JSON protocol has already been registered with the server.
/// </remarks>
/// <param name="builder">The <see cref="ISignalRBuilder"/> representing the SignalR server to add JSON protocol support to.</param>
/// <param name="configure">A delegate that can be used to configure the <see cref="JsonHubProtocolOptions"/></param>
/// <returns>The value of <paramref name="builder"/></returns>
public static TBuilder AddJsonProtocol<TBuilder>(this TBuilder builder, Action<JsonHubProtocolOptions> configure) where TBuilder : ISignalRBuilder
{ {
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IHubProtocol, JsonHubProtocol>()); builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IHubProtocol, JsonHubProtocol>());
builder.Services.Configure(configure);
return builder; return builder;
} }
} }

View File

@ -21,7 +21,7 @@
<Reference Include="Microsoft.AspNetCore.SignalR.Common" /> <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'" > <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<Reference Include="Microsoft.Bcl.Json.Sources" /> <Reference Include="Microsoft.Bcl.Json.Sources" />
<Reference Include="System.Buffers" /> <Reference Include="System.Buffers" />
<Reference Include="System.Runtime.CompilerServices.Unsafe" /> <Reference Include="System.Runtime.CompilerServices.Unsafe" />

View File

@ -10,6 +10,7 @@ using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.SignalR.Protocol namespace Microsoft.AspNetCore.SignalR.Protocol
{ {
@ -42,13 +43,27 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
private static readonly int ProtocolVersion = 1; private static readonly int ProtocolVersion = 1;
private static readonly int ProtocolMinorVersion = 0; private static readonly int ProtocolMinorVersion = 0;
/// <summary>
/// Gets the serializer used to serialize invocation arguments and return values.
/// </summary>
private readonly JsonSerializerOptions _payloadSerializerOptions;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JsonHubProtocol"/> class. /// Initializes a new instance of the <see cref="JsonHubProtocol"/> class.
/// </summary> /// </summary>
public JsonHubProtocol() public JsonHubProtocol() : this(Options.Create(new JsonHubProtocolOptions()))
{ {
} }
/// <summary>
/// Initializes a new instance of the <see cref="JsonHubProtocol"/> class.
/// </summary>
/// <param name="options">The options used to initialize the protocol.</param>
public JsonHubProtocol(IOptions<JsonHubProtocolOptions> options)
{
_payloadSerializerOptions = options.Value._serializerOptions;
}
/// <inheritdoc /> /// <inheritdoc />
public string Name => ProtocolName; public string Name => ProtocolName;
@ -188,11 +203,8 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
{ {
// If we have an invocation id already we can parse the end result // If we have an invocation id already we can parse the end result
var returnType = binder.GetReturnType(invocationId); var returnType = binder.GetReturnType(invocationId);
if (reader.TokenType != JsonTokenType.Null) using var token = JsonDocument.ParseValue(ref reader);
{ result = BindType(token.RootElement, returnType);
using var token = JsonDocument.ParseValue(ref reader);
result = BindType(token.RootElement, returnType);
}
} }
} }
else if (reader.TextEquals(ItemPropertyNameBytes)) else if (reader.TextEquals(ItemPropertyNameBytes))
@ -216,11 +228,8 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
try try
{ {
var itemType = binder.GetStreamItemType(id); var itemType = binder.GetStreamItemType(id);
if (reader.TokenType != JsonTokenType.Null) using var token = JsonDocument.ParseValue(ref reader);
{ item = BindType(token.RootElement, itemType);
using var token = JsonDocument.ParseValue(ref reader);
item = BindType(token.RootElement, itemType);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -571,7 +580,7 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
private JsonDocument GetParsedObject(object obj, Type type) private JsonDocument GetParsedObject(object obj, Type type)
{ {
var bytes = JsonSerializer.ToBytes(obj, type); var bytes = JsonSerializer.ToBytes(obj, type, _payloadSerializerOptions);
var token = JsonDocument.Parse(bytes); var token = JsonDocument.Parse(bytes);
return token; return token;
} }
@ -695,11 +704,7 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
return jsonObject.GetDateTimeOffset(); return jsonObject.GetDateTimeOffset();
} }
if (jsonObject.Type == JsonValueType.Null) return JsonSerializer.Parse(jsonObject.GetRawText(), type, _payloadSerializerOptions);
{
return null;
}
return JsonSerializer.Parse(jsonObject.GetRawText(), type);
} }
private object[] BindTypes(JsonElement jsonArray, IReadOnlyList<Type> paramTypes) private object[] BindTypes(JsonElement jsonArray, IReadOnlyList<Type> paramTypes)
@ -756,5 +761,18 @@ namespace Microsoft.AspNetCore.SignalR.Protocol
return message; return message;
} }
internal static JsonSerializerOptions CreateDefaultSerializerSettings()
{
var options = new JsonSerializerOptions();
options.WriteIndented = false;
options.ReadCommentHandling = JsonCommentHandling.Disallow;
options.AllowTrailingCommas = false;
options.IgnoreNullValues = false;
options.IgnoreReadOnlyProperties = false;
// TODO: camelCase
return options;
}
} }
} }

View File

@ -3,8 +3,6 @@
<PropertyGroup> <PropertyGroup>
<Description>Implements the SignalR Hub Protocol using Newtonsoft.Json.</Description> <Description>Implements the SignalR Hub Protocol using Newtonsoft.Json.</Description>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<!-- TODO: remove this from the shared framework once we have a JSON protocol implementation which doesn't use Newtonsoft.Json. -->
<IsAspNetCoreApp>true</IsAspNetCoreApp>
<RootNamespace>Microsoft.AspNetCore.SignalR</RootNamespace> <RootNamespace>Microsoft.AspNetCore.SignalR</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsShippingPackage>true</IsShippingPackage> <IsShippingPackage>true</IsShippingPackage>

View File

@ -9,6 +9,7 @@ using System.Linq;
using System.Text; using System.Text;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Protocol;
using Microsoft.Extensions.Options;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
@ -17,6 +18,17 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
{ {
protected override IHubProtocol JsonHubProtocol => new JsonHubProtocol(); protected override IHubProtocol JsonHubProtocol => new JsonHubProtocol();
protected override IHubProtocol GetProtocolWithOptions(bool useCamelCase, bool ignoreNullValues)
{
var protocolOptions = new JsonHubProtocolOptions()
{
IgnoreNullValues = ignoreNullValues,
//TODO: camelCase
};
return new JsonHubProtocol(Options.Create(protocolOptions));
}
[Theory] [Theory]
[InlineData("", "Error reading JSON.")] [InlineData("", "Error reading JSON.")]
[InlineData("42", "Unexpected JSON Token Type 'Number'. Expected a JSON Object.")] [InlineData("42", "Unexpected JSON Token Type 'Number'. Expected a JSON Object.")]
@ -42,7 +54,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
var writer = MemoryBufferWriter.Get(); var writer = MemoryBufferWriter.Get();
try try
{ {
JsonHubProtocol.WriteMessage(testData.Message, writer); var protocol = GetProtocolWithOptions(testData.UseCamelCase, testData.IgnoreNullValues);
protocol.WriteMessage(testData.Message, writer);
var json = Encoding.UTF8.GetString(writer.ToArray()); var json = Encoding.UTF8.GetString(writer.ToArray());
Assert.Equal(expectedOutput, json); Assert.Equal(expectedOutput, json);
@ -63,7 +76,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
var binder = new TestBinder(testData.Message); var binder = new TestBinder(testData.Message);
var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(input)); var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(input));
JsonHubProtocol.TryParseMessage(ref data, binder, out var message); var protocol = GetProtocolWithOptions(testData.UseCamelCase, testData.IgnoreNullValues);
protocol.TryParseMessage(ref data, binder, out var message);
Assert.Equal(testData.Message, message, TestHubMessageEqualityComparer.Instance); Assert.Equal(testData.Message, message, TestHubMessageEqualityComparer.Instance);
} }
@ -83,11 +97,11 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
public static IDictionary<string, JsonProtocolTestData> CustomProtocolTestData => new[] public static IDictionary<string, JsonProtocolTestData> CustomProtocolTestData => new[]
{ {
new JsonProtocolTestData("InvocationMessage_HasFloatArgument", new InvocationMessage(null, "Target", new object[] { 1, "Foo", 2.0f }), "{\"type\":1,\"target\":\"Target\",\"arguments\":[1,\"Foo\",2]}"), new JsonProtocolTestData("InvocationMessage_HasFloatArgument", new InvocationMessage(null, "Target", new object[] { 1, "Foo", 2.0f }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[1,\"Foo\",2]}"),
new JsonProtocolTestData("StreamItemMessage_HasFloatItem", new StreamItemMessage("123", 2.0f), "{\"type\":2,\"invocationId\":\"123\",\"item\":2}"), new JsonProtocolTestData("StreamItemMessage_HasFloatItem", new StreamItemMessage("123", 2.0f), true, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":2}"),
new JsonProtocolTestData("CompletionMessage_HasFloatResult", CompletionMessage.WithResult("123", 2.0f), "{\"type\":3,\"invocationId\":\"123\",\"result\":2}"), new JsonProtocolTestData("CompletionMessage_HasFloatResult", CompletionMessage.WithResult("123", 2.0f), true, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":2}"),
new JsonProtocolTestData("StreamInvocationMessage_HasFloatArgument", new StreamInvocationMessage("123", "Target", new object[] { 1, "Foo", 2.0f }), "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2]}"), new JsonProtocolTestData("StreamInvocationMessage_HasFloatArgument", new StreamInvocationMessage("123", "Target", new object[] { 1, "Foo", 2.0f }), true, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2]}"),
new JsonProtocolTestData("InvocationMessage_StringIsoDateArgument", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20\\u002b12:34\"]}"), new JsonProtocolTestData("InvocationMessage_StringIsoDateArgument", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), true, true, "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20\\u002b12:34\"]}"),
}.ToDictionary(t => t.Name); }.ToDictionary(t => t.Name);
public static IEnumerable<object[]> CustomProtocolTestDataNames => CustomProtocolTestData.Keys.Select(name => new object[] { name }); public static IEnumerable<object[]> CustomProtocolTestDataNames => CustomProtocolTestData.Keys.Select(name => new object[] { name });

View File

@ -19,6 +19,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
{ {
protected abstract IHubProtocol JsonHubProtocol { get; } protected abstract IHubProtocol JsonHubProtocol { get; }
protected abstract IHubProtocol GetProtocolWithOptions(bool useCamelCase, bool ignoreNullValues);
public static readonly IDictionary<string, string> TestHeaders = new Dictionary<string, string> public static readonly IDictionary<string, string> TestHeaders = new Dictionary<string, string>
{ {
{ "Foo", "Bar" }, { "Foo", "Bar" },
@ -31,18 +33,18 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
public static IDictionary<string, JsonProtocolTestData> ProtocolTestData => new[] public static IDictionary<string, JsonProtocolTestData> ProtocolTestData => new[]
{ {
new JsonProtocolTestData("InvocationMessage_HasInvocationId", new InvocationMessage("123", "Target", new object[] { 1, "Foo" }), "{\"type\":1,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\"]}"), new JsonProtocolTestData("InvocationMessage_HasInvocationId", new InvocationMessage("123", "Target", new object[] { 1, "Foo" }), true, true, "{\"type\":1,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\"]}"),
new JsonProtocolTestData("InvocationMessage_HasBoolArgument", new InvocationMessage(null, "Target", new object[] { true }), "{\"type\":1,\"target\":\"Target\",\"arguments\":[true]}"), new JsonProtocolTestData("InvocationMessage_HasBoolArgument", new InvocationMessage(null, "Target", new object[] { true }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[true]}"),
new JsonProtocolTestData("InvocationMessage_HasNullArgument", new InvocationMessage(null, "Target", new object[] { null }), "{\"type\":1,\"target\":\"Target\",\"arguments\":[null]}"), new JsonProtocolTestData("InvocationMessage_HasNullArgument", new InvocationMessage(null, "Target", new object[] { null }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[null]}"),
new JsonProtocolTestData("InvocationMessage_HasStreamArgument", new InvocationMessage(null, "Target", Array.Empty<object>(), new string[] { "__test_id__" }), "{\"type\":1,\"target\":\"Target\",\"arguments\":[],\"streamIds\":[\"__test_id__\"]}"), new JsonProtocolTestData("InvocationMessage_HasStreamArgument", new InvocationMessage(null, "Target", Array.Empty<object>(), new string[] { "__test_id__" }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[],\"streamIds\":[\"__test_id__\"]}"),
new JsonProtocolTestData("InvocationMessage_HasStreamAndNormalArgument", new InvocationMessage(null, "Target", new object[] { 42 }, new string[] { "__test_id__" }), "{\"type\":1,\"target\":\"Target\",\"arguments\":[42],\"streamIds\":[\"__test_id__\"]}"), new JsonProtocolTestData("InvocationMessage_HasStreamAndNormalArgument", new InvocationMessage(null, "Target", new object[] { 42 }, new string[] { "__test_id__" }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[42],\"streamIds\":[\"__test_id__\"]}"),
new JsonProtocolTestData("InvocationMessage_HasMultipleStreams", new InvocationMessage(null, "Target", Array.Empty<object>(), new string[] { "__test_id__", "__test_id2__" }), "{\"type\":1,\"target\":\"Target\",\"arguments\":[],\"streamIds\":[\"__test_id__\",\"__test_id2__\"]}"), new JsonProtocolTestData("InvocationMessage_HasMultipleStreams", new InvocationMessage(null, "Target", Array.Empty<object>(), new string[] { "__test_id__", "__test_id2__" }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[],\"streamIds\":[\"__test_id__\",\"__test_id2__\"]}"),
new JsonProtocolTestData("InvocationMessage_DateTimeOffsetArgument", new InvocationMessage("Method", new object[] { DateTimeOffset.Parse("2016-05-10T13:51:20+12:34") }), "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20+12:34\"]}"), new JsonProtocolTestData("InvocationMessage_DateTimeOffsetArgument", new InvocationMessage("Method", new object[] { DateTimeOffset.Parse("2016-05-10T13:51:20+12:34") }), true, true, "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20+12:34\"]}"),
new JsonProtocolTestData("StreamItemMessage_HasIntegerItem", new StreamItemMessage("123", 1), "{\"type\":2,\"invocationId\":\"123\",\"item\":1}"), new JsonProtocolTestData("StreamItemMessage_HasIntegerItem", new StreamItemMessage("123", 1), true, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":1}"),
new JsonProtocolTestData("StreamItemMessage_HasStringItem", new StreamItemMessage("123", "Foo"), "{\"type\":2,\"invocationId\":\"123\",\"item\":\"Foo\"}"), new JsonProtocolTestData("StreamItemMessage_HasStringItem", new StreamItemMessage("123", "Foo"), true, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":\"Foo\"}"),
new JsonProtocolTestData("StreamItemMessage_HasBoolItem", new StreamItemMessage("123", true), "{\"type\":2,\"invocationId\":\"123\",\"item\":true}"), new JsonProtocolTestData("StreamItemMessage_HasBoolItem", new StreamItemMessage("123", true), true, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":true}"),
new JsonProtocolTestData("StreamItemMessage_HasNullItem", new StreamItemMessage("123", null), "{\"type\":2,\"invocationId\":\"123\",\"item\":null}"), new JsonProtocolTestData("StreamItemMessage_HasNullItem", new StreamItemMessage("123", null), true, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":null}"),
// Dictionary not supported yet // Dictionary not supported yet
//new JsonProtocolTestData("StreamItemMessage_HasHeaders", AddHeaders(TestHeaders, new StreamItemMessage("123", new CustomObject())), "{\"type\":2," + SerializedHeaders + ",\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":[1,2,3]}}"), //new JsonProtocolTestData("StreamItemMessage_HasHeaders", AddHeaders(TestHeaders, new StreamItemMessage("123", new CustomObject())), "{\"type\":2," + SerializedHeaders + ",\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":[1,2,3]}}"),
@ -50,25 +52,26 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
//new JsonProtocolTestData("StreamInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() })), "{\"type\":4," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":[1,2,3]}]}"), //new JsonProtocolTestData("StreamInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() })), "{\"type\":4," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":[1,2,3]}]}"),
//new JsonProtocolTestData("CancelInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new CancelInvocationMessage("123")), "{\"type\":5," + SerializedHeaders + ",\"invocationId\":\"123\"}"), //new JsonProtocolTestData("CancelInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new CancelInvocationMessage("123")), "{\"type\":5," + SerializedHeaders + ",\"invocationId\":\"123\"}"),
new JsonProtocolTestData("CompletionMessage_HasIntegerResult", CompletionMessage.WithResult("123", 1), "{\"type\":3,\"invocationId\":\"123\",\"result\":1}"), new JsonProtocolTestData("CompletionMessage_HasIntegerResult", CompletionMessage.WithResult("123", 1), true, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":1}"),
new JsonProtocolTestData("CompletionMessage_HasStringResult", CompletionMessage.WithResult("123", "Foo"), "{\"type\":3,\"invocationId\":\"123\",\"result\":\"Foo\"}"), new JsonProtocolTestData("CompletionMessage_HasStringResult", CompletionMessage.WithResult("123", "Foo"), true, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":\"Foo\"}"),
new JsonProtocolTestData("CompletionMessage_HasBoolResult", CompletionMessage.WithResult("123", true), "{\"type\":3,\"invocationId\":\"123\",\"result\":true}"), new JsonProtocolTestData("CompletionMessage_HasBoolResult", CompletionMessage.WithResult("123", true), true, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":true}"),
new JsonProtocolTestData("CompletionMessage_HasNullResult", CompletionMessage.WithResult("123", null), "{\"type\":3,\"invocationId\":\"123\",\"result\":null}"), new JsonProtocolTestData("CompletionMessage_HasNullResult", CompletionMessage.WithResult("123", null), true, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":null}"),
new JsonProtocolTestData("CompletionMessage_HasError", CompletionMessage.WithError("123", "Whoops!"), "{\"type\":3,\"invocationId\":\"123\",\"error\":\"Whoops!\"}"), new JsonProtocolTestData("CompletionMessage_HasError", CompletionMessage.WithError("123", "Whoops!"), true, true, "{\"type\":3,\"invocationId\":\"123\",\"error\":\"Whoops!\"}"),
new JsonProtocolTestData("CompletionMessage_HasErrorAndHeaders", AddHeaders(TestHeaders, CompletionMessage.WithError("123", "Whoops!")), "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\",\"error\":\"Whoops!\"}"), new JsonProtocolTestData("CompletionMessage_HasErrorAndHeaders", AddHeaders(TestHeaders, CompletionMessage.WithError("123", "Whoops!")), true, true, "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\",\"error\":\"Whoops!\"}"),
new JsonProtocolTestData("StreamInvocationMessage_HasInvocationId", new StreamInvocationMessage("123", "Target", new object[] { 1, "Foo" }), "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\"]}"), new JsonProtocolTestData("StreamInvocationMessage_HasInvocationId", new StreamInvocationMessage("123", "Target", new object[] { 1, "Foo" }), true, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\"]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasBoolArgument", new StreamInvocationMessage("123", "Target", new object[] { true }), "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[true]}"), new JsonProtocolTestData("StreamInvocationMessage_HasBoolArgument", new StreamInvocationMessage("123", "Target", new object[] { true }), true, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[true]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasNullArgument", new StreamInvocationMessage("123", "Target", new object[] { null }), "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[null]}"), new JsonProtocolTestData("StreamInvocationMessage_HasNullArgument", new StreamInvocationMessage("123", "Target", new object[] { null }), true, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[null]}"),
new JsonProtocolTestData("StreamInvocationMessage_HasStreamArgument", new StreamInvocationMessage("123", "Target", Array.Empty<object>(), new string[] { "__test_id__" }), "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[],\"streamIds\":[\"__test_id__\"]}"), new JsonProtocolTestData("StreamInvocationMessage_HasStreamArgument", new StreamInvocationMessage("123", "Target", Array.Empty<object>(), new string[] { "__test_id__" }), true, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[],\"streamIds\":[\"__test_id__\"]}"),
new JsonProtocolTestData("CancelInvocationMessage_HasInvocationId", new CancelInvocationMessage("123"), "{\"type\":5,\"invocationId\":\"123\"}"), new JsonProtocolTestData("CancelInvocationMessage_HasInvocationId", new CancelInvocationMessage("123"), true, true, "{\"type\":5,\"invocationId\":\"123\"}"),
new JsonProtocolTestData("PingMessage", PingMessage.Instance, "{\"type\":6}"), new JsonProtocolTestData("PingMessage", PingMessage.Instance, true, true, "{\"type\":6}"),
new JsonProtocolTestData("CloseMessage", CloseMessage.Empty, "{\"type\":7}"), new JsonProtocolTestData("CloseMessage", CloseMessage.Empty, false, true, "{\"type\":7}"),
new JsonProtocolTestData("CloseMessage_HasError", new CloseMessage("Error!"), "{\"type\":7,\"error\":\"Error!\"}"), new JsonProtocolTestData("CloseMessage_HasError", new CloseMessage("Error!"), false, true, "{\"type\":7,\"error\":\"Error!\"}"),
new JsonProtocolTestData("CloseMessage_HasErrorEmptyString", new CloseMessage(""), "{\"type\":7,\"error\":\"\"}"), new JsonProtocolTestData("CloseMessage_HasErrorEmptyString", new CloseMessage(""), false, true, "{\"type\":7,\"error\":\"\"}"),
new JsonProtocolTestData("CloseMessage_HasErrorWithCamelCase", new CloseMessage("Error!"), true, true, "{\"type\":7,\"error\":\"Error!\"}"),
}.ToDictionary(t => t.Name); }.ToDictionary(t => t.Name);
@ -76,13 +79,12 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
public static IDictionary<string, JsonProtocolTestData> OutOfOrderJsonTestData => new[] public static IDictionary<string, JsonProtocolTestData> OutOfOrderJsonTestData => new[]
{ {
new JsonProtocolTestData("InvocationMessage_StringIsoDateArgumentFirst", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), "{ \"arguments\": [\"2016-05-10T13:51:20+12:34\"], \"type\":1, \"target\": \"Method\" }"), new JsonProtocolTestData("InvocationMessage_StringIsoDateArgumentFirst", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), false, true, "{ \"arguments\": [\"2016-05-10T13:51:20+12:34\"], \"type\":1, \"target\": \"Method\" }"),
//new JsonProtocolTestData("InvocationMessage_StringIsoDateArgumentFirst", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), false, "{ \"arguments\": [\"2016-05-10T13:51:20+12:34\"], \"type\":1, \"target\": \"Method\" }"), new JsonProtocolTestData("InvocationMessage_DateTimeOffsetArgumentFirst", new InvocationMessage("Method", new object[] { DateTimeOffset.Parse("2016-05-10T13:51:20+12:34") }), false, true, "{ \"arguments\": [\"2016-05-10T13:51:20+12:34\"], \"type\":1, \"target\": \"Method\" }"),
//new JsonProtocolTestData("InvocationMessage_DateTimeOffsetArgumentFirst", new InvocationMessage("Method", new object[] { DateTimeOffset.Parse("2016-05-10T13:51:20+12:34") }), false, "{ \"arguments\": [\"2016-05-10T13:51:20+12:34\"], \"type\":1, \"target\": \"Method\" }"), new JsonProtocolTestData("InvocationMessage_IntegerArrayArgumentFirst", new InvocationMessage("Method", new object[] { 1, 2 }), false, true, "{ \"arguments\": [1,2], \"type\":1, \"target\": \"Method\" }"),
new JsonProtocolTestData("InvocationMessage_IntegerArrayArgumentFirst", new InvocationMessage("Method", new object[] { 1, 2 }), "{ \"arguments\": [1,2], \"type\":1, \"target\": \"Method\" }"), new JsonProtocolTestData("StreamInvocationMessage_IntegerArrayArgumentFirst", new StreamInvocationMessage("3", "Method", new object[] { 1, 2 }), false, true, "{ \"type\":4, \"arguments\": [1,2], \"target\": \"Method\", \"invocationId\": \"3\" }"),
new JsonProtocolTestData("StreamInvocationMessage_IntegerArrayArgumentFirst", new StreamInvocationMessage("3", "Method", new object[] { 1, 2 }), "{ \"type\":4, \"arguments\": [1,2], \"target\": \"Method\", \"invocationId\": \"3\" }"), new JsonProtocolTestData("CompletionMessage_ResultFirst", new CompletionMessage("15", null, 10, hasResult: true), false, true, "{ \"type\":3, \"result\": 10, \"invocationId\": \"15\" }"),
new JsonProtocolTestData("CompletionMessage_ResultFirst", new CompletionMessage("15", null, 10, hasResult: true), "{ \"type\":3, \"result\": 10, \"invocationId\": \"15\" }"), new JsonProtocolTestData("StreamItemMessage_ItemFirst", new StreamItemMessage("1a", "foo"), false, true, "{ \"item\": \"foo\", \"invocationId\": \"1a\", \"type\":2 }")
new JsonProtocolTestData("StreamItemMessage_ItemFirst", new StreamItemMessage("1a", "foo"), "{ \"item\": \"foo\", \"invocationId\": \"1a\", \"type\":2 }")
}.ToDictionary(t => t.Name); }.ToDictionary(t => t.Name);
@ -99,7 +101,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
var writer = MemoryBufferWriter.Get(); var writer = MemoryBufferWriter.Get();
try try
{ {
JsonHubProtocol.WriteMessage(testData.Message, writer); var protocol = GetProtocolWithOptions(testData.UseCamelCase, testData.IgnoreNullValues);
protocol.WriteMessage(testData.Message, writer);
var json = Encoding.UTF8.GetString(writer.ToArray()); var json = Encoding.UTF8.GetString(writer.ToArray());
Assert.Equal(expectedOutput, json); Assert.Equal(expectedOutput, json);
@ -120,7 +123,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
var binder = new TestBinder(testData.Message); var binder = new TestBinder(testData.Message);
var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(input)); var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(input));
JsonHubProtocol.TryParseMessage(ref data, binder, out var message); var protocol = GetProtocolWithOptions(testData.UseCamelCase, testData.IgnoreNullValues);
protocol.TryParseMessage(ref data, binder, out var message);
Assert.Equal(testData.Message, message, TestHubMessageEqualityComparer.Instance); Assert.Equal(testData.Message, message, TestHubMessageEqualityComparer.Instance);
} }
@ -179,7 +183,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
var binder = new TestBinder(testData.Message); var binder = new TestBinder(testData.Message);
var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(input)); var data = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(input));
JsonHubProtocol.TryParseMessage(ref data, binder, out var message); var protocol = GetProtocolWithOptions(testData.UseCamelCase, testData.IgnoreNullValues);
protocol.TryParseMessage(ref data, binder, out var message);
Assert.Equal(testData.Message, message, TestHubMessageEqualityComparer.Instance); Assert.Equal(testData.Message, message, TestHubMessageEqualityComparer.Instance);
} }
@ -205,7 +210,8 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
[InlineData("{\"type\":4,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[ \"abc\", \"xyz\"]}", "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.")] [InlineData("{\"type\":4,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[ \"abc\", \"xyz\"]}", "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.")]
[InlineData("{\"type\":1,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[1,\"\",{\"1\":1,\"2\":2}]}", "Invocation provides 3 argument(s) but target expects 2.")] [InlineData("{\"type\":1,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[1,\"\",{\"1\":1,\"2\":2}]}", "Invocation provides 3 argument(s) but target expects 2.")]
[InlineData("{\"type\":1,\"arguments\":[1,\"\",{\"1\":1,\"2\":2}]},\"invocationId\":\"42\",\"target\":\"foo\"", "Invocation provides 3 argument(s) but target expects 2.")] [InlineData("{\"type\":1,\"arguments\":[1,\"\",{\"1\":1,\"2\":2}]},\"invocationId\":\"42\",\"target\":\"foo\"", "Invocation provides 3 argument(s) but target expects 2.")]
[InlineData("{\"type\":1,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[1,[1]]}", "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.")] // Both of these should be fixed by https://github.com/dotnet/corefx/issues/36901
// [InlineData("{\"type\":1,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[1,[1]]}", "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.")]
// [InlineData("{\"type\":1,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[1,[]]}", "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.")] // [InlineData("{\"type\":1,\"invocationId\":\"42\",\"target\":\"foo\",\"arguments\":[1,[]]}", "Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked.")]
public void ArgumentBindingErrors(string input, string expectedMessage) public void ArgumentBindingErrors(string input, string expectedMessage)
{ {
@ -277,12 +283,16 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
public string Name { get; } public string Name { get; }
public HubMessage Message { get; } public HubMessage Message { get; }
public string Json { get; } public string Json { get; }
public bool UseCamelCase { get; }
public bool IgnoreNullValues { get; }
public JsonProtocolTestData(string name, HubMessage message, string json) public JsonProtocolTestData(string name, HubMessage message, bool useCamelCase, bool ignoreNullValues, string json)
{ {
Name = name; Name = name;
Message = message; Message = message;
Json = json; Json = json;
UseCamelCase = useCamelCase;
IgnoreNullValues = ignoreNullValues;
} }
public override string ToString() => Name; public override string ToString() => Name;

View File

@ -22,6 +22,20 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
{ {
protected override IHubProtocol JsonHubProtocol => new NewtonsoftJsonHubProtocol(); protected override IHubProtocol JsonHubProtocol => new NewtonsoftJsonHubProtocol();
protected override IHubProtocol GetProtocolWithOptions(bool useCamelCase, bool ignoreNullValues)
{
var protocolOptions = new NewtonsoftJsonHubProtocolOptions
{
PayloadSerializerSettings = new JsonSerializerSettings()
{
NullValueHandling = ignoreNullValues ? NullValueHandling.Ignore : NullValueHandling.Include,
ContractResolver = useCamelCase ? new CamelCasePropertyNamesContractResolver() : new DefaultContractResolver()
}
};
return new NewtonsoftJsonHubProtocol(Options.Create(protocolOptions));
}
[Theory] [Theory]
[InlineData("", "Unexpected end when reading JSON.")] [InlineData("", "Unexpected end when reading JSON.")]
[InlineData("42", "Unexpected JSON Token Type 'Integer'. Expected a JSON Object.")] [InlineData("42", "Unexpected JSON Token Type 'Integer'. Expected a JSON Object.")]
@ -44,16 +58,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
var expectedOutput = Frame(testData.Json); var expectedOutput = Frame(testData.Json);
var protocolOptions = new NewtonsoftJsonHubProtocolOptions var protocol = GetProtocolWithOptions(testData.UseCamelCase, testData.IgnoreNullValues);
{
PayloadSerializerSettings = new JsonSerializerSettings()
{
NullValueHandling = testData.NullValueHandling,
ContractResolver = testData.CamelCase ? new CamelCasePropertyNamesContractResolver() : new DefaultContractResolver()
}
};
var protocol = new NewtonsoftJsonHubProtocol(Options.Create(protocolOptions));
var writer = MemoryBufferWriter.Get(); var writer = MemoryBufferWriter.Get();
try try
@ -69,49 +74,36 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Protocol
} }
} }
public static IDictionary<string, NewtonsoftJsonProtocolTestData> CustomProtocolTestData => new[] public static IDictionary<string, JsonProtocolTestData> CustomProtocolTestData => new[]
{ {
new NewtonsoftJsonProtocolTestData("InvocationMessage_HasFloatArgument", new InvocationMessage(null, "Target", new object[] { 1, "Foo", 2.0f }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"), new JsonProtocolTestData("InvocationMessage_HasFloatArgument", new InvocationMessage(null, "Target", new object[] { 1, "Foo", 2.0f }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"),
new NewtonsoftJsonProtocolTestData("StreamItemMessage_HasFloatItem", new StreamItemMessage("123", 2.0f), true, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":2.0}"), new JsonProtocolTestData("StreamItemMessage_HasFloatItem", new StreamItemMessage("123", 2.0f), true, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":2.0}"),
new NewtonsoftJsonProtocolTestData("CompletionMessage_HasFloatResult", CompletionMessage.WithResult("123", 2.0f), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":2.0}"), new JsonProtocolTestData("CompletionMessage_HasFloatResult", CompletionMessage.WithResult("123", 2.0f), true, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":2.0}"),
new NewtonsoftJsonProtocolTestData("StreamInvocationMessage_HasFloatArgument", new StreamInvocationMessage("123", "Target", new object[] { 1, "Foo", 2.0f }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"), new JsonProtocolTestData("StreamInvocationMessage_HasFloatArgument", new StreamInvocationMessage("123", "Target", new object[] { 1, "Foo", 2.0f }), true, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[1,\"Foo\",2.0]}"),
new NewtonsoftJsonProtocolTestData("InvocationMessage_StringIsoDateArgument", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), false, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20+12:34\"]}"), new JsonProtocolTestData("InvocationMessage_StringIsoDateArgument", new InvocationMessage("Method", new object[] { "2016-05-10T13:51:20+12:34" }), false, true, "{\"type\":1,\"target\":\"Method\",\"arguments\":[\"2016-05-10T13:51:20+12:34\"]}"),
new NewtonsoftJsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNoCamelCase", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), false, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}]}"), new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNoCamelCase", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), false, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}]}"),
new NewtonsoftJsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnore", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), true, NullValueHandling.Ignore, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}]}"), new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnore", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), true, true, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}]}"),
new NewtonsoftJsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), false, NullValueHandling.Include, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"), new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), false, false, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"),
new NewtonsoftJsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueInclude", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), true, NullValueHandling.Include, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"), new JsonProtocolTestData("InvocationMessage_HasCustomArgumentWithNullValueInclude", new InvocationMessage(null, "Target", new object[] { new CustomObject() }), true, false, "{\"type\":1,\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
new NewtonsoftJsonProtocolTestData("StreamItemMessage_HasCustomItemWithNoCamelCase", new StreamItemMessage("123", new CustomObject()), false, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}}"), new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNoCamelCase", new StreamItemMessage("123", new CustomObject()), false, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}}"),
new NewtonsoftJsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueIgnore", new StreamItemMessage("123", new CustomObject()), true, NullValueHandling.Ignore, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}}"), new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueIgnore", new StreamItemMessage("123", new CustomObject()), true, true, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}}"),
new NewtonsoftJsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueIgnoreAndNoCamelCase", new StreamItemMessage("123", new CustomObject()), false, NullValueHandling.Include, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}}"), new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueIgnoreAndNoCamelCase", new StreamItemMessage("123", new CustomObject()), false, false, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}}"),
new NewtonsoftJsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueInclude", new StreamItemMessage("123", new CustomObject()), true, NullValueHandling.Include, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"), new JsonProtocolTestData("StreamItemMessage_HasCustomItemWithNullValueInclude", new StreamItemMessage("123", new CustomObject()), true, false, "{\"type\":2,\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
new NewtonsoftJsonProtocolTestData("StreamItemMessage_HasHeaders", AddHeaders(TestHeaders, new StreamItemMessage("123", new CustomObject())), true, NullValueHandling.Include, "{\"type\":2," + SerializedHeaders + ",\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"), new JsonProtocolTestData("StreamItemMessage_HasHeaders", AddHeaders(TestHeaders, new StreamItemMessage("123", new CustomObject())), true, false, "{\"type\":2," + SerializedHeaders + ",\"invocationId\":\"123\",\"item\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
new NewtonsoftJsonProtocolTestData("CompletionMessage_HasCustomResultWithNoCamelCase", CompletionMessage.WithResult("123", new CustomObject()), false, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}}"), new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNoCamelCase", CompletionMessage.WithResult("123", new CustomObject()), false, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}}"),
new NewtonsoftJsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueIgnore", CompletionMessage.WithResult("123", new CustomObject()), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}}"), new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueIgnore", CompletionMessage.WithResult("123", new CustomObject()), true, true, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}}"),
new NewtonsoftJsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueIncludeAndNoCamelCase", CompletionMessage.WithResult("123", new CustomObject()), false, NullValueHandling.Include, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}}"), new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueIncludeAndNoCamelCase", CompletionMessage.WithResult("123", new CustomObject()), false, false, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}}"),
new NewtonsoftJsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueInclude", CompletionMessage.WithResult("123", new CustomObject()), true, NullValueHandling.Include, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"), new JsonProtocolTestData("CompletionMessage_HasCustomResultWithNullValueInclude", CompletionMessage.WithResult("123", new CustomObject()), true, false, "{\"type\":3,\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
new NewtonsoftJsonProtocolTestData("CompletionMessage_HasTestHeadersAndCustomItemResult", AddHeaders(TestHeaders, CompletionMessage.WithResult("123", new CustomObject())), true, NullValueHandling.Include, "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"), new JsonProtocolTestData("CompletionMessage_HasTestHeadersAndCustomItemResult", AddHeaders(TestHeaders, CompletionMessage.WithResult("123", new CustomObject())), true, false, "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\",\"result\":{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}}"),
new NewtonsoftJsonProtocolTestData("CompletionMessage_HasErrorAndCamelCase", CompletionMessage.Empty("123"), true, NullValueHandling.Ignore, "{\"type\":3,\"invocationId\":\"123\"}"), new JsonProtocolTestData("CompletionMessage_HasErrorAndCamelCase", CompletionMessage.Empty("123"), true, true, "{\"type\":3,\"invocationId\":\"123\"}"),
new NewtonsoftJsonProtocolTestData("CompletionMessage_HasErrorAndHeadersAndCamelCase", AddHeaders(TestHeaders, CompletionMessage.Empty("123")), true, NullValueHandling.Ignore, "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\"}"), new JsonProtocolTestData("CompletionMessage_HasErrorAndHeadersAndCamelCase", AddHeaders(TestHeaders, CompletionMessage.Empty("123")), true, true, "{\"type\":3," + SerializedHeaders + ",\"invocationId\":\"123\"}"),
new NewtonsoftJsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNoCamelCase", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), false, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}]}"), new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNoCamelCase", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), false, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"ByteArrProp\":\"AQID\"}]}"),
new NewtonsoftJsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnore", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), true, NullValueHandling.Ignore, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}]}"), new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnore", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), true, true, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"byteArrProp\":\"AQID\"}]}"),
new NewtonsoftJsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), false, NullValueHandling.Include, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"), new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueIgnoreAndNoCamelCase", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), false, false, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"StringProp\":\"SignalR!\",\"DoubleProp\":6.2831853071,\"IntProp\":42,\"DateTimeProp\":\"2017-04-11T00:00:00Z\",\"NullProp\":null,\"ByteArrProp\":\"AQID\"}]}"),
new NewtonsoftJsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueInclude", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), true, NullValueHandling.Include, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"), new JsonProtocolTestData("StreamInvocationMessage_HasCustomArgumentWithNullValueInclude", new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() }), true, false, "{\"type\":4,\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
new NewtonsoftJsonProtocolTestData("StreamInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() })), true, NullValueHandling.Include, "{\"type\":4," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"), new JsonProtocolTestData("StreamInvocationMessage_HasHeaders", AddHeaders(TestHeaders, new StreamInvocationMessage("123", "Target", new object[] { new CustomObject() })), true, false, "{\"type\":4," + SerializedHeaders + ",\"invocationId\":\"123\",\"target\":\"Target\",\"arguments\":[{\"stringProp\":\"SignalR!\",\"doubleProp\":6.2831853071,\"intProp\":42,\"dateTimeProp\":\"2017-04-11T00:00:00Z\",\"nullProp\":null,\"byteArrProp\":\"AQID\"}]}"),
new NewtonsoftJsonProtocolTestData("CloseMessage_HasErrorWithCamelCase", new CloseMessage("Error!"), true, NullValueHandling.Ignore, "{\"type\":7,\"error\":\"Error!\"}"),
}.ToDictionary(t => t.Name); }.ToDictionary(t => t.Name);
public static IEnumerable<object[]> CustomProtocolTestDataNames => CustomProtocolTestData.Keys.Select(name => new object[] { name }); public static IEnumerable<object[]> CustomProtocolTestDataNames => CustomProtocolTestData.Keys.Select(name => new object[] { name });
public class NewtonsoftJsonProtocolTestData : JsonProtocolTestData
{
public NewtonsoftJsonProtocolTestData(string name, HubMessage message, bool camelCase, NullValueHandling nullValueHandling, string json) : base(name, message, json)
{
CamelCase = camelCase;
NullValueHandling = nullValueHandling;
}
public bool CamelCase { get; }
public NullValueHandling NullValueHandling { get; }
}
} }
} }

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
{ {
return new HubConnectionContext(connection, TimeSpan.FromSeconds(15), NullLoggerFactory.Instance) return new HubConnectionContext(connection, TimeSpan.FromSeconds(15), NullLoggerFactory.Instance)
{ {
Protocol = protocol ?? new NewtonsoftJsonHubProtocol(), Protocol = protocol ?? new JsonHubProtocol(),
UserIdentifier = userIdentifier, UserIdentifier = userIdentifier,
}; };
} }

View File

@ -7,7 +7,7 @@
<Compile Include="Microsoft.AspNetCore.SignalR.Core.netcoreapp3.0.cs" /> <Compile Include="Microsoft.AspNetCore.SignalR.Core.netcoreapp3.0.cs" />
<Reference Include="Microsoft.AspNetCore.Authorization" /> <Reference Include="Microsoft.AspNetCore.Authorization" />
<Reference Include="Microsoft.AspNetCore.SignalR.Common" /> <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
<Reference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" /> <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" />
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" /> <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions" /> <Reference Include="Microsoft.Extensions.Logging.Abstractions" />
<Reference Include="System.Threading.Channels" /> <Reference Include="System.Threading.Channels" />

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>Real-time communication framework for ASP.NET Core.</Description> <Description>Real-time communication framework for ASP.NET Core.</Description>
@ -17,7 +17,7 @@
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.AspNetCore.Authorization" /> <Reference Include="Microsoft.AspNetCore.Authorization" />
<Reference Include="Microsoft.AspNetCore.SignalR.Common" /> <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
<Reference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" /> <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" />
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" /> <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions" /> <Reference Include="Microsoft.Extensions.Logging.Abstractions" />
<Reference Include="System.Threading.Channels" /> <Reference Include="System.Threading.Channels" />

View File

@ -32,7 +32,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddAuthorization(); services.AddAuthorization();
var builder = new SignalRServerBuilder(services); var builder = new SignalRServerBuilder(services);
builder.AddNewtonsoftJsonProtocol(); builder.AddJsonProtocol();
return builder; return builder;
} }
} }

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
return Clients.User(userId).SendAsync("Send", message); return Clients.User(userId).SendAsync("Send", message);
} }
public Task SendToMultipleUsers(List<string> userIds, string message) public Task SendToMultipleUsers(IReadOnlyList<string> userIds, string message)
{ {
return Clients.Users(userIds).SendAsync("Send", message); return Clients.Users(userIds).SendAsync("Send", message);
} }
@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
return Clients.Client(connectionId).SendAsync("Send", message); return Clients.Client(connectionId).SendAsync("Send", message);
} }
public Task SendToMultipleClients(string message, List<string> connectionIds) public Task SendToMultipleClients(string message, IReadOnlyList<string> connectionIds)
{ {
return Clients.Clients(connectionIds).SendAsync("Send", message); return Clients.Clients(connectionIds).SendAsync("Send", message);
} }
@ -48,12 +48,12 @@ namespace Microsoft.AspNetCore.SignalR.Tests
return Clients.Group(groupName).SendAsync("Send", message); return Clients.Group(groupName).SendAsync("Send", message);
} }
public Task GroupExceptSendMethod(string groupName, string message, List<string> excludedConnectionIds) public Task GroupExceptSendMethod(string groupName, string message, IReadOnlyList<string> excludedConnectionIds)
{ {
return Clients.GroupExcept(groupName, excludedConnectionIds).SendAsync("Send", message); return Clients.GroupExcept(groupName, excludedConnectionIds).SendAsync("Send", message);
} }
public Task SendToMultipleGroups(string message, List<string> groupNames) public Task SendToMultipleGroups(string message, IReadOnlyList<string> groupNames)
{ {
return Clients.Groups(groupNames).SendAsync("Send", message); return Clients.Groups(groupNames).SendAsync("Send", message);
} }
@ -142,7 +142,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
{ {
} }
public Task SendToAllExcept(string message, List<string> excludedConnectionIds) public Task SendToAllExcept(string message, IReadOnlyList<string> excludedConnectionIds)
{ {
return Clients.AllExcept(excludedConnectionIds).SendAsync("Send", message); return Clients.AllExcept(excludedConnectionIds).SendAsync("Send", message);
} }
@ -313,7 +313,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
return Clients.Client(connectionId).Send(message); return Clients.Client(connectionId).Send(message);
} }
public Task SendToMultipleClients(string message, List<string> connectionIds) public Task SendToMultipleClients(string message, IReadOnlyList<string> connectionIds)
{ {
return Clients.Clients(connectionIds).Send(message); return Clients.Clients(connectionIds).Send(message);
} }
@ -328,7 +328,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
return Clients.Group(groupName).Send(message); return Clients.Group(groupName).Send(message);
} }
public Task GroupExceptSendMethod(string groupName, string message, List<string> excludedConnectionIds) public Task GroupExceptSendMethod(string groupName, string message, IReadOnlyList<string> excludedConnectionIds)
{ {
return Clients.GroupExcept(groupName, excludedConnectionIds).Send(message); return Clients.GroupExcept(groupName, excludedConnectionIds).Send(message);
} }
@ -338,7 +338,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
return Clients.OthersInGroup(groupName).Send(message); return Clients.OthersInGroup(groupName).Send(message);
} }
public Task SendToMultipleGroups(string message, List<string> groupNames) public Task SendToMultipleGroups(string message, IReadOnlyList<string> groupNames)
{ {
return Clients.Groups(groupNames).Send(message); return Clients.Groups(groupNames).Send(message);
} }
@ -348,7 +348,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
return Clients.All.Broadcast(message); return Clients.All.Broadcast(message);
} }
public Task SendToAllExcept(string message, List<string> excludedConnectionIds) public Task SendToAllExcept(string message, IReadOnlyList<string> excludedConnectionIds)
{ {
return Clients.AllExcept(excludedConnectionIds).Send(message); return Clients.AllExcept(excludedConnectionIds).Send(message);
} }
@ -393,7 +393,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
return Clients.Client(connectionId).Send(message); return Clients.Client(connectionId).Send(message);
} }
public Task SendToMultipleClients(string message, List<string> connectionIds) public Task SendToMultipleClients(string message, IReadOnlyList<string> connectionIds)
{ {
return Clients.Clients(connectionIds).Send(message); return Clients.Clients(connectionIds).Send(message);
} }
@ -414,12 +414,12 @@ namespace Microsoft.AspNetCore.SignalR.Tests
return Clients.Group(groupName).Send(message); return Clients.Group(groupName).Send(message);
} }
public Task GroupExceptSendMethod(string groupName, string message, List<string> excludedConnectionIds) public Task GroupExceptSendMethod(string groupName, string message, IReadOnlyList<string> excludedConnectionIds)
{ {
return Clients.GroupExcept(groupName, excludedConnectionIds).Send(message); return Clients.GroupExcept(groupName, excludedConnectionIds).Send(message);
} }
public Task SendToMultipleGroups(string message, List<string> groupNames) public Task SendToMultipleGroups(string message, IReadOnlyList<string> groupNames)
{ {
return Clients.Groups(groupNames).Send(message); return Clients.Groups(groupNames).Send(message);
} }
@ -434,7 +434,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
return Clients.All.Broadcast(message); return Clients.All.Broadcast(message);
} }
public Task SendToAllExcept(string message, List<string> excludedConnectionIds) public Task SendToAllExcept(string message, IReadOnlyList<string> excludedConnectionIds)
{ {
return Clients.AllExcept(excludedConnectionIds).Send(message); return Clients.AllExcept(excludedConnectionIds).Send(message);
} }

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>Tests for users to verify their own implementations of SignalR types</Description> <Description>Tests for users to verify their own implementations of SignalR types</Description>
@ -19,6 +19,7 @@
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.AspNetCore.SignalR.Common" /> <Reference Include="Microsoft.AspNetCore.SignalR.Common" />
<Reference Include="Microsoft.AspNetCore.SignalR.Core" /> <Reference Include="Microsoft.AspNetCore.SignalR.Core" />
<Reference Include="Microsoft.AspNetCore.SignalR.Protocols.Json" />
<Reference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" /> <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" />
<Reference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" /> <Reference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" />
<Reference Include="xunit.assert" /> <Reference Include="xunit.assert" />

View File

@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="$(SignalRSharedSourceRoot)JsonUtils.cs" Link="Internal\JsonUtils.cs" /> <Compile Include="$(SignalRSharedSourceRoot)SystemTextJsonExtensions.cs" Link="Internal\SystemTextJsonExtensions.cs" />
<Compile Include="$(SignalRSharedSourceRoot)MemoryBufferWriter.cs" Link="Internal\MemoryBufferWriter.cs" /> <Compile Include="$(SignalRSharedSourceRoot)MemoryBufferWriter.cs" Link="Internal\MemoryBufferWriter.cs" />
</ItemGroup> </ItemGroup>