diff --git a/src/Nancy.Demo.Razor.Localization/DemoBoostrapper.cs b/src/Nancy.Demo.Razor.Localization/DemoBootstrapper.cs similarity index 82% rename from src/Nancy.Demo.Razor.Localization/DemoBoostrapper.cs rename to src/Nancy.Demo.Razor.Localization/DemoBootstrapper.cs index c0e05df367..b9ca5cf058 100644 --- a/src/Nancy.Demo.Razor.Localization/DemoBoostrapper.cs +++ b/src/Nancy.Demo.Razor.Localization/DemoBootstrapper.cs @@ -2,7 +2,7 @@ { using Bootstrapper; - public class DemoBoostrapper : DefaultNancyBootstrapper + public class DemoBootstrapper : DefaultNancyBootstrapper { protected override NancyInternalConfiguration InternalConfiguration { diff --git a/src/Nancy.Demo.Razor.Localization/Nancy.Demo.Razor.Localization.csproj b/src/Nancy.Demo.Razor.Localization/Nancy.Demo.Razor.Localization.csproj index ea94cd3273..8c501d0622 100644 --- a/src/Nancy.Demo.Razor.Localization/Nancy.Demo.Razor.Localization.csproj +++ b/src/Nancy.Demo.Razor.Localization/Nancy.Demo.Razor.Localization.csproj @@ -80,7 +80,7 @@ - + True diff --git a/src/Nancy.Tests/Nancy.Tests.csproj b/src/Nancy.Tests/Nancy.Tests.csproj index 4ea8173b4a..a0e4b5bcf3 100644 --- a/src/Nancy.Tests/Nancy.Tests.csproj +++ b/src/Nancy.Tests/Nancy.Tests.csproj @@ -156,6 +156,7 @@ + diff --git a/src/Nancy.Tests/Unit/Json/JavaScriptSerializerFixture.cs b/src/Nancy.Tests/Unit/Json/JavaScriptSerializerFixture.cs index 31a3a5143e..015b732775 100644 --- a/src/Nancy.Tests/Unit/Json/JavaScriptSerializerFixture.cs +++ b/src/Nancy.Tests/Unit/Json/JavaScriptSerializerFixture.cs @@ -1,134 +1,181 @@ namespace Nancy.Tests.Unit.Json { - using System; - using System.Collections.Generic; - using System.IO; - using FakeItEasy; - using Nancy.IO; - using Nancy.Json; - using Xunit; - using Xunit.Extensions; - using Xunit.Sdk; - - public class JavaScriptSerializerFixture - { - [Fact] - public void Should_register_converters_when_asked() - { - // Given - JsonSettings.Converters.Add(new TestConverter()); - JsonSettings.PrimitiveConverters.Add(new TestPrimitiveConverter()); - - var defaultSerializer = new JavaScriptSerializer(); - - // When - var serializer = new JavaScriptSerializer( - registerConverters: true, - resolver: null, - maxJsonLength: defaultSerializer.MaxJsonLength, - recursionLimit: defaultSerializer.RecursionLimit, - retainCasing: defaultSerializer.RetainCasing, - iso8601DateFormat: defaultSerializer.ISO8601DateFormat); - - var data = - new TestData() - { - ConverterData = - new TestConverterType() - { - Data = 42, - }, - - PrimitiveConverterData = - new TestPrimitiveConverterType() - { - Data = 1701, - }, - }; - - const string ExpectedJSON = @"{""converterData"":{""dataValue"":42},""primitiveConverterData"":1701}"; - - // Then - serializer.Serialize(data).ShouldEqual(ExpectedJSON); - - serializer.Deserialize(ExpectedJSON).ShouldEqual(data); - } - - [Fact] - public void Should_not_register_converters_when_not_asked() - { - // Given - JsonSettings.Converters.Add(new TestConverter()); - JsonSettings.PrimitiveConverters.Add(new TestPrimitiveConverter()); - - var defaultSerializer = new JavaScriptSerializer(); - - // When - var serializer = new JavaScriptSerializer( - registerConverters: false, - resolver: null, - maxJsonLength: defaultSerializer.MaxJsonLength, - recursionLimit: defaultSerializer.RecursionLimit, - retainCasing: defaultSerializer.RetainCasing, - iso8601DateFormat: defaultSerializer.ISO8601DateFormat); - - var data = - new TestData() - { - ConverterData = - new TestConverterType() - { - Data = 42, - }, - - PrimitiveConverterData = - new TestPrimitiveConverterType() - { - Data = 1701, - }, - }; - - const string ExpectedJSON = @"{""converterData"":{""data"":42},""primitiveConverterData"":{""data"":1701}}"; - - // Then - serializer.Serialize(data).ShouldEqual(ExpectedJSON); - - serializer.Deserialize(ExpectedJSON).ShouldEqual(data); - } - - [Fact] - public void Should_use_primitive_converter_when_available() - { - // When - var serializer = new JavaScriptSerializer(); - - serializer.RegisterConverters(new JavaScriptPrimitiveConverter[] { new TestPrimitiveConverter() }); - - // Then - serializer.Serialize(new TestPrimitiveConverterType() { Data = 12345 }).ShouldEqual("12345"); - - serializer.Deserialize("12345").ShouldEqual(new TestPrimitiveConverterType() { Data = 12345 }); - } - - [Fact] - public void Should_not_use_primitive_converter_for_wrong_type() - { - // When - var serializer = new JavaScriptSerializer(); - - serializer.RegisterConverters(new JavaScriptPrimitiveConverter[] { new TestPrimitiveConverter() }); - - // Then - serializer.Serialize(new TestConverterType() { Data = 12345 }).ShouldEqual(@"{""data"":12345}"); - - serializer.Deserialize(@"{""data"":12345}").ShouldEqual(new TestConverterType() { Data = 12345 }); - - try - { - serializer.Deserialize("12345"); - throw new ThrowsException(typeof(InvalidCastException)); - } - catch { } - } - } + using System; + using System.Collections.Generic; + using System.IO; + using FakeItEasy; + using Nancy.IO; + using Nancy.Json; + using Nancy.Json.Converters; + + using Xunit; + using Xunit.Extensions; + using Xunit.Sdk; + + public class JavaScriptSerializerFixture + { + [Fact] + public void Should_register_converters_when_asked() + { + // Given + JsonSettings.Converters.Add(new TestConverter()); + JsonSettings.PrimitiveConverters.Add(new TestPrimitiveConverter()); + + var defaultSerializer = new JavaScriptSerializer(); + + // When + var serializer = new JavaScriptSerializer( + registerConverters: true, + resolver: null, + maxJsonLength: defaultSerializer.MaxJsonLength, + recursionLimit: defaultSerializer.RecursionLimit, + retainCasing: defaultSerializer.RetainCasing, + iso8601DateFormat: defaultSerializer.ISO8601DateFormat); + + var data = + new TestData() + { + ConverterData = + new TestConverterType() + { + Data = 42, + }, + + PrimitiveConverterData = + new TestPrimitiveConverterType() + { + Data = 1701, + }, + }; + + const string ExpectedJSON = @"{""converterData"":{""dataValue"":42},""primitiveConverterData"":1701}"; + + // Then + serializer.Serialize(data).ShouldEqual(ExpectedJSON); + + serializer.Deserialize(ExpectedJSON).ShouldEqual(data); + } + + [Fact] + public void Should_not_register_converters_when_not_asked() + { + // Given + JsonSettings.Converters.Add(new TestConverter()); + JsonSettings.PrimitiveConverters.Add(new TestPrimitiveConverter()); + + var defaultSerializer = new JavaScriptSerializer(); + + // When + var serializer = new JavaScriptSerializer( + registerConverters: false, + resolver: null, + maxJsonLength: defaultSerializer.MaxJsonLength, + recursionLimit: defaultSerializer.RecursionLimit, + retainCasing: defaultSerializer.RetainCasing, + iso8601DateFormat: defaultSerializer.ISO8601DateFormat); + + var data = + new TestData() + { + ConverterData = + new TestConverterType() + { + Data = 42, + }, + + PrimitiveConverterData = + new TestPrimitiveConverterType() + { + Data = 1701, + }, + }; + + const string ExpectedJSON = @"{""converterData"":{""data"":42},""primitiveConverterData"":{""data"":1701}}"; + + // Then + serializer.Serialize(data).ShouldEqual(ExpectedJSON); + + serializer.Deserialize(ExpectedJSON).ShouldEqual(data); + } + + [Fact] + public void Should_use_primitive_converter_when_available() + { + // When + var serializer = new JavaScriptSerializer(); + + serializer.RegisterConverters(new JavaScriptPrimitiveConverter[] { new TestPrimitiveConverter() }); + + // Then + serializer.Serialize(new TestPrimitiveConverterType() { Data = 12345 }).ShouldEqual("12345"); + + serializer.Deserialize("12345").ShouldEqual(new TestPrimitiveConverterType() { Data = 12345 }); + } + + [Fact] + public void Should_not_use_primitive_converter_for_wrong_type() + { + // When + var serializer = new JavaScriptSerializer(); + + serializer.RegisterConverters(new JavaScriptPrimitiveConverter[] { new TestPrimitiveConverter() }); + + // Then + serializer.Serialize(new TestConverterType() { Data = 12345 }).ShouldEqual(@"{""data"":12345}"); + + serializer.Deserialize(@"{""data"":12345}").ShouldEqual(new TestConverterType() { Data = 12345 }); + + try + { + serializer.Deserialize("12345"); + throw new ThrowsException(typeof(InvalidCastException)); + } + catch { } + } + + [Fact] + public void Should_serialize_tuples() + { + var serializer = new JavaScriptSerializer(); + serializer.RegisterConverters(new[] { new TupleConverter() }); + + var tuple = Tuple.Create(10, 11); + serializer.Serialize(tuple).ShouldEqual(@"{""item1"":10,""item2"":11}"); + } + + [Fact] + public void Should_deserialize_tuple() + { + var serializer = new JavaScriptSerializer(); + serializer.RegisterConverters(new[] { new TupleConverter() }); + + string body = @"{""item1"":10,""item2"":11}"; + Tuple result = serializer.Deserialize>(body); + result.ToString().ShouldEqual("(10, 11)"); + } + + [Fact] + public void Should_deserialize_string_tuple() + { + var serializer = new JavaScriptSerializer(); + serializer.RegisterConverters(new[] { new TupleConverter() }); + + string body = @"{""item1"":""Hello"",""item2"":""World"",""item3"":42}"; + var result = serializer.Deserialize>(body); + result.ToString().ShouldEqual("(Hello, World, 42)"); + } + + [Fact] + public void Should_deserialize_type_with_tuples() + { + // When + var serializer = new JavaScriptSerializer(); + serializer.RegisterConverters(new[] { new TupleConverter() }); + + // Then + var typeWithTuple = serializer.Deserialize(@"{""value"":{""item1"":10,""item2"":11}}"); + typeWithTuple.Value.Item1.ShouldEqual(10); + typeWithTuple.Value.Item2.ShouldEqual(11); + } + } } diff --git a/src/Nancy.Tests/Unit/Json/TypeWithTuple.cs b/src/Nancy.Tests/Unit/Json/TypeWithTuple.cs new file mode 100644 index 0000000000..c790b15b3a --- /dev/null +++ b/src/Nancy.Tests/Unit/Json/TypeWithTuple.cs @@ -0,0 +1,9 @@ +namespace Nancy.Tests.Unit.Json +{ + using System; + + public class TypeWithTuple + { + public Tuple Value { get; set; } + } +} \ No newline at end of file diff --git a/src/Nancy.Tests/Unit/Responses/EmbeddedFileResponseFixture.cs b/src/Nancy.Tests/Unit/Responses/EmbeddedFileResponseFixture.cs index 9ea9abdf71..3b545d01c8 100644 --- a/src/Nancy.Tests/Unit/Responses/EmbeddedFileResponseFixture.cs +++ b/src/Nancy.Tests/Unit/Responses/EmbeddedFileResponseFixture.cs @@ -14,7 +14,7 @@ public void Should_contain_etag_in_response_header_if_embedded_resource_exists() new EmbeddedFileResponse(this.GetType().Assembly, "Nancy.Tests", "Resources.Views.staticviewresource.html"); // Then - response.Headers["ETag"].ShouldEqual("\"5D6EFDFDB135DC90F16D57E05603DA1E\""); + response.Headers["ETag"].ShouldEqual("\"B9D9DC2B50ADFD0867749D4837C63556339080CE\""); } [Fact] @@ -30,7 +30,7 @@ public void Should_contain_etag_in_response_header_if_embedded_resource_exists_w response.Contents.Invoke(outputStream); // Then - response.Headers["ETag"].ShouldEqual("\"5D6EFDFDB135DC90F16D57E05603DA1E\""); + response.Headers["ETag"].ShouldEqual("\"B9D9DC2B50ADFD0867749D4837C63556339080CE\""); } [Fact] diff --git a/src/Nancy/Bootstrapper/NancyBootstrapperBase.cs b/src/Nancy/Bootstrapper/NancyBootstrapperBase.cs index fccc43030c..15539869ed 100755 --- a/src/Nancy/Bootstrapper/NancyBootstrapperBase.cs +++ b/src/Nancy/Bootstrapper/NancyBootstrapperBase.cs @@ -244,6 +244,9 @@ public void Initialise() this.ConfigureApplicationContainer(this.ApplicationContainer); + // We need to call this to fix an issue with assemblies that are referenced by DI not being loaded + AppDomainAssemblyTypeScanner.UpdateTypes(); + var typeRegistrations = this.InternalConfiguration.GetTypeRegistations() .Concat(this.GetAdditionalTypes()); diff --git a/src/Nancy/Bootstrapper/NancyBootstrapperLocator.cs b/src/Nancy/Bootstrapper/NancyBootstrapperLocator.cs index 668bf50c5b..e87dde9bfb 100755 --- a/src/Nancy/Bootstrapper/NancyBootstrapperLocator.cs +++ b/src/Nancy/Bootstrapper/NancyBootstrapperLocator.cs @@ -6,7 +6,7 @@ /// /// Class for locating an INancyBootstrapper implementation. - /// + /// /// Will search the app domain for a non-abstract one, and if it can't find one /// it will use the default nancy one that uses TinyIoC. /// @@ -70,7 +70,7 @@ internal static bool TryFindMostDerivedType(List customBootstrappers, out var set = new HashSet(); bootstrapper = null; - if (customBootstrappers.All(boostrapper => set.Add(boostrapper.BaseType))) + if (customBootstrappers.All(b => set.Add(b.BaseType))) { var except = customBootstrappers.Except(set).ToList(); bootstrapper = except.Count == 1 ? except[0] : null; diff --git a/src/Nancy/DefaultNancyBootstrapper.cs b/src/Nancy/DefaultNancyBootstrapper.cs index 9a114a51b5..bb7bb3f594 100755 --- a/src/Nancy/DefaultNancyBootstrapper.cs +++ b/src/Nancy/DefaultNancyBootstrapper.cs @@ -1,12 +1,10 @@ -using Nancy.Diagnostics; - namespace Nancy { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; - + using Nancy.Diagnostics; using Bootstrapper; using Nancy.TinyIoc; @@ -256,4 +254,4 @@ private static void AutoRegister(TinyIoCContainer container, IEnumerable !ignoredAssemblies.Any(ia => ia(a))), DuplicateImplementationActions.RegisterMultiple, t => t.Assembly != assembly); } } -} \ No newline at end of file +} diff --git a/src/Nancy/Json/Converters/TupleConverter.cs b/src/Nancy/Json/Converters/TupleConverter.cs new file mode 100644 index 0000000000..de24306693 --- /dev/null +++ b/src/Nancy/Json/Converters/TupleConverter.cs @@ -0,0 +1,37 @@ +namespace Nancy.Json.Converters +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + + public class TupleConverter : JavaScriptConverter + { + public override IEnumerable SupportedTypes + { + get + { + yield return typeof(Tuple<>); + yield return typeof(Tuple<,>); + yield return typeof(Tuple<,,>); + yield return typeof(Tuple<,,,>); + yield return typeof(Tuple<,,,,>); + yield return typeof(Tuple<,,,,,>); + yield return typeof(Tuple<,,,,,,>); + yield return typeof(Tuple<,,,,,,,>); + } + } + + public override object Deserialize(IDictionary dictionary, Type type, JavaScriptSerializer serializer) + { + var ctor = type.GetConstructors().First(); + object instance = ctor.Invoke(dictionary.Values.ToArray()); + return instance; + } + + public override IDictionary Serialize(object obj, JavaScriptSerializer serializer) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/Nancy/Json/JavaScriptSerializer.cs b/src/Nancy/Json/JavaScriptSerializer.cs index e2e7455d10..f4084e9d9f 100644 --- a/src/Nancy/Json/JavaScriptSerializer.cs +++ b/src/Nancy/Json/JavaScriptSerializer.cs @@ -339,6 +339,14 @@ object ConvertToObject(IDictionary dict, Type type) isDictionaryWithGuidKey = arguments[0] == typeof(Guid); } + else + { + var converter = GetConverter(genericTypeDefinition); + if (converter != null) + return converter.Deserialize( + EvaluateDictionary(dict), + type, this); + } } else if (type.IsAssignableFrom(typeof(IDictionary))) type = typeof(Dictionary); diff --git a/src/Nancy/Json/JsonSettings.cs b/src/Nancy/Json/JsonSettings.cs index bee8bc38cb..de36595abd 100644 --- a/src/Nancy/Json/JsonSettings.cs +++ b/src/Nancy/Json/JsonSettings.cs @@ -48,6 +48,7 @@ static JsonSettings() Converters = new List { new TimeSpanConverter(), + new TupleConverter() }; PrimitiveConverters = new List(); RetainCasing = false; diff --git a/src/Nancy/Nancy.csproj b/src/Nancy/Nancy.csproj index c23f85ed01..c54954a8e2 100644 --- a/src/Nancy/Nancy.csproj +++ b/src/Nancy/Nancy.csproj @@ -175,6 +175,7 @@ + diff --git a/src/Nancy/Request.cs b/src/Nancy/Request.cs index e1d4cdaa9d..f24ffb5c99 100644 --- a/src/Nancy/Request.cs +++ b/src/Nancy/Request.cs @@ -38,7 +38,7 @@ public Request(string method, string path, string scheme) /// Initializes a new instance of the class. /// /// The HTTP data transfer method used by the client. - /// The url of the requested resource + /// The of the requested resource /// The headers that was passed in by the client. /// The that represents the incoming HTTP body. /// The client's IP address diff --git a/src/Nancy/Responses/EmbeddedFileResponse.cs b/src/Nancy/Responses/EmbeddedFileResponse.cs index d53f7360d8..5a9e7d7285 100644 --- a/src/Nancy/Responses/EmbeddedFileResponse.cs +++ b/src/Nancy/Responses/EmbeddedFileResponse.cs @@ -64,9 +64,9 @@ private static string GetFileNameFromResourceName(string resourcePath, string re private static string GenerateETag(Stream stream) { - using (var md5 = MD5.Create()) + using (var sha1 = new SHA1CryptoServiceProvider()) { - var hash = md5.ComputeHash(stream); + var hash = sha1.ComputeHash(stream); return string.Concat("\"", ByteArrayToString(hash), "\""); } } diff --git a/src/Nancy/Routing/RouteDescription.cs b/src/Nancy/Routing/RouteDescription.cs index 68043c640a..20a2ec7f96 100644 --- a/src/Nancy/Routing/RouteDescription.cs +++ b/src/Nancy/Routing/RouteDescription.cs @@ -19,12 +19,12 @@ public RouteDescription(string name, string method, string path, Func /// Compiled RegEx for path expansion in attribute values /// - private static readonly Regex AttributeValuePathExpansionRegEx = new Regex(@"(?[a-zA-Z]+)=(?[""|'])(?~.+?)\k", RegexOptions.Compiled); + private static readonly Regex AttributeValuePathExpansionRegEx = new Regex(@"(?[a-zA-Z]+)=(?[""'])(?~.+?)\k", RegexOptions.Compiled); /// /// Compiled RegEx for the CSRF anti forgery token diff --git a/tools/nuget/NuGet.exe b/tools/nuget/NuGet.exe index 8dd7e45ae7..9ca66594f9 100644 Binary files a/tools/nuget/NuGet.exe and b/tools/nuget/NuGet.exe differ