Flurl.Http initial commit
This commit is contained in:
parent
c8549bd987
commit
22b1fabed0
36
Flurl.Http/ClientConfigExtensions.cs
Normal file
36
Flurl.Http/ClientConfigExtensions.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
namespace Flurl.Http
|
||||
{
|
||||
public static class ClientConfigExtensions
|
||||
{
|
||||
public static FlurlClient WithTimeout(this FlurlClient client, TimeSpan timespan) {
|
||||
client.HttpClient.Timeout = timespan;
|
||||
return client;
|
||||
}
|
||||
|
||||
public static FlurlClient WithTimeout(this string url, TimeSpan timespan) {
|
||||
return new FlurlClient(url).WithTimeout(timespan);
|
||||
}
|
||||
|
||||
public static FlurlClient WithTimeout(this Url url, TimeSpan timespan) {
|
||||
return new FlurlClient(url).WithTimeout(timespan);
|
||||
}
|
||||
|
||||
public static FlurlClient WithTimeout(this FlurlClient client, int seconds) {
|
||||
return client.WithTimeout(TimeSpan.FromSeconds(seconds));
|
||||
}
|
||||
|
||||
public static FlurlClient WithTimeout(this string url, int seconds) {
|
||||
return new FlurlClient(url).WithTimeout(seconds);
|
||||
}
|
||||
|
||||
public static FlurlClient WithTimeout(this Url url, int seconds) {
|
||||
return new FlurlClient(url).WithTimeout(seconds);
|
||||
}
|
||||
}
|
||||
}
|
44
Flurl.Http/DownloadExtensions.cs
Normal file
44
Flurl.Http/DownloadExtensions.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using PCLStorage;
|
||||
|
||||
namespace Flurl.Http
|
||||
{
|
||||
public static class DownloadExtensions
|
||||
{
|
||||
public static async Task<string> DownloadAsync(this FlurlClient client, string localFolderPath, string localFileName = null, int bufferSize = 4096) {
|
||||
if (localFileName == null)
|
||||
localFileName = client.Url.Path.Split('/').Last();
|
||||
|
||||
|
||||
var folder = await EnsureFolderAsync(localFolderPath);
|
||||
var file = await folder.CreateFileAsync(localFileName, CreationCollisionOption.ReplaceExisting);
|
||||
|
||||
// http://developer.greenbutton.com/downloading-large-files-with-the-net-httpclient
|
||||
var response = await client.HttpClient.GetAsync(client.Url, HttpCompletionOption.ResponseHeadersRead);
|
||||
|
||||
// http://codereview.stackexchange.com/a/18679
|
||||
using (var httpStream = await response.Content.ReadAsStreamAsync())
|
||||
using (var fileStream = await file.OpenAsync(FileAccess.ReadAndWrite)) {
|
||||
await httpStream.CopyToAsync(fileStream, bufferSize);
|
||||
}
|
||||
|
||||
return PortablePath.Combine(localFolderPath, localFileName);
|
||||
}
|
||||
|
||||
public static Task<string> DownloadAsync(this string url, string localFolderPath, string localFileName = null, int bufferSize = 4096) {
|
||||
return new FlurlClient(url).DownloadAsync(localFolderPath, localFileName, bufferSize);
|
||||
}
|
||||
|
||||
public static Task<string> DownloadAsync(this Url url, string localFolderPath, string localFileName = null, int bufferSize = 4096) {
|
||||
return new FlurlClient(url).DownloadAsync(localFolderPath, localFileName, bufferSize);
|
||||
}
|
||||
|
||||
private static Task<IFolder> EnsureFolderAsync(string path) {
|
||||
return FileSystem.Current.LocalStorage.CreateFolderAsync(path, CreationCollisionOption.OpenIfExists);
|
||||
}
|
||||
}
|
||||
}
|
116
Flurl.Http/Flurl.Http.csproj
Normal file
116
Flurl.Http/Flurl.Http.csproj
Normal file
@ -0,0 +1,116 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{4C163DD1-318A-42C0-ACF2-9E0D766D8900}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Flurl.Http</RootNamespace>
|
||||
<AssemblyName>Flurl.Http</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile>Profile158</TargetFrameworkProfile>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>bin\Release\Flurl.Http.XML</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- A reference to the entire .NET Framework is automatically included -->
|
||||
<ProjectReference Include="..\Flurl\Flurl.csproj">
|
||||
<Project>{70a34167-759e-4902-82e0-e6a84c2ce46f}</Project>
|
||||
<Name>Flurl</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ClientConfigExtensions.cs" />
|
||||
<Compile Include="DownloadExtensions.cs" />
|
||||
<Compile Include="FlurlHttpException.cs" />
|
||||
<Compile Include="GetExtensions.cs" />
|
||||
<Compile Include="JsonHelper.cs" />
|
||||
<Compile Include="PostExtensions.cs" />
|
||||
<Compile Include="FlurlClient.cs" />
|
||||
<Compile Include="FlurlHttp.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="FlurlMessageHandler.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Threading.Tasks">
|
||||
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.165\lib\portable-net40+sl4+win8+wp71\Microsoft.Threading.Tasks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Threading.Tasks.Extensions">
|
||||
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.165\lib\portable-net40+sl4+win8+wp71\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\packages\Newtonsoft.Json.6.0.1\lib\portable-net40+sl5+wp80+win8+monotouch+monoandroid\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PCLStorage">
|
||||
<HintPath>..\packages\PCLStorage.0.9.4\lib\portable-net45+sl5+wp8+win8+monoandroid+monotouch\PCLStorage.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PCLStorage.Abstractions">
|
||||
<HintPath>..\packages\PCLStorage.0.9.4\lib\portable-net45+sl5+wp8+win8+monoandroid+monotouch\PCLStorage.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.IO">
|
||||
<HintPath>..\packages\Microsoft.Bcl.1.1.6\lib\portable-net40+sl5+win8+wp8\System.IO.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http">
|
||||
<HintPath>..\packages\Microsoft.Net.Http.2.2.18\lib\portable-net40+sl4+win8+wp71\System.Net.Http.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.Extensions">
|
||||
<HintPath>..\packages\Microsoft.Net.Http.2.2.18\lib\portable-net40+sl4+win8+wp71\System.Net.Http.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.Primitives">
|
||||
<HintPath>..\packages\Microsoft.Net.Http.2.2.18\lib\portable-net40+sl4+win8+wp71\System.Net.Http.Primitives.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime">
|
||||
<HintPath>..\packages\Microsoft.Bcl.1.1.6\lib\portable-net40+sl5+win8+wp8\System.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks">
|
||||
<HintPath>..\packages\Microsoft.Bcl.1.1.6\lib\portable-net40+sl5+win8+wp8\System.Threading.Tasks.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets')" />
|
||||
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
|
||||
<Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
|
||||
</Target>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
42
Flurl.Http/FlurlClient.cs
Normal file
42
Flurl.Http/FlurlClient.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flurl.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple container for a Url and an HttpClient, used for fluent chaining.
|
||||
/// </summary>
|
||||
public class FlurlClient
|
||||
{
|
||||
public FlurlClient(string url) : this(new Url(url)) { }
|
||||
|
||||
public FlurlClient(Url url) {
|
||||
this.Url = url;
|
||||
}
|
||||
|
||||
private HttpClient _httpClient;
|
||||
|
||||
public Url Url { get; private set; }
|
||||
|
||||
public HttpClient HttpClient {
|
||||
get {
|
||||
if (_httpClient == null) {
|
||||
var handler = new FlurlMesageHandler();
|
||||
_httpClient = new HttpClient(handler) {
|
||||
Timeout = FlurlHttp.DefaultTimeout
|
||||
};
|
||||
}
|
||||
return _httpClient;
|
||||
}
|
||||
set {
|
||||
if (!FlurlHttp.TestMode)
|
||||
_httpClient = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
55
Flurl.Http/FlurlHttp.cs
Normal file
55
Flurl.Http/FlurlHttp.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flurl.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// A static configuration object affecting global behavior Flurl HTTP methods
|
||||
/// </summary>
|
||||
public static class FlurlHttp
|
||||
{
|
||||
/// <summary>
|
||||
/// Get or set whether to actually execute HTTP methods
|
||||
/// </summary>
|
||||
public static bool TestMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default timeout for every HTTP request.
|
||||
/// </summary>
|
||||
public static TimeSpan DefaultTimeout { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Register a callback to occur immediately before every HTTP request is sent.
|
||||
/// </summary>
|
||||
public static Action<HttpRequestMessage> BeforeCall { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Register a callback to occur immediately after every HTTP response is received.
|
||||
/// </summary>
|
||||
public static Action<HttpRequestMessage, HttpResponseMessage> AfterCall { get; set; }
|
||||
|
||||
static FlurlHttp() {
|
||||
DefaultTimeout = new HttpClient().Timeout;
|
||||
BeforeCall = req => { };
|
||||
AfterCall = (req, resp) => { };
|
||||
}
|
||||
|
||||
public static class Testing
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the last-sent HttpRequestMessage ONLY when TestMode = true
|
||||
/// </summary>
|
||||
public static HttpRequestMessage LastRequest { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the string content of the last-sent HTTP request ONLY when TestMode = true
|
||||
/// </summary>
|
||||
public static string LastRequestBody { get; internal set; }
|
||||
}
|
||||
}
|
||||
}
|
34
Flurl.Http/FlurlHttpException.cs
Normal file
34
Flurl.Http/FlurlHttpException.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
namespace Flurl.Http
|
||||
{
|
||||
public class FlurlHttpException : HttpRequestException
|
||||
{
|
||||
public HttpRequestMessage Request { get; private set; }
|
||||
public HttpResponseMessage Response { get; private set; }
|
||||
|
||||
public FlurlHttpException(HttpRequestMessage request, HttpResponseMessage response, Exception inner)
|
||||
: base(BuildMessage(request, response, inner), inner)
|
||||
{
|
||||
this.Request = request;
|
||||
this.Response = response;
|
||||
}
|
||||
|
||||
public FlurlHttpException(HttpRequestMessage request, HttpResponseMessage response)
|
||||
: this(request, response, null) { }
|
||||
|
||||
private static string BuildMessage(HttpRequestMessage request, HttpResponseMessage response, Exception inner) {
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return string.Format("Request to {0} failed with status {1} ({2}).", request.RequestUri.AbsoluteUri, (int)response.StatusCode, response.ReasonPhrase);
|
||||
else if (inner != null)
|
||||
return string.Format("Request to {0} failed. {1}", request.RequestUri.AbsoluteUri, inner.Message);
|
||||
|
||||
// in theory we should never get here.
|
||||
return string.Format("Request to {0} failed.", request.RequestUri.AbsoluteUri);
|
||||
}
|
||||
}
|
||||
}
|
49
Flurl.Http/FlurlMessageHandler.cs
Normal file
49
Flurl.Http/FlurlMessageHandler.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flurl.Http
|
||||
{
|
||||
internal class FlurlMesageHandler : HttpClientHandler
|
||||
{
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
|
||||
FlurlHttp.BeforeCall(request);
|
||||
|
||||
if (FlurlHttp.TestMode) {
|
||||
FlurlHttp.Testing.LastRequest = request;
|
||||
FlurlHttp.Testing.LastRequestBody = await request.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
HttpResponseMessage response = null;
|
||||
try {
|
||||
response = FlurlHttp.TestMode ? GetTestResponse() : await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
catch (TaskCanceledException) {
|
||||
throw new TimeoutException(string.Format("Request to {0} timed out.", request.RequestUri.AbsoluteUri));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new FlurlHttpException(request, response, ex);
|
||||
}
|
||||
finally {
|
||||
FlurlHttp.AfterCall(request, response);
|
||||
}
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new FlurlHttpException(request, response);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private static HttpResponseMessage GetTestResponse() {
|
||||
return new HttpResponseMessage {
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Content = new StringContent("{ message: 'Hello!' }")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
36
Flurl.Http/GetExtensions.cs
Normal file
36
Flurl.Http/GetExtensions.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flurl.Http
|
||||
{
|
||||
public static class GetExtensions
|
||||
{
|
||||
public static async Task<T> GetJsonAsync<T>(this FlurlClient client) {
|
||||
using (var stream = await client.HttpClient.GetStreamAsync(client.Url))
|
||||
return JsonHelper.ReadJsonFromStream<T>(stream);
|
||||
}
|
||||
|
||||
public static Task<T> GetJsonAsync<T>(this string url) {
|
||||
return new FlurlClient(url).GetJsonAsync<T>();
|
||||
}
|
||||
|
||||
public static Task<T> GetJsonAsync<T>(this Url url) {
|
||||
return new FlurlClient(url).GetJsonAsync<T>();
|
||||
}
|
||||
|
||||
public static async Task<dynamic> GetJsonAsync(this FlurlClient client) {
|
||||
dynamic d = await client.GetJsonAsync<ExpandoObject>();
|
||||
return d;
|
||||
}
|
||||
|
||||
public static Task<dynamic> GetJsonAsync(this string url) {
|
||||
return new FlurlClient(url).GetJsonAsync();
|
||||
}
|
||||
|
||||
public static Task<dynamic> GetJsonAsync(this Url url) {
|
||||
return new FlurlClient(url).GetJsonAsync();
|
||||
}
|
||||
}
|
||||
}
|
20
Flurl.Http/JsonHelper.cs
Normal file
20
Flurl.Http/JsonHelper.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Flurl.Http
|
||||
{
|
||||
public static class JsonHelper
|
||||
{
|
||||
public static T ReadJsonFromStream<T>(Stream stream) {
|
||||
// http://james.newtonking.com/json/help/index.html?topic=html/Performance.htm
|
||||
using (var sr = new StreamReader(stream))
|
||||
using (var jr = new JsonTextReader(sr)) {
|
||||
return new JsonSerializer().Deserialize<T>(jr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
29
Flurl.Http/PostExtensions.cs
Normal file
29
Flurl.Http/PostExtensions.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Flurl.Http
|
||||
{
|
||||
public static class PostExtensions
|
||||
{
|
||||
public static async Task<T> PostJsonAsync<T>(this FlurlClient client, object data) {
|
||||
var content = (HttpContent)new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
|
||||
var resp = await client.HttpClient.PostAsync(client.Url, content);
|
||||
using (var stream = await resp.Content.ReadAsStreamAsync())
|
||||
return JsonHelper.ReadJsonFromStream<T>(stream);
|
||||
}
|
||||
|
||||
public static Task<T> PostJsonAsync<T>(this string url, object data) {
|
||||
return new FlurlClient(url).PostJsonAsync<T>(data);
|
||||
}
|
||||
|
||||
public static Task<T> PostJsonAsync<T>(this Url url, object data) {
|
||||
return new FlurlClient(url).PostJsonAsync<T>(data);
|
||||
}
|
||||
}
|
||||
}
|
30
Flurl.Http/Properties/AssemblyInfo.cs
Normal file
30
Flurl.Http/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Resources;
|
||||
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: AssemblyTitle("Flurl.Http")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Microsoft")]
|
||||
[assembly: AssemblyProduct("Flurl.Http")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2014")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: NeutralResourcesLanguage("en")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
15
Flurl.Http/app.config
Normal file
15
Flurl.Http/app.config
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.6.6.0" newVersion="2.6.6.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.6.6.0" newVersion="2.6.6.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
9
Flurl.Http/packages.config
Normal file
9
Flurl.Http/packages.config
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Bcl" version="1.1.6" targetFramework="portable-win+net45+sl50+wp80" />
|
||||
<package id="Microsoft.Bcl.Async" version="1.0.165" targetFramework="portable-win+net45+sl50+wp80" />
|
||||
<package id="Microsoft.Bcl.Build" version="1.0.10" targetFramework="portable-win+net45+sl50+wp80" />
|
||||
<package id="Microsoft.Net.Http" version="2.2.18" targetFramework="portable-win+net45+sl50+wp80" />
|
||||
<package id="Newtonsoft.Json" version="6.0.1" targetFramework="portable-win+net45+sl50+wp80" />
|
||||
<package id="PCLStorage" version="0.9.4" targetFramework="portable-win+net45+sl50+wp80" />
|
||||
</packages>
|
@ -32,11 +32,39 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Newtonsoft.Json.6.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework">
|
||||
<HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PCLStorage, Version=0.9.4.0, Culture=neutral, PublicKeyToken=286fe515a2c35b64, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\PCLStorage.0.9.4\lib\net45\PCLStorage.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PCLStorage.Abstractions, Version=0.9.4.0, Culture=neutral, PublicKeyToken=286fe515a2c35b64, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\PCLStorage.0.9.4\lib\net45\PCLStorage.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Net.Http.Extensions">
|
||||
<HintPath>..\packages\Microsoft.Net.Http.2.2.18\lib\net45\System.Net.Http.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.Formatting, Version=5.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.1.1\lib\net45\System.Net.Http.Formatting.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.Primitives">
|
||||
<HintPath>..\packages\Microsoft.Net.Http.2.2.18\lib\net45\System.Net.Http.Primitives.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.WebRequest" />
|
||||
<Reference Include="System.Web.Http, Version=5.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.1.1\lib\net45\System.Web.Http.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
@ -44,20 +72,32 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="HttpTests.cs" />
|
||||
<Compile Include="ReflectionHelper.cs" />
|
||||
<Compile Include="UrlBuilderTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Flurl.Http\Flurl.Http.csproj">
|
||||
<Project>{4c163dd1-318a-42c0-acf2-9e0d766d8900}</Project>
|
||||
<Name>Flurl.Http</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Flurl\Flurl.csproj">
|
||||
<Project>{70a34167-759e-4902-82e0-e6a84c2ce46f}</Project>
|
||||
<Name>Flurl</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
|
||||
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets')" />
|
||||
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
|
||||
<Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
|
||||
</Target>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
77
Flurl.Test/HttpTests.cs
Normal file
77
Flurl.Test/HttpTests.cs
Normal file
@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Http;
|
||||
using NUnit.Framework;
|
||||
using Flurl.Http;
|
||||
|
||||
namespace Flurl.Test
|
||||
{
|
||||
[TestFixture]
|
||||
public class HttpTests
|
||||
{
|
||||
[Test]
|
||||
// check that for every FlurlClient extension method, we have an equivalent Url and string extension
|
||||
public void extension_methods_consistently_supported() {
|
||||
var allExtMethods = ReflectionHelper.GetAllExtensionMethods(typeof(FlurlClient).Assembly);
|
||||
var fcExtensions = allExtMethods.Where(m => m.GetParameters()[0].ParameterType == typeof(FlurlClient)).ToArray();
|
||||
|
||||
foreach (var method in fcExtensions) {
|
||||
foreach (var type in new[] { typeof(string), typeof(Url) }) {
|
||||
if (!allExtMethods.Except(fcExtensions).Any(m => ReflectionHelper.IsEquivalentExtensionMethod(method, m, type))) {
|
||||
Assert.Fail("No equivalent {0} extension method found for FlurlClient.{1}", type.Name, method.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void can_set_timeout() {
|
||||
var client = "http://www.google.com".WithTimeout(15);
|
||||
Assert.AreEqual(client.HttpClient.Timeout, TimeSpan.FromSeconds(15));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task can_download_file() {
|
||||
var path = await "http://www.google.com".DownloadAsync(@"c:\a\b", "google.txt");
|
||||
Assert.That(File.Exists(path));
|
||||
File.Delete(path);
|
||||
Directory.Delete(@"c:\a", true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task can_post_json() {
|
||||
FlurlHttp.TestMode = true;
|
||||
await "http://some-api.com".PostJsonAsync<object>(new { a = 1, b = 2 });
|
||||
|
||||
Assert.AreEqual(HttpMethod.Post, FlurlHttp.Testing.LastRequest.Method);
|
||||
Assert.AreEqual("{\"a\":1,\"b\":2}", FlurlHttp.Testing.LastRequestBody);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task can_get_json_dynamic() {
|
||||
FlurlHttp.TestMode = false;
|
||||
var result = await "http://echo.jsontest.com/key/value/one/two".GetJsonAsync();
|
||||
Assert.AreEqual("value", result.key);
|
||||
Assert.AreEqual("two", result.one);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task can_get_json_strongly_typed() {
|
||||
FlurlHttp.TestMode = false;
|
||||
var result = await "http://echo.jsontest.com/key/value/one/two".GetJsonAsync<JsonTestData>();
|
||||
Assert.AreEqual("value", result.key);
|
||||
Assert.AreEqual("two", result.one);
|
||||
}
|
||||
|
||||
class JsonTestData
|
||||
{
|
||||
public string key { get; set; }
|
||||
public string one { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
103
Flurl.Test/ReflectionHelper.cs
Normal file
103
Flurl.Test/ReflectionHelper.cs
Normal file
@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Flurl.Test
|
||||
{
|
||||
public static class ReflectionHelper
|
||||
{
|
||||
public static MethodInfo[] GetAllExtensionMethods(Assembly asm) {
|
||||
// http://stackoverflow.com/a/299526/62600
|
||||
return (
|
||||
from type in asm.GetTypes()
|
||||
where type.IsSealed && !type.IsGenericType && !type.IsNested
|
||||
from method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
where method.IsDefined(typeof(ExtensionAttribute), false)
|
||||
select method).ToArray();
|
||||
}
|
||||
|
||||
public static bool IsEquivalentExtensionMethod(MethodInfo method1, MethodInfo method2, Type method2ExtType) {
|
||||
if (method1.Name != method2.Name)
|
||||
return false;
|
||||
|
||||
if (!AreSameType(method1.ReturnType, method2.ReturnType))
|
||||
return false;
|
||||
|
||||
var genArgs1 = method1.GetGenericArguments();
|
||||
var genArgs2 = method2.GetGenericArguments();
|
||||
|
||||
if (genArgs1.Length != genArgs2.Length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < genArgs1.Length; i++) {
|
||||
if (!AreSameType(genArgs1[i], genArgs2[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
var args1 = method1.GetParameters();
|
||||
var args2 = method2.GetParameters();
|
||||
|
||||
if (args1.Length != args2.Length)
|
||||
return false;
|
||||
|
||||
if (!AreSameType(args2[0].ParameterType, method2ExtType))
|
||||
return false;
|
||||
|
||||
for (int i = 1; i < args1.Length; i++) {
|
||||
if (args1[i].Name != args2[i].Name) return false;
|
||||
if (!AreSameType(args1[i].ParameterType, args2[i].ParameterType)) return false;
|
||||
if (args1[i].IsOptional != args2[i].IsOptional) return false;
|
||||
if (!AreSameValue(args1[i].DefaultValue, args2[i].DefaultValue)) return false;
|
||||
if (args1[i].IsIn != args2[i].IsIn) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool AreSameValue(object a, object b) {
|
||||
if (a == null && b == null)
|
||||
return true;
|
||||
if (a == null ^ b == null)
|
||||
return false;
|
||||
// ok, neither is null
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool AreSameType(Type a, Type b) {
|
||||
if (a.IsGenericParameter && b.IsGenericParameter) {
|
||||
var constraintsA = a.GetGenericParameterConstraints();
|
||||
var constraintsB = b.GetGenericParameterConstraints();
|
||||
|
||||
if (constraintsA.Length != constraintsB.Length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < constraintsA.Length; i++) {
|
||||
if (!AreSameType(constraintsA[i], constraintsB[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (a.IsGenericType && b.IsGenericType) {
|
||||
if (a.GetGenericTypeDefinition() != b.GetGenericTypeDefinition())
|
||||
return false;
|
||||
|
||||
var genArgsA = a.GetGenericArguments();
|
||||
var genArgsB = b.GetGenericArguments();
|
||||
|
||||
if (genArgsA.Length != genArgsB.Length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < genArgsA.Length; i++) {
|
||||
if (!AreSameType(genArgsA[i], genArgsB[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return a == b;
|
||||
}
|
||||
}
|
||||
}
|
15
Flurl.Test/app.config
Normal file
15
Flurl.Test/app.config
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.18.0" newVersion="4.2.18.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
@ -1,4 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.AspNet.WebApi.Client" version="5.1.1" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.WebApi.Core" version="5.1.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Bcl" version="1.1.3" targetFramework="net45" />
|
||||
<package id="Microsoft.Bcl.Build" version="1.0.10" targetFramework="net45" />
|
||||
<package id="Microsoft.Net.Http" version="2.2.18" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="6.0.1" targetFramework="net45" />
|
||||
<package id="NUnit" version="2.6.3" targetFramework="net45" />
|
||||
<package id="PCLStorage" version="0.9.4" targetFramework="net45" />
|
||||
</packages>
|
@ -17,6 +17,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flurl.Http", "Flurl.Http\Flurl.Http.csproj", "{4C163DD1-318A-42C0-ACF2-9E0D766D8900}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -31,6 +33,10 @@ Global
|
||||
{81F583F8-1D15-4E0B-8064-EB892042C09A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{81F583F8-1D15-4E0B-8064-EB892042C09A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{81F583F8-1D15-4E0B-8064-EB892042C09A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4C163DD1-318A-42C0-ACF2-9E0D766D8900}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4C163DD1-318A-42C0-ACF2-9E0D766D8900}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4C163DD1-318A-42C0-ACF2-9E0D766D8900}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4C163DD1-318A-42C0-ACF2-9E0D766D8900}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
4
Flurl.sln.DotSettings.user
Normal file
4
Flurl.sln.DotSettings.user
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user