From 84aa3d1f4e511d24ec0e3bd523177ededc4f6f7b Mon Sep 17 00:00:00 2001 From: Cyprien Autexier Date: Tue, 27 Sep 2016 18:14:09 +0200 Subject: [PATCH 01/28] add gitversion config --- GitVersion.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 GitVersion.yml diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 0000000..cbb5d94 --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1,9 @@ +branches: + dev(elop)?(ment)?$: + tag: alpha + features?[/-]: + tag: alpha.{BranchName} + releases?[/-]: + mode: ContinuousDeployment + hotfix(es)?[/-]: + mode: ContinuousDeployment \ No newline at end of file From 7dd44131d763ac0bd3c861b1d01447c5eaaa927c Mon Sep 17 00:00:00 2001 From: Cyprien Autexier Date: Tue, 27 Sep 2016 19:06:53 +0200 Subject: [PATCH 02/28] add response to unhandled api exception --- src/GeekLearning.RestKit.Core/UnhandledApiException.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/GeekLearning.RestKit.Core/UnhandledApiException.cs b/src/GeekLearning.RestKit.Core/UnhandledApiException.cs index b6a7035..4ddb47a 100644 --- a/src/GeekLearning.RestKit.Core/UnhandledApiException.cs +++ b/src/GeekLearning.RestKit.Core/UnhandledApiException.cs @@ -8,9 +8,12 @@ namespace GeekLearning.RestKit.Core { public class UnhandledApiException : ApiException { - public UnhandledApiException(HttpResponseMessage message) - { + public UnhandledApiException(HttpResponseMessage response) + { + this.Response = response; } + + public HttpResponseMessage Response { get; private set; } } } From c639a57e68f214d2fe67553abff08738068d2eed Mon Sep 17 00:00:00 2001 From: Cyprien Autexier Date: Wed, 7 Dec 2016 11:14:05 +1100 Subject: [PATCH 03/28] add first HttpHandlerFactory primitives --- src/GeekLearning.RestKit.Core/ClientBase.cs | 7 +++++++ .../CustomHandlerHttpClientFactory.cs | 12 ++++++++++++ .../DefaultHttpClientFactory.cs | 11 +++++++++++ src/GeekLearning.RestKit.Core/IHttpClientFactory.cs | 9 +++++++++ src/GeekLearning.RestKit.Core/RestKitExtensions.cs | 10 ++++++++++ 5 files changed, 49 insertions(+) create mode 100644 src/GeekLearning.RestKit.Core/CustomHandlerHttpClientFactory.cs create mode 100644 src/GeekLearning.RestKit.Core/DefaultHttpClientFactory.cs create mode 100644 src/GeekLearning.RestKit.Core/IHttpClientFactory.cs diff --git a/src/GeekLearning.RestKit.Core/ClientBase.cs b/src/GeekLearning.RestKit.Core/ClientBase.cs index 6073014..d25d08d 100644 --- a/src/GeekLearning.RestKit.Core/ClientBase.cs +++ b/src/GeekLearning.RestKit.Core/ClientBase.cs @@ -16,19 +16,26 @@ public abstract class ClientBase private IMediaFormatterProvider mediaFormatterProvider; private IServiceProvider serviceProvider; private Lazy requestFilters; + private IHttpClientFactory httpClientFactory; public ClientBase(IOptions options, + IHttpClientFactory httpClientFactory, IMediaFormatterProvider mediaFormatterProvider, IServiceProvider serviceProvider) { this.Options = options.Value; this.mediaFormatterProvider = mediaFormatterProvider; this.serviceProvider = serviceProvider; + this.httpClientFactory = httpClientFactory; this.requestFilters = new Lazy(() => this.Options.RequestFilters.Select(x=> ActivatorUtilities.CreateInstance(this.serviceProvider, x.Type, x.Arguments)).Cast().ToArray() ); } + protected HttpClient GetClient(){ + return this.httpClientFactory.CreateClient(); + } + protected TOptions Options { get; private set; } protected Task TransformResponseAsync(HttpResponseMessage message) diff --git a/src/GeekLearning.RestKit.Core/CustomHandlerHttpClientFactory.cs b/src/GeekLearning.RestKit.Core/CustomHandlerHttpClientFactory.cs new file mode 100644 index 0000000..2577714 --- /dev/null +++ b/src/GeekLearning.RestKit.Core/CustomHandlerHttpClientFactory.cs @@ -0,0 +1,12 @@ +using System.Net.Http; + +namespace GeekLearning.RestKit.Core +{ + public class CustomHandlerHttpClientFactory : IHttpClientFactory + where THandler: HttpMessageHandler, new() + { + public HttpClient CreateClient(){ + return new HttpClient(new THandler()); + } + } +} diff --git a/src/GeekLearning.RestKit.Core/DefaultHttpClientFactory.cs b/src/GeekLearning.RestKit.Core/DefaultHttpClientFactory.cs new file mode 100644 index 0000000..c411320 --- /dev/null +++ b/src/GeekLearning.RestKit.Core/DefaultHttpClientFactory.cs @@ -0,0 +1,11 @@ +using System.Net.Http; + +namespace GeekLearning.RestKit.Core +{ + public class DefaultHttpClientFactory: IHttpClientFactory + { + public HttpClient CreateClient(){ + return new HttpClient(); + } + } +} diff --git a/src/GeekLearning.RestKit.Core/IHttpClientFactory.cs b/src/GeekLearning.RestKit.Core/IHttpClientFactory.cs new file mode 100644 index 0000000..9094b70 --- /dev/null +++ b/src/GeekLearning.RestKit.Core/IHttpClientFactory.cs @@ -0,0 +1,9 @@ +using System.Net.Http; + +namespace GeekLearning.RestKit.Core +{ + public interface IHttpClientFactory + { + HttpClient CreateClient(); + } +} diff --git a/src/GeekLearning.RestKit.Core/RestKitExtensions.cs b/src/GeekLearning.RestKit.Core/RestKitExtensions.cs index 10d85fa..a442351 100644 --- a/src/GeekLearning.RestKit.Core/RestKitExtensions.cs +++ b/src/GeekLearning.RestKit.Core/RestKitExtensions.cs @@ -11,6 +11,16 @@ public static class RestKitExtensions { public static IRestKitServicesBuilder AddRestKit(this IServiceCollection services) { + services.TryAddSingleton(); + services.TryAddSingleton(); + + return new Internal.RestKitServicesBuilder(services); + } + + public static IRestKitServicesBuilder AddRestKit(this IServiceCollection services) + where THttpClientFactory : class, IHttpClientFactory + { + services.TryAddSingleton(); services.TryAddSingleton(); return new Internal.RestKitServicesBuilder(services); From 981cedab122b9281491d550d84ab3055abff5120 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Fri, 9 Dec 2016 15:01:39 +1100 Subject: [PATCH 04/28] add http logging primitives --- GeekLearning.RestKit.sln | 6 ++ .../GeekLearning.Http.Logging.xproj | 21 +++++++ .../HttpRequestLogger.cs | 59 +++++++++++++++++++ .../Properties/AssemblyInfo.cs | 19 ++++++ src/GeekLearning.Http.Logging/project.json | 16 +++++ 5 files changed, 121 insertions(+) create mode 100644 src/GeekLearning.Http.Logging/GeekLearning.Http.Logging.xproj create mode 100644 src/GeekLearning.Http.Logging/HttpRequestLogger.cs create mode 100644 src/GeekLearning.Http.Logging/Properties/AssemblyInfo.cs create mode 100644 src/GeekLearning.Http.Logging/project.json diff --git a/GeekLearning.RestKit.sln b/GeekLearning.RestKit.sln index ef0d638..983814b 100644 --- a/GeekLearning.RestKit.sln +++ b/GeekLearning.RestKit.sln @@ -7,6 +7,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "GeekLearning.RestKit.Core", EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "GeekLearning.RestKit.Json", "src\GeekLearning.RestKit.Json\GeekLearning.RestKit.Json.xproj", "{9C387D89-0D16-4279-8B94-37D60DAA7FF5}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "GeekLearning.Http.Logging", "src\GeekLearning.Http.Logging\GeekLearning.Http.Logging.xproj", "{380EE612-647B-4CDD-B491-B9B4AE7E1479}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {9C387D89-0D16-4279-8B94-37D60DAA7FF5}.Debug|Any CPU.Build.0 = Debug|Any CPU {9C387D89-0D16-4279-8B94-37D60DAA7FF5}.Release|Any CPU.ActiveCfg = Release|Any CPU {9C387D89-0D16-4279-8B94-37D60DAA7FF5}.Release|Any CPU.Build.0 = Release|Any CPU + {380EE612-647B-4CDD-B491-B9B4AE7E1479}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {380EE612-647B-4CDD-B491-B9B4AE7E1479}.Debug|Any CPU.Build.0 = Debug|Any CPU + {380EE612-647B-4CDD-B491-B9B4AE7E1479}.Release|Any CPU.ActiveCfg = Release|Any CPU + {380EE612-647B-4CDD-B491-B9B4AE7E1479}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/GeekLearning.Http.Logging/GeekLearning.Http.Logging.xproj b/src/GeekLearning.Http.Logging/GeekLearning.Http.Logging.xproj new file mode 100644 index 0000000..5df76bd --- /dev/null +++ b/src/GeekLearning.Http.Logging/GeekLearning.Http.Logging.xproj @@ -0,0 +1,21 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 380ee612-647b-4cdd-b491-b9b4ae7e1479 + GeekLearning.Http.Logging + .\obj + .\bin\ + v4.5.2 + + + + 2.0 + + + diff --git a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs new file mode 100644 index 0000000..c674442 --- /dev/null +++ b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Net.Http; +using System.Threading; +using Microsoft.Extensions.Logging; +using GeekLearning.D64; + +namespace GeekLearning.Http.Logging +{ + public class HttpRequestLogger : DelegatingHandler + { + private ILogger logger; + TimebasedId timebaseId; + + protected HttpRequestLogger(HttpMessageHandler innerHandler, ILogger logger) : base(innerHandler) + { + this.timebaseId = new TimebasedId(false); + this.logger = logger; + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var correlationId = timebaseId.NewId(); + + var requestBody = request.Content.ReadAsStringAsync(); + this.logger.LogInformation( + "`{0}` to `{1}` with correlationId `{2}`:\n{3}\n\n{4}", + request.Method, + request.RequestUri, + correlationId, + string.Join("\n", request.Headers.Select(h => $"{h.Key}: {h.Value}")), + requestBody); + + var response = await base.SendAsync(request, cancellationToken); + var responseBody = response.Content.ReadAsStringAsync(); + + this.logger.LogInformation( + "`RECEIVE` `{0}` `{1}` with correlationId `{2}`:\n{3}\n\n{4}", + response.StatusCode, + response.ReasonPhrase, + correlationId, + string.Join("\n", response.Headers.Select(h => $"{h.Key}: {h.Value}")), + requestBody); + + return response; + } + + } + + public class HttpRequestLogger : HttpRequestLogger + where TInnerHandler : HttpMessageHandler, new() + { + protected HttpRequestLogger(ILogger logger) : base(new TInnerHandler(), logger) + { + } + } +} diff --git a/src/GeekLearning.Http.Logging/Properties/AssemblyInfo.cs b/src/GeekLearning.Http.Logging/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..11edf9e --- /dev/null +++ b/src/GeekLearning.Http.Logging/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GeekLearning.Http.Logging")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("380ee612-647b-4cdd-b491-b9b4ae7e1479")] diff --git a/src/GeekLearning.Http.Logging/project.json b/src/GeekLearning.Http.Logging/project.json new file mode 100644 index 0000000..f874746 --- /dev/null +++ b/src/GeekLearning.Http.Logging/project.json @@ -0,0 +1,16 @@ +{ + "version": "1.0.0-*", + + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "1.1.0", + "NETStandard.Library": "1.6.1", + "GeekLearning.D64": "1.0.0" + }, + + "frameworks": { + "net452": {}, + "netstandard1.3": { + "imports": "dnxcore50" + } + } +} From adc6320ca06186eda02209fbdf7c3e89d7a767a2 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Fri, 9 Dec 2016 15:15:36 +1100 Subject: [PATCH 05/28] make constructors public --- src/GeekLearning.Http.Logging/HttpRequestLogger.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs index c674442..cc8e330 100644 --- a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs +++ b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs @@ -14,7 +14,7 @@ public class HttpRequestLogger : DelegatingHandler private ILogger logger; TimebasedId timebaseId; - protected HttpRequestLogger(HttpMessageHandler innerHandler, ILogger logger) : base(innerHandler) + public HttpRequestLogger(HttpMessageHandler innerHandler, ILogger logger) : base(innerHandler) { this.timebaseId = new TimebasedId(false); this.logger = logger; @@ -52,7 +52,7 @@ protected override async Task SendAsync(HttpRequestMessage public class HttpRequestLogger : HttpRequestLogger where TInnerHandler : HttpMessageHandler, new() { - protected HttpRequestLogger(ILogger logger) : base(new TInnerHandler(), logger) + public HttpRequestLogger(ILogger logger) : base(new TInnerHandler(), logger) { } } From 7b87fcc1cd5c9e62d735c353a99c9e3a26f68e10 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Fri, 9 Dec 2016 16:04:52 +1100 Subject: [PATCH 06/28] Use Load into Buffer --- src/GeekLearning.Http.Logging/HttpRequestLogger.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs index cc8e330..a7dabe2 100644 --- a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs +++ b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs @@ -24,6 +24,7 @@ protected override async Task SendAsync(HttpRequestMessage { var correlationId = timebaseId.NewId(); + await request.Content.LoadIntoBufferAsync(); var requestBody = request.Content.ReadAsStringAsync(); this.logger.LogInformation( "`{0}` to `{1}` with correlationId `{2}`:\n{3}\n\n{4}", @@ -34,6 +35,7 @@ protected override async Task SendAsync(HttpRequestMessage requestBody); var response = await base.SendAsync(request, cancellationToken); + await response.Content.LoadIntoBufferAsync(); var responseBody = response.Content.ReadAsStringAsync(); this.logger.LogInformation( From fc9601ff6ce217520d061bbb1704b35237cb2f27 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Fri, 9 Dec 2016 18:50:48 +1100 Subject: [PATCH 07/28] Get compatibility --- .../HttpRequestLogger.cs | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs index a7dabe2..85f650e 100644 --- a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs +++ b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs @@ -24,15 +24,27 @@ protected override async Task SendAsync(HttpRequestMessage { var correlationId = timebaseId.NewId(); - await request.Content.LoadIntoBufferAsync(); - var requestBody = request.Content.ReadAsStringAsync(); - this.logger.LogInformation( - "`{0}` to `{1}` with correlationId `{2}`:\n{3}\n\n{4}", - request.Method, - request.RequestUri, - correlationId, - string.Join("\n", request.Headers.Select(h => $"{h.Key}: {h.Value}")), - requestBody); + if (request.Content != null) + { + await request.Content.LoadIntoBufferAsync(); + var requestBody = request.Content.ReadAsStringAsync(); + this.logger.LogInformation( + "`{0}` to `{1}` with correlationId `{2}`:\n{3}\n\n{4}", + request.Method, + request.RequestUri, + correlationId, + string.Join("\n", request.Headers.Select(h => $"{h.Key}: {h.Value}")), + requestBody); + } + else + { + this.logger.LogInformation( + "`{0}` to `{1}` with correlationId `{2}`:\n{3}", + request.Method, + request.RequestUri, + correlationId, + string.Join("\n", request.Headers.Select(h => $"{h.Key}: {h.Value}"))); + } var response = await base.SendAsync(request, cancellationToken); await response.Content.LoadIntoBufferAsync(); From 4a185ca0cd9ab4a9565d4543e523f655b069160d Mon Sep 17 00:00:00 2001 From: sandorfr Date: Sat, 10 Dec 2016 15:33:44 +1100 Subject: [PATCH 08/28] fix response logging --- src/GeekLearning.Http.Logging/HttpRequestLogger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs index 85f650e..4f2b470 100644 --- a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs +++ b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs @@ -56,7 +56,7 @@ protected override async Task SendAsync(HttpRequestMessage response.ReasonPhrase, correlationId, string.Join("\n", response.Headers.Select(h => $"{h.Key}: {h.Value}")), - requestBody); + responseBody); return response; } From 358d2ba249cdb766829e33b40b18850da18d03f1 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Thu, 15 Dec 2016 13:03:58 +0100 Subject: [PATCH 09/28] header values format --- src/GeekLearning.Http.Logging/HttpRequestLogger.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs index 4f2b470..6dd2ec8 100644 --- a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs +++ b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs @@ -33,7 +33,7 @@ protected override async Task SendAsync(HttpRequestMessage request.Method, request.RequestUri, correlationId, - string.Join("\n", request.Headers.Select(h => $"{h.Key}: {h.Value}")), + string.Join("\n", request.Headers.Select(h => $"{h.Key}: {string.Join(" ", h.Value)}")), requestBody); } else @@ -43,7 +43,7 @@ protected override async Task SendAsync(HttpRequestMessage request.Method, request.RequestUri, correlationId, - string.Join("\n", request.Headers.Select(h => $"{h.Key}: {h.Value}"))); + string.Join("\n", request.Headers.Select(h => $"{h.Key}: {string.Join(" ", h.Value)}"))); } var response = await base.SendAsync(request, cancellationToken); @@ -55,7 +55,7 @@ protected override async Task SendAsync(HttpRequestMessage response.StatusCode, response.ReasonPhrase, correlationId, - string.Join("\n", response.Headers.Select(h => $"{h.Key}: {h.Value}")), + string.Join("\n", response.Headers.Select(h => $"{h.Key}: {string.Join(" ", h.Value)}")), responseBody); return response; From 71a6569428c4c0130fb172ca473715ef047625fd Mon Sep 17 00:00:00 2001 From: sandorfr Date: Thu, 15 Dec 2016 13:20:38 +0100 Subject: [PATCH 10/28] await read content --- src/GeekLearning.Http.Logging/HttpRequestLogger.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs index 6dd2ec8..9931f4d 100644 --- a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs +++ b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs @@ -27,7 +27,7 @@ protected override async Task SendAsync(HttpRequestMessage if (request.Content != null) { await request.Content.LoadIntoBufferAsync(); - var requestBody = request.Content.ReadAsStringAsync(); + var requestBody = await request.Content.ReadAsStringAsync(); this.logger.LogInformation( "`{0}` to `{1}` with correlationId `{2}`:\n{3}\n\n{4}", request.Method, @@ -48,7 +48,7 @@ protected override async Task SendAsync(HttpRequestMessage var response = await base.SendAsync(request, cancellationToken); await response.Content.LoadIntoBufferAsync(); - var responseBody = response.Content.ReadAsStringAsync(); + var responseBody = await response.Content.ReadAsStringAsync(); this.logger.LogInformation( "`RECEIVE` `{0}` `{1}` with correlationId `{2}`:\n{3}\n\n{4}", From fc4137e4b50da9e6d8102fbc0e52c053dfcfeafb Mon Sep 17 00:00:00 2001 From: sandorfr Date: Thu, 15 Dec 2016 15:09:03 +0100 Subject: [PATCH 11/28] status code as int --- src/GeekLearning.Http.Logging/HttpRequestLogger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs index 9931f4d..b836d60 100644 --- a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs +++ b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs @@ -52,7 +52,7 @@ protected override async Task SendAsync(HttpRequestMessage this.logger.LogInformation( "`RECEIVE` `{0}` `{1}` with correlationId `{2}`:\n{3}\n\n{4}", - response.StatusCode, + (int)response.StatusCode, response.ReasonPhrase, correlationId, string.Join("\n", response.Headers.Select(h => $"{h.Key}: {string.Join(" ", h.Value)}")), From 9d7b3a3c3d6e78f90ccc3a5b017e1719edb042f0 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Tue, 20 Dec 2016 14:34:03 +0100 Subject: [PATCH 12/28] measure timings --- .../HttpRequestLogger.cs | 26 +++++++++++++++++-- .../HttpRequestLoggerOptions.cs | 22 ++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 src/GeekLearning.Http.Logging/HttpRequestLoggerOptions.cs diff --git a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs index b836d60..38fb67c 100644 --- a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs +++ b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs @@ -6,18 +6,25 @@ using System.Threading; using Microsoft.Extensions.Logging; using GeekLearning.D64; +using System.Diagnostics; namespace GeekLearning.Http.Logging { public class HttpRequestLogger : DelegatingHandler { private ILogger logger; + private HttpRequestLoggerOptions options; TimebasedId timebaseId; - public HttpRequestLogger(HttpMessageHandler innerHandler, ILogger logger) : base(innerHandler) + public HttpRequestLogger(HttpMessageHandler innerHandler, ILogger logger) : this(innerHandler, logger, new HttpRequestLoggerOptions()) + { + } + + public HttpRequestLogger(HttpMessageHandler innerHandler, ILogger logger, HttpRequestLoggerOptions options) : base(innerHandler) { this.timebaseId = new TimebasedId(false); this.logger = logger; + this.options = options; } protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) @@ -45,11 +52,26 @@ protected override async Task SendAsync(HttpRequestMessage correlationId, string.Join("\n", request.Headers.Select(h => $"{h.Key}: {string.Join(" ", h.Value)}"))); } - + Stopwatch stopwatch = null; + if (this.options.MeasureRequestTime) + { + stopwatch = new Stopwatch(); + stopwatch.Start(); + } var response = await base.SendAsync(request, cancellationToken); await response.Content.LoadIntoBufferAsync(); var responseBody = await response.Content.ReadAsStringAsync(); + if (stopwatch != null) + { + stopwatch.Stop(); + this.logger.LogInformation( + "`REQUEST` `{0}` ran for `{1}` ms", + correlationId, + stopwatch.Elapsed.TotalMilliseconds.ToString(System.Globalization.CultureInfo.InvariantCulture) + ); + } + this.logger.LogInformation( "`RECEIVE` `{0}` `{1}` with correlationId `{2}`:\n{3}\n\n{4}", (int)response.StatusCode, diff --git a/src/GeekLearning.Http.Logging/HttpRequestLoggerOptions.cs b/src/GeekLearning.Http.Logging/HttpRequestLoggerOptions.cs new file mode 100644 index 0000000..b6d8631 --- /dev/null +++ b/src/GeekLearning.Http.Logging/HttpRequestLoggerOptions.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace GeekLearning.Http.Logging +{ + public class HttpRequestLoggerOptions + { + public HttpRequestLoggerOptions() : this(measureRequestTime: true) + { + + } + + public HttpRequestLoggerOptions(bool measureRequestTime) + { + this.MeasureRequestTime = measureRequestTime; + } + + public bool MeasureRequestTime { get; set; } + } +} From 45f27396e6d0cec8c5f898ad6b36349cb3f29db3 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Fri, 23 Dec 2016 11:13:37 +0100 Subject: [PATCH 13/28] add message body truncate if too large --- .../HttpRequestLogger.cs | 16 ++++++++++++++-- .../HttpRequestLoggerOptions.cs | 2 ++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs index 38fb67c..eb97e95 100644 --- a/src/GeekLearning.Http.Logging/HttpRequestLogger.cs +++ b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs @@ -41,7 +41,7 @@ protected override async Task SendAsync(HttpRequestMessage request.RequestUri, correlationId, string.Join("\n", request.Headers.Select(h => $"{h.Key}: {string.Join(" ", h.Value)}")), - requestBody); + TruncateMessageIfTooBig(requestBody)); } else { @@ -78,11 +78,23 @@ protected override async Task SendAsync(HttpRequestMessage response.ReasonPhrase, correlationId, string.Join("\n", response.Headers.Select(h => $"{h.Key}: {string.Join(" ", h.Value)}")), - responseBody); + TruncateMessageIfTooBig(responseBody)); return response; } + private string TruncateMessageIfTooBig(string body) + { + if (body.Length > options.MaxSize) + { + return body.Substring(0, options.MaxSize / 2) + "<< TRUNCATED IN LOGS >>" + body.Substring(body.Length - options.MaxSize / 2); + } + else + { + return body; + } + } + } public class HttpRequestLogger : HttpRequestLogger diff --git a/src/GeekLearning.Http.Logging/HttpRequestLoggerOptions.cs b/src/GeekLearning.Http.Logging/HttpRequestLoggerOptions.cs index b6d8631..f4ec432 100644 --- a/src/GeekLearning.Http.Logging/HttpRequestLoggerOptions.cs +++ b/src/GeekLearning.Http.Logging/HttpRequestLoggerOptions.cs @@ -18,5 +18,7 @@ public HttpRequestLoggerOptions(bool measureRequestTime) } public bool MeasureRequestTime { get; set; } + + public int MaxSize { get; set; } = 512000; } } From db33c530cb6e888ab1d3867bd64b539f0a66692e Mon Sep 17 00:00:00 2001 From: sandorfr Date: Sun, 25 Dec 2016 18:16:12 +0100 Subject: [PATCH 14/28] multipart/formdata support media type subtype and suffix support #1 --- GeekLearning.RestKit.sln | 12 ++++ global.json | 6 ++ src/GeekLearning.RestKit.Core/ApiException.cs | 7 +++ src/GeekLearning.RestKit.Core/ClientBase.cs | 13 +++-- src/GeekLearning.RestKit.Core/FileFactory.cs | 26 +++++++++ src/GeekLearning.RestKit.Core/FormData.cs | 12 ++++ .../FormattedData.cs | 16 ++++++ src/GeekLearning.RestKit.Core/IFile.cs | 14 +++++ src/GeekLearning.RestKit.Core/IFormData.cs | 12 ++++ .../IMediaFormatter.cs | 4 +- .../Internal/MediaFormatterProvider.cs | 13 +++-- .../Internal/StreamFormFile.cs | 28 +++++++++ .../ParsedMediaType.cs | 35 ++++++++++++ .../UnhandledApiException.cs | 9 +-- .../UnsupportedMediaTypeApiException.cs | 11 ++-- .../GeekLearning.RestKit.FormData.xproj | 21 +++++++ .../MultipartFormDataFormatter.cs | 57 +++++++++++++++++++ .../Properties/AssemblyInfo.cs | 19 +++++++ .../project.json | 14 +++++ .../JsonMediaFormatter.cs | 14 +++-- .../GeekLearning.RestKit.Tests.xproj | 22 +++++++ .../MediaTypeParserTest.cs | 28 +++++++++ .../Properties/AssemblyInfo.cs | 19 +++++++ tests/GeekLearning.RestKit.Tests/project.json | 32 +++++++++++ 24 files changed, 417 insertions(+), 27 deletions(-) create mode 100644 global.json create mode 100644 src/GeekLearning.RestKit.Core/FileFactory.cs create mode 100644 src/GeekLearning.RestKit.Core/FormData.cs create mode 100644 src/GeekLearning.RestKit.Core/FormattedData.cs create mode 100644 src/GeekLearning.RestKit.Core/IFile.cs create mode 100644 src/GeekLearning.RestKit.Core/IFormData.cs create mode 100644 src/GeekLearning.RestKit.Core/Internal/StreamFormFile.cs create mode 100644 src/GeekLearning.RestKit.Core/ParsedMediaType.cs create mode 100644 src/GeekLearning.RestKit.FormData/GeekLearning.RestKit.FormData.xproj create mode 100644 src/GeekLearning.RestKit.FormData/MultipartFormDataFormatter.cs create mode 100644 src/GeekLearning.RestKit.FormData/Properties/AssemblyInfo.cs create mode 100644 src/GeekLearning.RestKit.FormData/project.json create mode 100644 tests/GeekLearning.RestKit.Tests/GeekLearning.RestKit.Tests.xproj create mode 100644 tests/GeekLearning.RestKit.Tests/MediaTypeParserTest.cs create mode 100644 tests/GeekLearning.RestKit.Tests/Properties/AssemblyInfo.cs create mode 100644 tests/GeekLearning.RestKit.Tests/project.json diff --git a/GeekLearning.RestKit.sln b/GeekLearning.RestKit.sln index 983814b..bc7aebd 100644 --- a/GeekLearning.RestKit.sln +++ b/GeekLearning.RestKit.sln @@ -9,6 +9,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "GeekLearning.RestKit.Json", EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "GeekLearning.Http.Logging", "src\GeekLearning.Http.Logging\GeekLearning.Http.Logging.xproj", "{380EE612-647B-4CDD-B491-B9B4AE7E1479}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "GeekLearning.RestKit.FormData", "src\GeekLearning.RestKit.FormData\GeekLearning.RestKit.FormData.xproj", "{0A64C2AC-3813-46DA-8039-1C79E1950C2A}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "GeekLearning.RestKit.Tests", "tests\GeekLearning.RestKit.Tests\GeekLearning.RestKit.Tests.xproj", "{2CFD4C43-C052-42E0-835C-FEA669C4EE91}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +31,14 @@ Global {380EE612-647B-4CDD-B491-B9B4AE7E1479}.Debug|Any CPU.Build.0 = Debug|Any CPU {380EE612-647B-4CDD-B491-B9B4AE7E1479}.Release|Any CPU.ActiveCfg = Release|Any CPU {380EE612-647B-4CDD-B491-B9B4AE7E1479}.Release|Any CPU.Build.0 = Release|Any CPU + {0A64C2AC-3813-46DA-8039-1C79E1950C2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A64C2AC-3813-46DA-8039-1C79E1950C2A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A64C2AC-3813-46DA-8039-1C79E1950C2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A64C2AC-3813-46DA-8039-1C79E1950C2A}.Release|Any CPU.Build.0 = Release|Any CPU + {2CFD4C43-C052-42E0-835C-FEA669C4EE91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2CFD4C43-C052-42E0-835C-FEA669C4EE91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2CFD4C43-C052-42E0-835C-FEA669C4EE91}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2CFD4C43-C052-42E0-835C-FEA669C4EE91}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/global.json b/global.json new file mode 100644 index 0000000..1aea5e4 --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "projects": [ "src", "tests" ], + "sdk": { + "version": "1.0.0-preview2-1-003177" + } +} diff --git a/src/GeekLearning.RestKit.Core/ApiException.cs b/src/GeekLearning.RestKit.Core/ApiException.cs index ee6db6e..bbf99b9 100644 --- a/src/GeekLearning.RestKit.Core/ApiException.cs +++ b/src/GeekLearning.RestKit.Core/ApiException.cs @@ -11,5 +11,12 @@ public abstract class ApiException: Exception public ApiException() { } + + public ApiException(HttpResponseMessage response) + { + this.Response = response; + } + + public HttpResponseMessage Response { get; protected set; } } } diff --git a/src/GeekLearning.RestKit.Core/ClientBase.cs b/src/GeekLearning.RestKit.Core/ClientBase.cs index d25d08d..d9c7d7d 100644 --- a/src/GeekLearning.RestKit.Core/ClientBase.cs +++ b/src/GeekLearning.RestKit.Core/ClientBase.cs @@ -26,13 +26,14 @@ public ClientBase(IOptions options, this.Options = options.Value; this.mediaFormatterProvider = mediaFormatterProvider; this.serviceProvider = serviceProvider; - this.httpClientFactory = httpClientFactory; + this.httpClientFactory = httpClientFactory; this.requestFilters = new Lazy(() => - this.Options.RequestFilters.Select(x=> ActivatorUtilities.CreateInstance(this.serviceProvider, x.Type, x.Arguments)).Cast().ToArray() + this.Options.RequestFilters.Select(x => ActivatorUtilities.CreateInstance(this.serviceProvider, x.Type, x.Arguments)).Cast().ToArray() ); } - protected HttpClient GetClient(){ + protected HttpClient GetClient() + { return this.httpClientFactory.CreateClient(); } @@ -43,7 +44,7 @@ protected Task TransformResponseAsync(HttpResponseMessage mess IMediaFormatter mediaFormatter = this.mediaFormatterProvider.GetMediaFormatter(message.Content.Headers.ContentType); if (mediaFormatter == null) { - throw new UnsupportedMediaTypeApiException(message.Content.Headers.ContentType); + throw new UnsupportedMediaTypeApiException(message); } return mediaFormatter.TransformAsync(message.Content); } @@ -59,14 +60,14 @@ protected HttpRequestMessage ApplyFilters(HttpRequestMessage requestMessage, par return finalMessage; } - protected HttpContent TransformRequestBody(object data, string mediaType) + protected HttpContent TransformRequestBody(object body, IDictionary formData, string mediaType) { IMediaFormatter mediaFormatter = this.mediaFormatterProvider.GetMediaFormatter(mediaType); if (mediaFormatter == null) { throw new UnsupportedMediaTypeApiException(mediaType); } - return mediaFormatter.Format(data); + return mediaFormatter.Format(body, formData); } /// diff --git a/src/GeekLearning.RestKit.Core/FileFactory.cs b/src/GeekLearning.RestKit.Core/FileFactory.cs new file mode 100644 index 0000000..30fe2d3 --- /dev/null +++ b/src/GeekLearning.RestKit.Core/FileFactory.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace GeekLearning.RestKit.Core +{ + public class FileFactory + { + IFile Get(byte[] data, string fileName) + { + return Get(new MemoryStream(data), fileName); + } + + IFile Get(Stream stream, string fileName) + { + return new Internal.StreamFormFile(stream, fileName); + } + + IFile Get(FileStream stream) + { + return Get(stream, stream.Name); + } + } +} diff --git a/src/GeekLearning.RestKit.Core/FormData.cs b/src/GeekLearning.RestKit.Core/FormData.cs new file mode 100644 index 0000000..1e65b65 --- /dev/null +++ b/src/GeekLearning.RestKit.Core/FormData.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace GeekLearning.RestKit.Core +{ + public class FormData: IFormData + { + public object Data { get; set; } + } +} diff --git a/src/GeekLearning.RestKit.Core/FormattedData.cs b/src/GeekLearning.RestKit.Core/FormattedData.cs new file mode 100644 index 0000000..2da8c85 --- /dev/null +++ b/src/GeekLearning.RestKit.Core/FormattedData.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace GeekLearning.RestKit.Core +{ + public class FormattedData + { + public object Data { get; set; } + + public string CollectionFormat { get; set; } + + public string Type { get; set; } + } +} diff --git a/src/GeekLearning.RestKit.Core/IFile.cs b/src/GeekLearning.RestKit.Core/IFile.cs new file mode 100644 index 0000000..5350ed3 --- /dev/null +++ b/src/GeekLearning.RestKit.Core/IFile.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; + +namespace GeekLearning.RestKit.Core +{ + public interface IFile: IFormData + { + string FileName { get; } + HttpContent CreateContent(); + } +} diff --git a/src/GeekLearning.RestKit.Core/IFormData.cs b/src/GeekLearning.RestKit.Core/IFormData.cs new file mode 100644 index 0000000..45a8f1d --- /dev/null +++ b/src/GeekLearning.RestKit.Core/IFormData.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace GeekLearning.RestKit.Core +{ + public interface IFormData + { + object Data { get;} + } +} diff --git a/src/GeekLearning.RestKit.Core/IMediaFormatter.cs b/src/GeekLearning.RestKit.Core/IMediaFormatter.cs index 87cec2f..535e23d 100644 --- a/src/GeekLearning.RestKit.Core/IMediaFormatter.cs +++ b/src/GeekLearning.RestKit.Core/IMediaFormatter.cs @@ -9,7 +9,7 @@ namespace GeekLearning.RestKit.Core public interface IMediaFormatter { Task TransformAsync(HttpContent content); - HttpContent Format(object data); - bool Supports(string contentType); + HttpContent Format(object body, IDictionary formData); + bool Supports(ParsedMediaType mediaType); } } diff --git a/src/GeekLearning.RestKit.Core/Internal/MediaFormatterProvider.cs b/src/GeekLearning.RestKit.Core/Internal/MediaFormatterProvider.cs index c81098e..2e9b992 100644 --- a/src/GeekLearning.RestKit.Core/Internal/MediaFormatterProvider.cs +++ b/src/GeekLearning.RestKit.Core/Internal/MediaFormatterProvider.cs @@ -6,7 +6,7 @@ namespace GeekLearning.RestKit.Core.Internal { - public class MediaFormatterProvider: IMediaFormatterProvider + public class MediaFormatterProvider : IMediaFormatterProvider { private IEnumerable mediaFormatters; @@ -17,12 +17,17 @@ public MediaFormatterProvider(IEnumerable mediaFormatters) public IMediaFormatter GetMediaFormatter(MediaTypeHeaderValue contentType) { - return GetMediaFormatter(contentType.MediaType); + return GetMediaFormatter(new ParsedMediaType(contentType.MediaType)); } - public IMediaFormatter GetMediaFormatter(string contentType) + public IMediaFormatter GetMediaFormatter(string mediaType) { - return mediaFormatters.First(f=> f.Supports(contentType)); + return mediaFormatters.First(f => f.Supports(new ParsedMediaType(mediaType))); + } + + public IMediaFormatter GetMediaFormatter(ParsedMediaType mediaType) + { + return mediaFormatters.First(f => f.Supports(mediaType)); } } } diff --git a/src/GeekLearning.RestKit.Core/Internal/StreamFormFile.cs b/src/GeekLearning.RestKit.Core/Internal/StreamFormFile.cs new file mode 100644 index 0000000..8ab2b7f --- /dev/null +++ b/src/GeekLearning.RestKit.Core/Internal/StreamFormFile.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; + +namespace GeekLearning.RestKit.Core.Internal +{ + public class StreamFormFile : IFile + { + public StreamFormFile(Stream stream, string fileName) + { + this.FileName = fileName; + this.Stream = stream; + } + + public object Data => this.Stream; + + public string FileName { get; } + public Stream Stream { get; } + + public HttpContent CreateContent() + { + return new StreamContent(this.Stream); + } + } +} diff --git a/src/GeekLearning.RestKit.Core/ParsedMediaType.cs b/src/GeekLearning.RestKit.Core/ParsedMediaType.cs new file mode 100644 index 0000000..1e8bd84 --- /dev/null +++ b/src/GeekLearning.RestKit.Core/ParsedMediaType.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace GeekLearning.RestKit.Core +{ + public class ParsedMediaType + { + public ParsedMediaType(string mediaType) + { + var lastPlus = mediaType.LastIndexOf('+'); + var firstSlash = mediaType.IndexOf('/'); + + Type = mediaType.Substring(0, firstSlash); + if (lastPlus >= 0) + { + SubType = mediaType.Substring(firstSlash + 1, lastPlus - firstSlash - 1); + Suffix = mediaType.Substring(lastPlus + 1); + } + else + { + SubType = mediaType.Substring(firstSlash + 1); + } + } + + public string Type { get; } + + public string SubType { get; } + + public string Suffix { get; } + + public string StructuredType => this.Suffix ?? this.SubType; + } +} diff --git a/src/GeekLearning.RestKit.Core/UnhandledApiException.cs b/src/GeekLearning.RestKit.Core/UnhandledApiException.cs index 4ddb47a..eb65378 100644 --- a/src/GeekLearning.RestKit.Core/UnhandledApiException.cs +++ b/src/GeekLearning.RestKit.Core/UnhandledApiException.cs @@ -8,12 +8,13 @@ namespace GeekLearning.RestKit.Core { public class UnhandledApiException : ApiException { - - public UnhandledApiException(HttpResponseMessage response) + public UnhandledApiException() : base() { - this.Response = response; + } + public UnhandledApiException(HttpResponseMessage response) : base(response) + { - public HttpResponseMessage Response { get; private set; } + } } } diff --git a/src/GeekLearning.RestKit.Core/UnsupportedMediaTypeApiException.cs b/src/GeekLearning.RestKit.Core/UnsupportedMediaTypeApiException.cs index 6439296..d5d2613 100644 --- a/src/GeekLearning.RestKit.Core/UnsupportedMediaTypeApiException.cs +++ b/src/GeekLearning.RestKit.Core/UnsupportedMediaTypeApiException.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; @@ -8,14 +9,10 @@ namespace GeekLearning.RestKit.Core { public class UnsupportedMediaTypeApiException: ApiException { - public UnsupportedMediaTypeApiException() + + public UnsupportedMediaTypeApiException(HttpResponseMessage response): base(response) { - - } - - public UnsupportedMediaTypeApiException(MediaTypeHeaderValue header) - { - this.MediaType = header.MediaType; + this.MediaType = response.Content.Headers.ContentType.MediaType; } public UnsupportedMediaTypeApiException(string mediaType) diff --git a/src/GeekLearning.RestKit.FormData/GeekLearning.RestKit.FormData.xproj b/src/GeekLearning.RestKit.FormData/GeekLearning.RestKit.FormData.xproj new file mode 100644 index 0000000..40bf593 --- /dev/null +++ b/src/GeekLearning.RestKit.FormData/GeekLearning.RestKit.FormData.xproj @@ -0,0 +1,21 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 0a64c2ac-3813-46da-8039-1c79e1950c2a + GeekLearning.RestKit.FormData + .\obj + .\bin\ + v4.5.2 + + + + 2.0 + + + diff --git a/src/GeekLearning.RestKit.FormData/MultipartFormDataFormatter.cs b/src/GeekLearning.RestKit.FormData/MultipartFormDataFormatter.cs new file mode 100644 index 0000000..52073e6 --- /dev/null +++ b/src/GeekLearning.RestKit.FormData/MultipartFormDataFormatter.cs @@ -0,0 +1,57 @@ +namespace GeekLearning.RestKit.FormData +{ + using GeekLearning.RestKit.Core; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using System.Net.Http; + using System.Collections; + + public class MultipartFormDataFormatter : IMediaFormatter + { + public MultipartFormDataFormatter() + { + } + + public HttpContent Format(object body, IDictionary formData) + { + var containerContent = new MultipartFormDataContent(); + foreach (var item in formData) + { + var file = formData as IFile; + + if (file != null) + { + var content = file.CreateContent(); + content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data"); + content.Headers.ContentDisposition.Name = item.Key; + content.Headers.ContentDisposition.FileName = file.FileName; + content.Headers.ContentDisposition.FileNameStar = file.FileName; + content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream"); + containerContent.Add(content); + } + else + { + var content = new StringContent(Convert.ToString(item.Value.Data, System.Globalization.CultureInfo.InvariantCulture)); + content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data"); + content.Headers.ContentDisposition.Name = item.Key; + containerContent.Add(content); + } + } + + return containerContent; + } + + public bool Supports(ParsedMediaType mediaType) + { + return string.Equals(mediaType.Type, "multipart", StringComparison.OrdinalIgnoreCase) + && string.Equals(mediaType.SubType, "form-data", StringComparison.OrdinalIgnoreCase); + } + + public Task TransformAsync(HttpContent content) + { + throw new NotSupportedException(); + } + } +} diff --git a/src/GeekLearning.RestKit.FormData/Properties/AssemblyInfo.cs b/src/GeekLearning.RestKit.FormData/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7f14920 --- /dev/null +++ b/src/GeekLearning.RestKit.FormData/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GeekLearning.RestKit.FormData")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0a64c2ac-3813-46da-8039-1c79e1950c2a")] diff --git a/src/GeekLearning.RestKit.FormData/project.json b/src/GeekLearning.RestKit.FormData/project.json new file mode 100644 index 0000000..e932819 --- /dev/null +++ b/src/GeekLearning.RestKit.FormData/project.json @@ -0,0 +1,14 @@ +{ + "version": "1.0.0-*", + + "dependencies": { + "NETStandard.Library": "1.6.0", + "GeekLearning.RestKit.Core": "*" + }, + + "frameworks": { + "netstandard1.6": { + "imports": "dnxcore50" + } + } +} diff --git a/src/GeekLearning.RestKit.Json/JsonMediaFormatter.cs b/src/GeekLearning.RestKit.Json/JsonMediaFormatter.cs index 223b547..97261a0 100644 --- a/src/GeekLearning.RestKit.Json/JsonMediaFormatter.cs +++ b/src/GeekLearning.RestKit.Json/JsonMediaFormatter.cs @@ -17,16 +17,22 @@ public JsonMediaFormatter() { } - public HttpContent Format(object data) + public HttpContent Format(object body, IDictionary formData) { - return new StringContent(JsonConvert.SerializeObject(data), System.Text.Encoding.UTF8, JsonMediaType); + return new StringContent(JsonConvert.SerializeObject(body), System.Text.Encoding.UTF8, JsonMediaType); } - public bool Supports(string contentType) + public bool Supports(ParsedMediaType mediaType) { - return contentType.Equals(JsonMediaType, StringComparison.OrdinalIgnoreCase); + return string.Equals(mediaType.Type, "application", StringComparison.OrdinalIgnoreCase) + && string.Equals(mediaType.StructuredType, "json", StringComparison.OrdinalIgnoreCase); } + //public bool Supports(string contentType) + //{ + // return contentType.Equals(JsonMediaType, StringComparison.OrdinalIgnoreCase); + //} + public async Task TransformAsync(HttpContent content) { var stringContent = await content.ReadAsStringAsync(); diff --git a/tests/GeekLearning.RestKit.Tests/GeekLearning.RestKit.Tests.xproj b/tests/GeekLearning.RestKit.Tests/GeekLearning.RestKit.Tests.xproj new file mode 100644 index 0000000..c8d465a --- /dev/null +++ b/tests/GeekLearning.RestKit.Tests/GeekLearning.RestKit.Tests.xproj @@ -0,0 +1,22 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 2cfd4c43-c052-42e0-835c-fea669c4ee91 + GeekLearning.RestKit.Tests + .\obj + .\bin\ + v4.5.2 + + + 2.0 + + + + + + \ No newline at end of file diff --git a/tests/GeekLearning.RestKit.Tests/MediaTypeParserTest.cs b/tests/GeekLearning.RestKit.Tests/MediaTypeParserTest.cs new file mode 100644 index 0000000..4f61460 --- /dev/null +++ b/tests/GeekLearning.RestKit.Tests/MediaTypeParserTest.cs @@ -0,0 +1,28 @@ +using GeekLearning.RestKit.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace GeekLearning.RestKit.Tests +{ + public class MediaTypeParserTest + { + + [Theory(DisplayName = nameof(TestMediaTypes))] + [InlineData("application/json", "application", "json", null, "json")] + [InlineData("application/vnd.apptype+json", "application", "vnd.apptype", "json", "json")] + [InlineData("application/vnd.apptype+xml", "application", "vnd.apptype", "xml", "xml")] + [InlineData("application/xml+json", "application", "xml", "json", "json")] + public void TestMediaTypes(string mediaType, string type, string subType, string suffix, string structured) + { + var parsedMediaType = new ParsedMediaType(mediaType); + + Assert.Equal(type, parsedMediaType.Type); + Assert.Equal(subType, parsedMediaType.SubType); + Assert.Equal(suffix, parsedMediaType.Suffix); + Assert.Equal(structured, parsedMediaType.StructuredType); + } + } +} diff --git a/tests/GeekLearning.RestKit.Tests/Properties/AssemblyInfo.cs b/tests/GeekLearning.RestKit.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..eefe2c1 --- /dev/null +++ b/tests/GeekLearning.RestKit.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GeekLearning.RestKit.Tests")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2cfd4c43-c052-42e0-835c-fea669c4ee91")] diff --git a/tests/GeekLearning.RestKit.Tests/project.json b/tests/GeekLearning.RestKit.Tests/project.json new file mode 100644 index 0000000..50e8116 --- /dev/null +++ b/tests/GeekLearning.RestKit.Tests/project.json @@ -0,0 +1,32 @@ +{ + "version": "1.0.0-*", + "buildOptions": { + }, + + "testRunner": "xunit", + + "dependencies": { + "GeekLearning.RestKit.Core": "*", + "dotnet-test-xunit": "2.2.0-preview2-build1029", + "xunit": "2.2.0-beta2-build3300", + "xunit.runner.visualstudio": "2.2.0-beta2-build1149" + }, + + "frameworks": { + "netcoreapp1.0": { + "imports": "dnxcore50", + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.1" + } + } + }, + "net452": { + "frameworkAssemblies": { + }, + "dependencies": { + } + } + } +} From 2d73505274f743aeef3bfb80ea68a4ede8488809 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Sun, 25 Dec 2016 18:59:38 +0100 Subject: [PATCH 15/28] formdata --- src/GeekLearning.RestKit.Core/ClientBase.cs | 10 ++++++++++ src/GeekLearning.RestKit.Core/FormData.cs | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/src/GeekLearning.RestKit.Core/ClientBase.cs b/src/GeekLearning.RestKit.Core/ClientBase.cs index d9c7d7d..45d6a3f 100644 --- a/src/GeekLearning.RestKit.Core/ClientBase.cs +++ b/src/GeekLearning.RestKit.Core/ClientBase.cs @@ -70,6 +70,16 @@ protected HttpContent TransformRequestBody(object body, IDictionary /// Append the given query keys and values to the uri. /// diff --git a/src/GeekLearning.RestKit.Core/FormData.cs b/src/GeekLearning.RestKit.Core/FormData.cs index 1e65b65..a2fbbdc 100644 --- a/src/GeekLearning.RestKit.Core/FormData.cs +++ b/src/GeekLearning.RestKit.Core/FormData.cs @@ -7,6 +7,11 @@ namespace GeekLearning.RestKit.Core { public class FormData: IFormData { + public FormData(object data) + { + Data = data; + } + public object Data { get; set; } } } From fb80a74aef53ba92f8fe4a14acd22c46220144e4 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Mon, 26 Dec 2016 22:10:25 +0100 Subject: [PATCH 16/28] api exception and status mapping --- src/GeekLearning.RestKit.Core/ApiException.cs | 4 +- .../ApiException{TResponse}.cs | 21 +++--- .../BadRequestApiException.cs | 28 ++++++-- src/GeekLearning.RestKit.Core/ClientBase.cs | 65 +++++++++++++++++++ .../ConflictApiException.cs | 30 ++++++--- .../ForbiddenApiException.cs | 29 ++++++--- .../IApiException{TResponse}.cs | 12 ++++ .../InternalServerErrorApiException.cs | 29 +++++++++ .../NotFoundApiException.cs | 31 ++++++--- .../ServiceUnavailableApiException.cs | 29 +++++++++ .../UnauthorizedApiException.cs | 31 ++++++--- .../UnhandledApiException.cs | 30 ++++++--- 12 files changed, 279 insertions(+), 60 deletions(-) create mode 100644 src/GeekLearning.RestKit.Core/IApiException{TResponse}.cs create mode 100644 src/GeekLearning.RestKit.Core/InternalServerErrorApiException.cs create mode 100644 src/GeekLearning.RestKit.Core/ServiceUnavailableApiException.cs diff --git a/src/GeekLearning.RestKit.Core/ApiException.cs b/src/GeekLearning.RestKit.Core/ApiException.cs index bbf99b9..73497fe 100644 --- a/src/GeekLearning.RestKit.Core/ApiException.cs +++ b/src/GeekLearning.RestKit.Core/ApiException.cs @@ -14,9 +14,9 @@ public ApiException() public ApiException(HttpResponseMessage response) { - this.Response = response; + this.ResponseMessage = response; } - public HttpResponseMessage Response { get; protected set; } + public HttpResponseMessage ResponseMessage { get; protected set; } } } diff --git a/src/GeekLearning.RestKit.Core/ApiException{TResponse}.cs b/src/GeekLearning.RestKit.Core/ApiException{TResponse}.cs index 97febe6..a159da1 100644 --- a/src/GeekLearning.RestKit.Core/ApiException{TResponse}.cs +++ b/src/GeekLearning.RestKit.Core/ApiException{TResponse}.cs @@ -1,15 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace GeekLearning.RestKit.Core +namespace GeekLearning.RestKit.Core { - public abstract class ApiException: ApiException + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + using System.Threading.Tasks; + + public class ApiException: ApiException, IApiException { - public ApiException() + public ApiException(HttpResponseMessage response, TResponse data) : base(response) { + this.Response = data; } - + + public TResponse Response { get; } } } diff --git a/src/GeekLearning.RestKit.Core/BadRequestApiException.cs b/src/GeekLearning.RestKit.Core/BadRequestApiException.cs index 575bdf7..bee8ad7 100644 --- a/src/GeekLearning.RestKit.Core/BadRequestApiException.cs +++ b/src/GeekLearning.RestKit.Core/BadRequestApiException.cs @@ -1,15 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace GeekLearning.RestKit.Core +namespace GeekLearning.RestKit.Core { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + using System.Threading.Tasks; + public class BadRequestApiException: ApiException { + public BadRequestApiException() + { + } + + public BadRequestApiException(HttpResponseMessage message): base(message) + { + } } - public class BadRequestApiException : ApiException + public class BadRequestApiException : BadRequestApiException, IApiException { + public BadRequestApiException(HttpResponseMessage message, TResponse response) : base(message) + { + this.Response = response; + } + + public TResponse Response { get; } } } diff --git a/src/GeekLearning.RestKit.Core/ClientBase.cs b/src/GeekLearning.RestKit.Core/ClientBase.cs index 45d6a3f..b5bb27b 100644 --- a/src/GeekLearning.RestKit.Core/ClientBase.cs +++ b/src/GeekLearning.RestKit.Core/ClientBase.cs @@ -70,6 +70,71 @@ protected HttpContent TransformRequestBody(object body, IDictionary(HttpResponseMessage message) + { + if (message.Content == null) + { + this.OnFailure(message); + } + else + { + var result = await this.TransformResponseAsync(message); + + switch (message.StatusCode) + { + case HttpStatusCode.BadRequest: + throw new BadRequestApiException(message, result); + case HttpStatusCode.Unauthorized: + throw new UnauthorizedApiException(message, result); + case HttpStatusCode.Forbidden: + throw new ForbiddenApiException(message, result); + case HttpStatusCode.NotFound: + throw new NotFoundApiException(message, result); + case HttpStatusCode.Conflict: + throw new ConflictApiException(message, result); + case HttpStatusCode.InternalServerError: + throw new ConflictApiException(message, result); + case HttpStatusCode.ServiceUnavailable: + throw new ServiceUnavailableApiException(message, result); + default: + throw new UnhandledApiException(message, result); + } + } + } + + protected bool MatchStatus(HttpResponseMessage message, int status) + { + return status == (int)message.StatusCode; + } + + protected bool MatchStatus(HttpResponseMessage message, string status) + { + return message.StatusCode.ToString().Equals(status, StringComparison.OrdinalIgnoreCase) + || message.ReasonPhrase == status; + } protected IFormData GetFormData(IFormData data) { diff --git a/src/GeekLearning.RestKit.Core/ConflictApiException.cs b/src/GeekLearning.RestKit.Core/ConflictApiException.cs index d020401..639ee4e 100644 --- a/src/GeekLearning.RestKit.Core/ConflictApiException.cs +++ b/src/GeekLearning.RestKit.Core/ConflictApiException.cs @@ -1,15 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace GeekLearning.RestKit.Core +namespace GeekLearning.RestKit.Core { - public class ConflictApiException: ApiException + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + using System.Threading.Tasks; + + public class ConflictApiException : ApiException { + public ConflictApiException() + { + } + + public ConflictApiException(HttpResponseMessage message) : base(message) + { + } } - public class ConflictApiException : ApiException + public class ConflictApiException : BadRequestApiException, IApiException { + public ConflictApiException(HttpResponseMessage message, TResponse response) : base(message) + { + this.Response = response; + } + + public TResponse Response { get; } } } diff --git a/src/GeekLearning.RestKit.Core/ForbiddenApiException.cs b/src/GeekLearning.RestKit.Core/ForbiddenApiException.cs index 919c96b..77ac11d 100644 --- a/src/GeekLearning.RestKit.Core/ForbiddenApiException.cs +++ b/src/GeekLearning.RestKit.Core/ForbiddenApiException.cs @@ -1,16 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace GeekLearning.RestKit.Core +namespace GeekLearning.RestKit.Core { - public class ForbiddenApiException: ApiException + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + using System.Threading.Tasks; + + public class ForbiddenApiException : ApiException { + public ForbiddenApiException() + { + } + public ForbiddenApiException(HttpResponseMessage message) : base(message) + { + } } - public class ForbiddenApiException: ApiException + public class ForbiddenApiException : BadRequestApiException, IApiException { + public ForbiddenApiException(HttpResponseMessage message, TResponse response) : base(message) + { + this.Response = response; + } + + public TResponse Response { get; } } } diff --git a/src/GeekLearning.RestKit.Core/IApiException{TResponse}.cs b/src/GeekLearning.RestKit.Core/IApiException{TResponse}.cs new file mode 100644 index 0000000..f945f9c --- /dev/null +++ b/src/GeekLearning.RestKit.Core/IApiException{TResponse}.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace GeekLearning.RestKit.Core +{ + public interface IApiException + { + TResponse Response { get; } + } +} diff --git a/src/GeekLearning.RestKit.Core/InternalServerErrorApiException.cs b/src/GeekLearning.RestKit.Core/InternalServerErrorApiException.cs new file mode 100644 index 0000000..11835e0 --- /dev/null +++ b/src/GeekLearning.RestKit.Core/InternalServerErrorApiException.cs @@ -0,0 +1,29 @@ +namespace GeekLearning.RestKit.Core +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + using System.Threading.Tasks; + + public class InternalServerErrorApiException: ApiException + { + public InternalServerErrorApiException() + { + } + + public InternalServerErrorApiException(HttpResponseMessage message): base(message) + { + } + } + + public class InternalServerErrorApiException : BadRequestApiException, IApiException + { + public InternalServerErrorApiException(HttpResponseMessage message, TResponse response) : base(message) + { + this.Response = response; + } + + public TResponse Response { get; } + } +} diff --git a/src/GeekLearning.RestKit.Core/NotFoundApiException.cs b/src/GeekLearning.RestKit.Core/NotFoundApiException.cs index 1c0124c..67fc33b 100644 --- a/src/GeekLearning.RestKit.Core/NotFoundApiException.cs +++ b/src/GeekLearning.RestKit.Core/NotFoundApiException.cs @@ -1,15 +1,30 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace GeekLearning.RestKit.Core +namespace GeekLearning.RestKit.Core { - public class NotFoundApiException: ApiException + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + using System.Threading.Tasks; + + + public class NotFoundApiException : ApiException { + public NotFoundApiException() + { + } + + public NotFoundApiException(HttpResponseMessage message) : base(message) + { + } } - public class NotFoundApiException : ApiException + public class NotFoundApiException : BadRequestApiException, IApiException { + public NotFoundApiException(HttpResponseMessage message, TResponse response) : base(message) + { + this.Response = response; + } + + public TResponse Response { get; } } } diff --git a/src/GeekLearning.RestKit.Core/ServiceUnavailableApiException.cs b/src/GeekLearning.RestKit.Core/ServiceUnavailableApiException.cs new file mode 100644 index 0000000..dd238e6 --- /dev/null +++ b/src/GeekLearning.RestKit.Core/ServiceUnavailableApiException.cs @@ -0,0 +1,29 @@ +namespace GeekLearning.RestKit.Core +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + using System.Threading.Tasks; + + public class ServiceUnavailableApiException : ApiException + { + public ServiceUnavailableApiException() + { + } + + public ServiceUnavailableApiException(HttpResponseMessage message): base(message) + { + } + } + + public class ServiceUnavailableApiException : BadRequestApiException, IApiException + { + public ServiceUnavailableApiException(HttpResponseMessage message, TResponse response) : base(message) + { + this.Response = response; + } + + public TResponse Response { get; } + } +} diff --git a/src/GeekLearning.RestKit.Core/UnauthorizedApiException.cs b/src/GeekLearning.RestKit.Core/UnauthorizedApiException.cs index 209bb78..871ae41 100644 --- a/src/GeekLearning.RestKit.Core/UnauthorizedApiException.cs +++ b/src/GeekLearning.RestKit.Core/UnauthorizedApiException.cs @@ -1,15 +1,30 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace GeekLearning.RestKit.Core +namespace GeekLearning.RestKit.Core { - public class UnauthorizedApiException: ApiException + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + using System.Threading.Tasks; + + + public class UnauthorizedApiException : ApiException { + public UnauthorizedApiException() + { + } + + public UnauthorizedApiException(HttpResponseMessage message) : base(message) + { + } } - public class UnauthorizedApiException : ApiException + public class UnauthorizedApiException : BadRequestApiException, IApiException { + public UnauthorizedApiException(HttpResponseMessage message, TResponse response) : base(message) + { + this.Response = response; + } + + public TResponse Response { get; } } } diff --git a/src/GeekLearning.RestKit.Core/UnhandledApiException.cs b/src/GeekLearning.RestKit.Core/UnhandledApiException.cs index eb65378..83d4eb3 100644 --- a/src/GeekLearning.RestKit.Core/UnhandledApiException.cs +++ b/src/GeekLearning.RestKit.Core/UnhandledApiException.cs @@ -1,20 +1,30 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; - -namespace GeekLearning.RestKit.Core +namespace GeekLearning.RestKit.Core { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + using System.Threading.Tasks; + + public class UnhandledApiException : ApiException { - public UnhandledApiException() : base() + public UnhandledApiException() { - } - public UnhandledApiException(HttpResponseMessage response) : base(response) + + public UnhandledApiException(HttpResponseMessage message) : base(message) { + } + } + public class UnhandledApiException : BadRequestApiException, IApiException + { + public UnhandledApiException(HttpResponseMessage message, TResponse response) : base(message) + { + this.Response = response; } + + public TResponse Response { get; } } } From 79142f0f12bd905bc15927d522bd4f484c80c23b Mon Sep 17 00:00:00 2001 From: sandorfr Date: Tue, 27 Dec 2016 11:30:38 +0100 Subject: [PATCH 17/28] exception mapping fixes --- src/GeekLearning.RestKit.Core/ClientBase.cs | 38 ++++++++++----------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/GeekLearning.RestKit.Core/ClientBase.cs b/src/GeekLearning.RestKit.Core/ClientBase.cs index b5bb27b..0ad2749 100644 --- a/src/GeekLearning.RestKit.Core/ClientBase.cs +++ b/src/GeekLearning.RestKit.Core/ClientBase.cs @@ -70,34 +70,34 @@ protected HttpContent TransformRequestBody(object body, IDictionary(HttpResponseMessage message) + protected async Task MapToException(HttpResponseMessage message) { if (message.Content == null) { - this.OnFailure(message); + return this.MapToException(message); } else { @@ -106,21 +106,21 @@ protected async Task OnFailure(HttpResponseMessage message) switch (message.StatusCode) { case HttpStatusCode.BadRequest: - throw new BadRequestApiException(message, result); + return new BadRequestApiException(message, result); case HttpStatusCode.Unauthorized: - throw new UnauthorizedApiException(message, result); + return new UnauthorizedApiException(message, result); case HttpStatusCode.Forbidden: - throw new ForbiddenApiException(message, result); + return new ForbiddenApiException(message, result); case HttpStatusCode.NotFound: - throw new NotFoundApiException(message, result); + return new NotFoundApiException(message, result); case HttpStatusCode.Conflict: - throw new ConflictApiException(message, result); + return new ConflictApiException(message, result); case HttpStatusCode.InternalServerError: - throw new ConflictApiException(message, result); + return new ConflictApiException(message, result); case HttpStatusCode.ServiceUnavailable: - throw new ServiceUnavailableApiException(message, result); + return new ServiceUnavailableApiException(message, result); default: - throw new UnhandledApiException(message, result); + return new UnhandledApiException(message, result); } } } From 9d5765e605e867f2ab928e2bea4cffbc646efdd8 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Tue, 27 Dec 2016 17:27:05 +0100 Subject: [PATCH 18/28] Polly options net452 support for formData --- src/GeekLearning.RestKit.Core/ClientBase.cs | 2 +- src/GeekLearning.RestKit.Core/ClientOptionsBase.cs | 5 ++++- .../IProvideErrorHandlingPolicy.cs | 13 +++++++++++++ src/GeekLearning.RestKit.Core/project.json | 9 ++++++--- src/GeekLearning.RestKit.FormData/project.json | 1 + 5 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 src/GeekLearning.RestKit.Core/IProvideErrorHandlingPolicy.cs diff --git a/src/GeekLearning.RestKit.Core/ClientBase.cs b/src/GeekLearning.RestKit.Core/ClientBase.cs index 0ad2749..ddaf158 100644 --- a/src/GeekLearning.RestKit.Core/ClientBase.cs +++ b/src/GeekLearning.RestKit.Core/ClientBase.cs @@ -11,7 +11,7 @@ using Microsoft.Extensions.DependencyInjection; public abstract class ClientBase - where TOptions : class, IProvideRequestFilters, new() + where TOptions : class, IProvideRequestFilters, IProvideErrorHandlingPolicy, new() { private IMediaFormatterProvider mediaFormatterProvider; private IServiceProvider serviceProvider; diff --git a/src/GeekLearning.RestKit.Core/ClientOptionsBase.cs b/src/GeekLearning.RestKit.Core/ClientOptionsBase.cs index 882faf1..2e2b13a 100644 --- a/src/GeekLearning.RestKit.Core/ClientOptionsBase.cs +++ b/src/GeekLearning.RestKit.Core/ClientOptionsBase.cs @@ -4,8 +4,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; + using Polly; - public abstract class ClientOptionsBase : IProvideRequestFilters + public abstract class ClientOptionsBase : IProvideRequestFilters, IProvideErrorHandlingPolicy { private List requestfilters = new List(); @@ -25,5 +26,7 @@ public IEnumerable RequestFilters return requestfilters; } } + + public Policy Policy { get; set; } } } diff --git a/src/GeekLearning.RestKit.Core/IProvideErrorHandlingPolicy.cs b/src/GeekLearning.RestKit.Core/IProvideErrorHandlingPolicy.cs new file mode 100644 index 0000000..e061aeb --- /dev/null +++ b/src/GeekLearning.RestKit.Core/IProvideErrorHandlingPolicy.cs @@ -0,0 +1,13 @@ +namespace GeekLearning.RestKit.Core +{ + using Polly; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + public interface IProvideErrorHandlingPolicy + { + Policy Policy { get; } + } +} diff --git a/src/GeekLearning.RestKit.Core/project.json b/src/GeekLearning.RestKit.Core/project.json index 49e0783..8d9961f 100644 --- a/src/GeekLearning.RestKit.Core/project.json +++ b/src/GeekLearning.RestKit.Core/project.json @@ -3,13 +3,16 @@ "dependencies": { "Microsoft.Extensions.Options": "1.0.0", - "NETStandard.Library": "1.6.0" + "NETStandard.Library": "1.6.0", + "Polly": "5.0.2-*" }, "frameworks": { - "net452": {}, + "net452": { + "dependencies": { + } + }, "netstandard1.3": { - "imports": "dnxcore50" } } } diff --git a/src/GeekLearning.RestKit.FormData/project.json b/src/GeekLearning.RestKit.FormData/project.json index e932819..be5b969 100644 --- a/src/GeekLearning.RestKit.FormData/project.json +++ b/src/GeekLearning.RestKit.FormData/project.json @@ -7,6 +7,7 @@ }, "frameworks": { + "net452": {}, "netstandard1.6": { "imports": "dnxcore50" } From 23c3e6bc05cb54d8c0cdadfe01966b7f2b142d4a Mon Sep 17 00:00:00 2001 From: sandorfr Date: Thu, 29 Dec 2016 12:57:46 +0100 Subject: [PATCH 19/28] deduplicate get media formatter --- .../Internal/MediaFormatterProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GeekLearning.RestKit.Core/Internal/MediaFormatterProvider.cs b/src/GeekLearning.RestKit.Core/Internal/MediaFormatterProvider.cs index 2e9b992..c3286b3 100644 --- a/src/GeekLearning.RestKit.Core/Internal/MediaFormatterProvider.cs +++ b/src/GeekLearning.RestKit.Core/Internal/MediaFormatterProvider.cs @@ -22,7 +22,7 @@ public IMediaFormatter GetMediaFormatter(MediaTypeHeaderValue contentType) public IMediaFormatter GetMediaFormatter(string mediaType) { - return mediaFormatters.First(f => f.Supports(new ParsedMediaType(mediaType))); + return GetMediaFormatter(new ParsedMediaType(mediaType)); } public IMediaFormatter GetMediaFormatter(ParsedMediaType mediaType) From 4026f47b2fc5f203800b7b21416522c85aabab5a Mon Sep 17 00:00:00 2001 From: Sandor Date: Thu, 12 Jan 2017 11:28:13 +0100 Subject: [PATCH 20/28] Fix `UnsupportedMediaType` exception Address https://github.com/geeklearningio/gl-swagger-generator/issues/12 --- .../Internal/MediaFormatterProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GeekLearning.RestKit.Core/Internal/MediaFormatterProvider.cs b/src/GeekLearning.RestKit.Core/Internal/MediaFormatterProvider.cs index c3286b3..9f104f5 100644 --- a/src/GeekLearning.RestKit.Core/Internal/MediaFormatterProvider.cs +++ b/src/GeekLearning.RestKit.Core/Internal/MediaFormatterProvider.cs @@ -27,7 +27,7 @@ public IMediaFormatter GetMediaFormatter(string mediaType) public IMediaFormatter GetMediaFormatter(ParsedMediaType mediaType) { - return mediaFormatters.First(f => f.Supports(mediaType)); + return mediaFormatters.FirstOrDefault(f => f.Supports(mediaType)); } } } From 99ba7e935e5f72f1c8abb15f0df3f9f020082e4e Mon Sep 17 00:00:00 2001 From: Cyprien Autexier Date: Mon, 30 Jan 2017 14:38:32 +1100 Subject: [PATCH 21/28] fix #6 --- src/GeekLearning.RestKit.Core/ClientBase.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/GeekLearning.RestKit.Core/ClientBase.cs b/src/GeekLearning.RestKit.Core/ClientBase.cs index ddaf158..39c8f1d 100644 --- a/src/GeekLearning.RestKit.Core/ClientBase.cs +++ b/src/GeekLearning.RestKit.Core/ClientBase.cs @@ -41,12 +41,19 @@ protected HttpClient GetClient() protected Task TransformResponseAsync(HttpResponseMessage message) { - IMediaFormatter mediaFormatter = this.mediaFormatterProvider.GetMediaFormatter(message.Content.Headers.ContentType); - if (mediaFormatter == null) + if (message.Content != null) + { + IMediaFormatter mediaFormatter = this.mediaFormatterProvider.GetMediaFormatter(message.Content.Headers.ContentType); + if (mediaFormatter == null) + { + throw new UnsupportedMediaTypeApiException(message); + } + return mediaFormatter.TransformAsync(message.Content); + } + else { - throw new UnsupportedMediaTypeApiException(message); + return Task.FromResult(default(TTarget)); } - return mediaFormatter.TransformAsync(message.Content); } protected HttpRequestMessage ApplyFilters(HttpRequestMessage requestMessage, params string[] securityDefinitions) From 1b9d25a09ffc2702e7bd034aff46a70d3928fa49 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Wed, 1 Feb 2017 04:44:47 +0100 Subject: [PATCH 22/28] make sure there is content in the body prior to trying finding a formatter. --- src/GeekLearning.RestKit.Core/ClientBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GeekLearning.RestKit.Core/ClientBase.cs b/src/GeekLearning.RestKit.Core/ClientBase.cs index 39c8f1d..38e777f 100644 --- a/src/GeekLearning.RestKit.Core/ClientBase.cs +++ b/src/GeekLearning.RestKit.Core/ClientBase.cs @@ -41,7 +41,7 @@ protected HttpClient GetClient() protected Task TransformResponseAsync(HttpResponseMessage message) { - if (message.Content != null) + if (message.Content != null && message.Content.Headers.ContentLength > 0) { IMediaFormatter mediaFormatter = this.mediaFormatterProvider.GetMediaFormatter(message.Content.Headers.ContentType); if (mediaFormatter == null) From 80cea1543fa9792ddc47c1479380e104fec03830 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Wed, 1 Feb 2017 06:27:36 +0100 Subject: [PATCH 23/28] fix generic exception ancestors --- src/GeekLearning.RestKit.Core/ConflictApiException.cs | 2 +- src/GeekLearning.RestKit.Core/ForbiddenApiException.cs | 2 +- .../InternalServerErrorApiException.cs | 2 +- src/GeekLearning.RestKit.Core/NotFoundApiException.cs | 2 +- src/GeekLearning.RestKit.Core/ServiceUnavailableApiException.cs | 2 +- src/GeekLearning.RestKit.Core/UnauthorizedApiException.cs | 2 +- src/GeekLearning.RestKit.Core/UnhandledApiException.cs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/GeekLearning.RestKit.Core/ConflictApiException.cs b/src/GeekLearning.RestKit.Core/ConflictApiException.cs index 639ee4e..10bd97e 100644 --- a/src/GeekLearning.RestKit.Core/ConflictApiException.cs +++ b/src/GeekLearning.RestKit.Core/ConflictApiException.cs @@ -17,7 +17,7 @@ public ConflictApiException(HttpResponseMessage message) : base(message) } } - public class ConflictApiException : BadRequestApiException, IApiException + public class ConflictApiException : ConflictApiException, IApiException { public ConflictApiException(HttpResponseMessage message, TResponse response) : base(message) { diff --git a/src/GeekLearning.RestKit.Core/ForbiddenApiException.cs b/src/GeekLearning.RestKit.Core/ForbiddenApiException.cs index 77ac11d..ff88864 100644 --- a/src/GeekLearning.RestKit.Core/ForbiddenApiException.cs +++ b/src/GeekLearning.RestKit.Core/ForbiddenApiException.cs @@ -17,7 +17,7 @@ public ForbiddenApiException(HttpResponseMessage message) : base(message) } } - public class ForbiddenApiException : BadRequestApiException, IApiException + public class ForbiddenApiException : ForbiddenApiException, IApiException { public ForbiddenApiException(HttpResponseMessage message, TResponse response) : base(message) { diff --git a/src/GeekLearning.RestKit.Core/InternalServerErrorApiException.cs b/src/GeekLearning.RestKit.Core/InternalServerErrorApiException.cs index 11835e0..22d50dc 100644 --- a/src/GeekLearning.RestKit.Core/InternalServerErrorApiException.cs +++ b/src/GeekLearning.RestKit.Core/InternalServerErrorApiException.cs @@ -17,7 +17,7 @@ public InternalServerErrorApiException(HttpResponseMessage message): base(messag } } - public class InternalServerErrorApiException : BadRequestApiException, IApiException + public class InternalServerErrorApiException : InternalServerErrorApiException, IApiException { public InternalServerErrorApiException(HttpResponseMessage message, TResponse response) : base(message) { diff --git a/src/GeekLearning.RestKit.Core/NotFoundApiException.cs b/src/GeekLearning.RestKit.Core/NotFoundApiException.cs index 67fc33b..5e2368b 100644 --- a/src/GeekLearning.RestKit.Core/NotFoundApiException.cs +++ b/src/GeekLearning.RestKit.Core/NotFoundApiException.cs @@ -18,7 +18,7 @@ public NotFoundApiException(HttpResponseMessage message) : base(message) } } - public class NotFoundApiException : BadRequestApiException, IApiException + public class NotFoundApiException : NotFoundApiException, IApiException { public NotFoundApiException(HttpResponseMessage message, TResponse response) : base(message) { diff --git a/src/GeekLearning.RestKit.Core/ServiceUnavailableApiException.cs b/src/GeekLearning.RestKit.Core/ServiceUnavailableApiException.cs index dd238e6..ad0f3fc 100644 --- a/src/GeekLearning.RestKit.Core/ServiceUnavailableApiException.cs +++ b/src/GeekLearning.RestKit.Core/ServiceUnavailableApiException.cs @@ -17,7 +17,7 @@ public ServiceUnavailableApiException(HttpResponseMessage message): base(message } } - public class ServiceUnavailableApiException : BadRequestApiException, IApiException + public class ServiceUnavailableApiException : ServiceUnavailableApiException, IApiException { public ServiceUnavailableApiException(HttpResponseMessage message, TResponse response) : base(message) { diff --git a/src/GeekLearning.RestKit.Core/UnauthorizedApiException.cs b/src/GeekLearning.RestKit.Core/UnauthorizedApiException.cs index 871ae41..a0ab5b2 100644 --- a/src/GeekLearning.RestKit.Core/UnauthorizedApiException.cs +++ b/src/GeekLearning.RestKit.Core/UnauthorizedApiException.cs @@ -18,7 +18,7 @@ public UnauthorizedApiException(HttpResponseMessage message) : base(message) } } - public class UnauthorizedApiException : BadRequestApiException, IApiException + public class UnauthorizedApiException : UnauthorizedApiException, IApiException { public UnauthorizedApiException(HttpResponseMessage message, TResponse response) : base(message) { diff --git a/src/GeekLearning.RestKit.Core/UnhandledApiException.cs b/src/GeekLearning.RestKit.Core/UnhandledApiException.cs index 83d4eb3..a81ca3d 100644 --- a/src/GeekLearning.RestKit.Core/UnhandledApiException.cs +++ b/src/GeekLearning.RestKit.Core/UnhandledApiException.cs @@ -18,7 +18,7 @@ public UnhandledApiException(HttpResponseMessage message) : base(message) } } - public class UnhandledApiException : BadRequestApiException, IApiException + public class UnhandledApiException : UnhandledApiException, IApiException { public UnhandledApiException(HttpResponseMessage message, TResponse response) : base(message) { From ea4e12435b76f5cca57d529aa1daa6909f1428d1 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Fri, 10 Mar 2017 04:27:17 +0100 Subject: [PATCH 24/28] fix FileFactory method visibility --- src/GeekLearning.RestKit.Core/FileFactory.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/GeekLearning.RestKit.Core/FileFactory.cs b/src/GeekLearning.RestKit.Core/FileFactory.cs index 30fe2d3..b7ad808 100644 --- a/src/GeekLearning.RestKit.Core/FileFactory.cs +++ b/src/GeekLearning.RestKit.Core/FileFactory.cs @@ -8,17 +8,17 @@ namespace GeekLearning.RestKit.Core { public class FileFactory { - IFile Get(byte[] data, string fileName) + public IFile Get(byte[] data, string fileName) { return Get(new MemoryStream(data), fileName); } - IFile Get(Stream stream, string fileName) + public IFile Get(Stream stream, string fileName) { return new Internal.StreamFormFile(stream, fileName); } - IFile Get(FileStream stream) + public IFile Get(FileStream stream) { return Get(stream, stream.Name); } From 82b00eea326da1ec06e10665c50ced140d8e2931 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Mon, 13 Mar 2017 03:15:01 +0100 Subject: [PATCH 25/28] fix file formData --- src/GeekLearning.RestKit.Core/ClientBase.cs | 9 +++++---- .../RestKitFormDataExtensions.cs | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 src/GeekLearning.RestKit.FormData/RestKitFormDataExtensions.cs diff --git a/src/GeekLearning.RestKit.Core/ClientBase.cs b/src/GeekLearning.RestKit.Core/ClientBase.cs index 38e777f..43f4703 100644 --- a/src/GeekLearning.RestKit.Core/ClientBase.cs +++ b/src/GeekLearning.RestKit.Core/ClientBase.cs @@ -143,12 +143,13 @@ protected bool MatchStatus(HttpResponseMessage message, string status) || message.ReasonPhrase == status; } - protected IFormData GetFormData(IFormData data) - { - return data; - } protected IFormData GetFormData(object data) { + var formData = data as IFormData; + if (formData != null) + { + return formData; + } return new FormData(data); } diff --git a/src/GeekLearning.RestKit.FormData/RestKitFormDataExtensions.cs b/src/GeekLearning.RestKit.FormData/RestKitFormDataExtensions.cs new file mode 100644 index 0000000..83a6294 --- /dev/null +++ b/src/GeekLearning.RestKit.FormData/RestKitFormDataExtensions.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using GeekLearning.RestKit.Core; + +namespace GeekLearning.RestKit.FormData +{ + public static class RestKitFormDataExtensions + { + public static IRestKitServicesBuilder AddFormData(this IRestKitServicesBuilder restKitServicesBuilder) + { + return restKitServicesBuilder.AddMediaFormater(); + } + } +} From 967a9b51becb222acd8aa9ff9750aeeb69c0fcdf Mon Sep 17 00:00:00 2001 From: sandorfr Date: Mon, 13 Mar 2017 04:34:59 +0100 Subject: [PATCH 26/28] switch form data to netstandard 1.3 --- src/GeekLearning.RestKit.FormData/project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GeekLearning.RestKit.FormData/project.json b/src/GeekLearning.RestKit.FormData/project.json index be5b969..c079c4a 100644 --- a/src/GeekLearning.RestKit.FormData/project.json +++ b/src/GeekLearning.RestKit.FormData/project.json @@ -8,7 +8,7 @@ "frameworks": { "net452": {}, - "netstandard1.6": { + "netstandard1.3": { "imports": "dnxcore50" } } From 72dc98f9d4ac192459a1ca718d79c11901337009 Mon Sep 17 00:00:00 2001 From: sandorfr Date: Mon, 13 Mar 2017 05:17:08 +0100 Subject: [PATCH 27/28] fix IFile support --- src/GeekLearning.RestKit.FormData/MultipartFormDataFormatter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GeekLearning.RestKit.FormData/MultipartFormDataFormatter.cs b/src/GeekLearning.RestKit.FormData/MultipartFormDataFormatter.cs index 52073e6..cbd4145 100644 --- a/src/GeekLearning.RestKit.FormData/MultipartFormDataFormatter.cs +++ b/src/GeekLearning.RestKit.FormData/MultipartFormDataFormatter.cs @@ -19,7 +19,7 @@ public HttpContent Format(object body, IDictionary formData) var containerContent = new MultipartFormDataContent(); foreach (var item in formData) { - var file = formData as IFile; + var file = item.Value as IFile; if (file != null) { From c8e9348a1c4e9fc481de39aad4721ffac4f7e75a Mon Sep 17 00:00:00 2001 From: sandorfr Date: Tue, 28 Mar 2017 03:59:50 +0200 Subject: [PATCH 28/28] Add missing SafeToString to ClientBase --- src/GeekLearning.RestKit.Core/ClientBase.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/GeekLearning.RestKit.Core/ClientBase.cs b/src/GeekLearning.RestKit.Core/ClientBase.cs index 43f4703..9088bc8 100644 --- a/src/GeekLearning.RestKit.Core/ClientBase.cs +++ b/src/GeekLearning.RestKit.Core/ClientBase.cs @@ -153,6 +153,15 @@ protected IFormData GetFormData(object data) return new FormData(data); } + protected string SafeToString(object value) + { + if (value == null) + { + return null; + } + return value.ToString(); + } + /// /// Append the given query keys and values to the uri. ///