diff --git a/GeekLearning.RestKit.sln b/GeekLearning.RestKit.sln
index ef0d638..bc7aebd 100644
--- a/GeekLearning.RestKit.sln
+++ b/GeekLearning.RestKit.sln
@@ -7,6 +7,12 @@ 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
+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
@@ -21,6 +27,18 @@ 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
+ {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/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
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.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..eb97e95
--- /dev/null
+++ b/src/GeekLearning.Http.Logging/HttpRequestLogger.cs
@@ -0,0 +1,107 @@
+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;
+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) : 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)
+ {
+ var correlationId = timebaseId.NewId();
+
+ if (request.Content != null)
+ {
+ await request.Content.LoadIntoBufferAsync();
+ var requestBody = await 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}: {string.Join(" ", h.Value)}")),
+ TruncateMessageIfTooBig(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}: {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,
+ response.ReasonPhrase,
+ correlationId,
+ string.Join("\n", response.Headers.Select(h => $"{h.Key}: {string.Join(" ", h.Value)}")),
+ 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
+ where TInnerHandler : HttpMessageHandler, new()
+ {
+ public HttpRequestLogger(ILogger logger) : base(new TInnerHandler(), logger)
+ {
+ }
+ }
+}
diff --git a/src/GeekLearning.Http.Logging/HttpRequestLoggerOptions.cs b/src/GeekLearning.Http.Logging/HttpRequestLoggerOptions.cs
new file mode 100644
index 0000000..f4ec432
--- /dev/null
+++ b/src/GeekLearning.Http.Logging/HttpRequestLoggerOptions.cs
@@ -0,0 +1,24 @@
+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; }
+
+ public int MaxSize { get; set; } = 512000;
+ }
+}
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"
+ }
+ }
+}
diff --git a/src/GeekLearning.RestKit.Core/ApiException.cs b/src/GeekLearning.RestKit.Core/ApiException.cs
index ee6db6e..73497fe 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.ResponseMessage = response;
+ }
+
+ 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 6073014..9088bc8 100644
--- a/src/GeekLearning.RestKit.Core/ClientBase.cs
+++ b/src/GeekLearning.RestKit.Core/ClientBase.cs
@@ -11,34 +11,49 @@
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;
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()
+ 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)
{
- IMediaFormatter mediaFormatter = this.mediaFormatterProvider.GetMediaFormatter(message.Content.Headers.ContentType);
- if (mediaFormatter == null)
+ if (message.Content != null && message.Content.Headers.ContentLength > 0)
{
- throw new UnsupportedMediaTypeApiException(message.Content.Headers.ContentType);
+ IMediaFormatter mediaFormatter = this.mediaFormatterProvider.GetMediaFormatter(message.Content.Headers.ContentType);
+ if (mediaFormatter == null)
+ {
+ throw new UnsupportedMediaTypeApiException(message);
+ }
+ return mediaFormatter.TransformAsync(message.Content);
+ }
+ else
+ {
+ return Task.FromResult(default(TTarget));
}
- return mediaFormatter.TransformAsync(message.Content);
}
protected HttpRequestMessage ApplyFilters(HttpRequestMessage requestMessage, params string[] securityDefinitions)
@@ -52,14 +67,99 @@ 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);
+ }
+
+ protected ApiException MapToException(HttpResponseMessage message)
+ {
+ switch (message.StatusCode)
+ {
+ case HttpStatusCode.BadRequest:
+ return new BadRequestApiException(message);
+ case HttpStatusCode.Unauthorized:
+ return new UnauthorizedApiException(message);
+ case HttpStatusCode.Forbidden:
+ return new ForbiddenApiException(message);
+ case HttpStatusCode.NotFound:
+ return new NotFoundApiException(message);
+ case HttpStatusCode.Conflict:
+ return new ConflictApiException(message);
+ case HttpStatusCode.InternalServerError:
+ return new InternalServerErrorApiException(message);
+ case HttpStatusCode.ServiceUnavailable:
+ return new ServiceUnavailableApiException(message);
+ default:
+ return new UnhandledApiException(message);
+ }
+ }
+
+ protected async Task MapToException(HttpResponseMessage message)
+ {
+ if (message.Content == null)
+ {
+ return this.MapToException(message);
+ }
+ else
+ {
+ var result = await this.TransformResponseAsync(message);
+
+ switch (message.StatusCode)
+ {
+ case HttpStatusCode.BadRequest:
+ return new BadRequestApiException(message, result);
+ case HttpStatusCode.Unauthorized:
+ return new UnauthorizedApiException(message, result);
+ case HttpStatusCode.Forbidden:
+ return new ForbiddenApiException(message, result);
+ case HttpStatusCode.NotFound:
+ return new NotFoundApiException(message, result);
+ case HttpStatusCode.Conflict:
+ return new ConflictApiException(message, result);
+ case HttpStatusCode.InternalServerError:
+ return new ConflictApiException(message, result);
+ case HttpStatusCode.ServiceUnavailable:
+ return new ServiceUnavailableApiException(message, result);
+ default:
+ return 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(object data)
+ {
+ var formData = data as IFormData;
+ if (formData != null)
+ {
+ return formData;
+ }
+ return new FormData(data);
+ }
+
+ protected string SafeToString(object value)
+ {
+ if (value == null)
+ {
+ return null;
+ }
+ return value.ToString();
}
///
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/ConflictApiException.cs b/src/GeekLearning.RestKit.Core/ConflictApiException.cs
index d020401..10bd97e 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 : ConflictApiException, IApiException
{
+ public ConflictApiException(HttpResponseMessage message, TResponse response) : base(message)
+ {
+ this.Response = response;
+ }
+
+ public TResponse Response { get; }
}
}
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/FileFactory.cs b/src/GeekLearning.RestKit.Core/FileFactory.cs
new file mode 100644
index 0000000..b7ad808
--- /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
+ {
+ public IFile Get(byte[] data, string fileName)
+ {
+ return Get(new MemoryStream(data), fileName);
+ }
+
+ public IFile Get(Stream stream, string fileName)
+ {
+ return new Internal.StreamFormFile(stream, fileName);
+ }
+
+ public IFile Get(FileStream stream)
+ {
+ return Get(stream, stream.Name);
+ }
+ }
+}
diff --git a/src/GeekLearning.RestKit.Core/ForbiddenApiException.cs b/src/GeekLearning.RestKit.Core/ForbiddenApiException.cs
index 919c96b..ff88864 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 : ForbiddenApiException, IApiException
{
+ public ForbiddenApiException(HttpResponseMessage message, TResponse response) : base(message)
+ {
+ this.Response = response;
+ }
+
+ public TResponse Response { get; }
}
}
diff --git a/src/GeekLearning.RestKit.Core/FormData.cs b/src/GeekLearning.RestKit.Core/FormData.cs
new file mode 100644
index 0000000..a2fbbdc
--- /dev/null
+++ b/src/GeekLearning.RestKit.Core/FormData.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace GeekLearning.RestKit.Core
+{
+ public class FormData: IFormData
+ {
+ public FormData(object data)
+ {
+ Data = data;
+ }
+
+ 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/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/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/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/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/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/Internal/MediaFormatterProvider.cs b/src/GeekLearning.RestKit.Core/Internal/MediaFormatterProvider.cs
index c81098e..9f104f5 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 GetMediaFormatter(new ParsedMediaType(mediaType));
+ }
+
+ public IMediaFormatter GetMediaFormatter(ParsedMediaType mediaType)
+ {
+ return mediaFormatters.FirstOrDefault(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/InternalServerErrorApiException.cs b/src/GeekLearning.RestKit.Core/InternalServerErrorApiException.cs
new file mode 100644
index 0000000..22d50dc
--- /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 : InternalServerErrorApiException, 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..5e2368b 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 : NotFoundApiException, IApiException
{
+ public NotFoundApiException(HttpResponseMessage message, TResponse response) : base(message)
+ {
+ this.Response = response;
+ }
+
+ public TResponse Response { get; }
}
}
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/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);
diff --git a/src/GeekLearning.RestKit.Core/ServiceUnavailableApiException.cs b/src/GeekLearning.RestKit.Core/ServiceUnavailableApiException.cs
new file mode 100644
index 0000000..ad0f3fc
--- /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 : ServiceUnavailableApiException, 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..a0ab5b2 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 : UnauthorizedApiException, 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 b6a7035..a81ca3d 100644
--- a/src/GeekLearning.RestKit.Core/UnhandledApiException.cs
+++ b/src/GeekLearning.RestKit.Core/UnhandledApiException.cs
@@ -1,16 +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(HttpResponseMessage message)
+ public UnhandledApiException()
{
+ }
+ public UnhandledApiException(HttpResponseMessage message) : base(message)
+ {
}
}
+
+ public class UnhandledApiException : UnhandledApiException, IApiException
+ {
+ public UnhandledApiException(HttpResponseMessage message, TResponse response) : base(message)
+ {
+ this.Response = response;
+ }
+
+ public TResponse Response { get; }
+ }
}
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.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/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..cbd4145
--- /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 = item.Value 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/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();
+ }
+ }
+}
diff --git a/src/GeekLearning.RestKit.FormData/project.json b/src/GeekLearning.RestKit.FormData/project.json
new file mode 100644
index 0000000..c079c4a
--- /dev/null
+++ b/src/GeekLearning.RestKit.FormData/project.json
@@ -0,0 +1,15 @@
+{
+ "version": "1.0.0-*",
+
+ "dependencies": {
+ "NETStandard.Library": "1.6.0",
+ "GeekLearning.RestKit.Core": "*"
+ },
+
+ "frameworks": {
+ "net452": {},
+ "netstandard1.3": {
+ "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": {
+ }
+ }
+ }
+}