tmenier / flurl Goto Github PK
View Code? Open in Web Editor NEWFluent URL builder and testable HTTP client for .NET
Home Page: https://flurl.dev
License: MIT License
Fluent URL builder and testable HTTP client for .NET
Home Page: https://flurl.dev
License: MIT License
Great Library, I'm very happy with it so far. Only hiccup was when testing error callbacks with the builtin httpTest framework the error callbacks didn't work, the exceptions were never caught. Since the before callback does work this is confusing to me.
Had to use an external Mock Server to test the error callbacks, seems a little bit unnecessary.
Currently, new Url("http://abc.com/?a=1&a=2") throws an error - which it shouldn't as this is valid URL.
As for behavior in this case, there are multiple options, but I guess best would be to create an array of strings in this case...
Thanks, Mihi
At a minimum, support XDocuments (get, post, etc). XPath and deserialization are other possibilities.
I need to receive cookies from a site.
One option would be to send a FlurlMessageHandler initialized with a CookieContainer to the DefaultHttpClientFactory constructor, but that is not supported.
Another option is to create a custom HttpClientFactory and not call the base constructor. Something like this:
public class MyCustomHttpClientFactory : DefaultHttpClientFactory
{
public virtual HttpClient CreateClient(Url url, CookieContainer cookieContainer) {
var customHttpClientHandler = new HttpClientHandler() { CookieContainer = cookieContainer };
return new HttpClient(new FlurlMessageHandler(customHttpClientHandler)) {
Timeout = FlurlHttp.Configuration.DefaultTimeout
};
}
}
But FlurlMessageHandler is internal, so no luck.
Any ideas?
Hello @tmenier
I have a problem by using the flurl httpclient. It won't download the complete data.
var httpClient = new HttpClient();
var test = await httpClient.GetAsync("http://rebuy.de");
var testResponse = await test.Content.ReadAsStringAsync();var flurl = await "http://rebuy.de".GetAsync();
var flurlResponse = await flurl.Content.ReadAsStringAsync();
Compare the two strings. testResponse
has 57697 letters and flurlResponse
has 32397 letters.
Can you help me with that problem?
More specifically, figure out why it stopped working in the first place.
AppendPathSegment trims the trailing slash when calling API's like this:
// Example: "https://api.site.com/v1/endpoint/?param=value"
var URL = "https://api.site.com/v1/".AppendPathSegment("endpoint").SetQueryParam("param", value)
// Outputs: "https://api.site.com/v1/endpoint?param=value"
Notice the slash after "endpoint", without that slash "/" the API call would fail or improperly redirect (301)
And for now as you suggested, I made an extension method, so it wouldn't break the fluent syntax.
It's different than what you suggested but this gives more control, since this is a special case, most APIs don't have this slash at the end.
namespace Flurl.Extensions
{
/// <summary>
/// A set of string extension methods for working with Flurl URLs
/// </summary>
public static class StringExtensions
{
/// <summary>
/// Explicitly adds a '/' character to the end of the Url.
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static Url AppendPathSeparator(this string url)
{
if (!string.IsNullOrEmpty(url) && !url.EndsWith("/")) url += "/";
return new Url(url);
}
/// <summary>
/// Explicitly adds a '/' character to the end of the Url.
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static Url AppendPathSeparator(this Url url)
{
if (url != null && !string.IsNullOrEmpty(url.Path) && !url.Path.EndsWith("/")) url += "/";
return new Url(url);
}
}
}
I use it like this:
var URL = "https://api.site.com/v1/".AppendPathSegment("endpoint").AppendPathSeparator().SetQueryParam("param", value)
// Outputs: "https://api.site.com/v1/endpoint/?param=value"
Hey, is there a way to use the ModernHttpClient instead of HttpClient?
Hyphens are very common in request header names but not allowed in C# identifiers. Underscores, just the opposite. Allow WithHeaders(object)
to work with hyphenated header names by converting underscores to hyphens. For example:
url.WithHeaders(new { User_Agent = "Flurl", Cache_Control = "no-cache" })
Avoid this behavior with WithHeader(string, string)
and WithHeaders(IDictionary)
, so there's still a way to use underscores in header names if desired.
I am trying to use Flurl with the disqus api (https://disqus.com/api/docs/).
They have in their api the possibility to define multiple values.
https://disqus.com/api/docs/forums/listPosts/
for example, you can provide "include" param multiple values.
Basically you get &limit=100&include=deleted&include=spam&include=approved
When using the library this is not possible...
I tried to build the url myself and then call the .GetJsonAsync but I get an error that there are multiple values...
Maybe it needs to be possible to set an override?
Got some problems au try to use the "SetQueryParams" in other cultures.
When using the extension "ToString ()" as .ToString (CultureInfo.InvariantCulture) resolves the problem I had with floating types.
I believe that passing culture as invariant does not affect most and can solve many cases of problems with numbers.
Is this possible?
Or is there any other way around this?
It is really awesome what Flurl component can do.
But, to me, extend from System.String is out of context.
I understand that, when you extend from an object, this extension still been the same context of the extended object.
With that in mind, I usually do request agains URI routes, instead of whatever text string.
If you OK, I can do a pull request with this behavior.
Will be my pleasure do contribute.
Would it be an idea to opt-out for downloading files feature and thereby not needing the PCLStorage dependency?
A very common scenario is to catch an exception and log the URL and status code. Make this a little easier by exposing these properties directly off FlurlHttpException:
Also, rename Call
to CallDetails
? (would be breaking change)
AppendPathSegment(this string url, params object[] segments)
Then we may pass string, int, decimal, etc... with implicity ToString() convert.
Thanks for Flurl! I'm using it in a project and it really cuts down on the code required to do HTTP stuff.
My project uses a bunch of System.Uri
objects. Is there a reason why Flurl.Url
doesn't have a constructor taking a System.Uri
(or at least static implicit operator Url(System.Uri uri)
), or that the Flurl.Url
functionality even exists outside of extension methods? I have this a lot of places and I hate it.
var html = await _client
.WithUrl(new Url(URL.ToString()))
.GetStringAsync();
I'd be happy to add some code to make the interface a little smoother as long as you approve of this design. I just don't understand why Flurl has its own Url class when it seems like most of the stuff could be done as extension methods of the System.Uri class.
Hi,
Is there a way to get upload and download progress?
I can't find anything about that in the doc.
Just started using Flurl to build URLs with query strings.
I was writing some tests to ensure Flurl encodes reserved and unsafe characters. All tests passed except this one for Tilde ~
characters.
[Test]
public void should_encode_tilde_character()
{
var url = new Flurl.Url("http://localhost").SetQueryParam("query", @"tilde value ~");
url.ToString().ShouldEqual("http://localhost?query=tilde%20value%20%7E");
}
It seems there is some debate about tilde characters being encoded or not.
I'm curious if there's an existing way to call a URL with HEAD using Flurl.Http. I see that there are methods for GET, POST, PUT, DELETE, similar to the underlying HttpClient library. HttpClient also exposes a SendAsync() method that allows you to send a custom HttpRequestMessage and override the HttpMethod.
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Head, "http://www.example.com/api/endpoint");
using (HttpClient client = new HttpClient())
using (HttpResponseMessage response = await client.SendAsync(message))
{
// do some stuff
}
I see that I can create my own extension methods, which would work. If that's the route I should go, are you interested in having that functionality in master? I can create a PR if you are.
Thanks,
Ben
Had a quick stab at converting the library over to PCL v4.5 Profile78 here's what has
been discovered so far:
Pretty straight forward actually and means the library can be used across all platforms
Windows, iOS, Android, Windows Phone, etc. Changing NameValueCollection over
to HttpValueCollection however is a breaking change that warrants incrementing the
major.
Your thoughts on how to progress?
The case:
In one test class I use some code like:
using (var httpTest = new HttpTest())
{
httpTest
.RespondWithJson(new { count = 0, total = 0, order = "[]" });
var result = await _client.CheckOrdersAuthAsync(); //using Flurl.Http
httpTest.ShouldHaveCalled($"{CheckOrdersUrl}&limit=0")
.WithVerb(HttpMethod.Get)
.Times(2);
Assert.Equal(true, result);
}
and in the other test class:
var result = await client.CheckOrdersAuthAsync(); //using a real Flurl.http method
Assert.Equal(true, result);
I have many tests and when I run them all together real test sometimes uses HttpTest responces and throw exceptions.
It's bug or I does not understand something?
Are there any plans to support .net core? I'm using Flurl.Http with ASP.NET 5 and it says Flurl does not support DnxCore5.0
Could you explain what the CodeGen exactly is doing? Thx
Hi,
thank you for the library.
I have come across an issue.
result = await baseUrl.WithTimeout (2).WithBasicAuth ("aaa","bbb").PostJsonAsync (project).ReceiveJson ().ConfigureAwait (false);
When the url is unavailable (server is down) the timeout won't work. It timeouts after cca 60 seconds instead of 2 seconds I set. Setting timeout works in conjunction with GetJsonAsync.
For libraries using Flurl, it would be very helpful to use Flurl with custom json.net serialization settings without clobbering the global json.net serialization settings (see OpenStack.NET #551). I believe this could be supported in Flurl through something like this:
FlurlHttp.Configure(c => {
c.JsonSerializerSettings = new JsonSerializerSettings { ... }
});
Then JsonHelper and CapturedJsonContent, would to specify the serializer settings when serializing/deserializing instead of using JsonConvert directly.
// if JsonSerializerSettings is null, it's the same as if you called JsonSerializer.CreateDefault()
JsonSerializer.CreateDefault(FlurlHttp.Configuration.JsonSerializerSettings)
Does this seem reasonable? I'll submit a PR with tests if you think this is something you would accept.
Hi,
Im using Xamarin PCL
and tried this code:
public async Task GetAccountAsync (string username, string password)
{
AccountDto result = null;
using (var fc = new FlurlClient ().EnableCookies ()) {
try {
result = await ApiConsumer.Url
.AppendPathSegment ("account/token")
.WithClient (fc)
.PostJsonAsync (new { UserName = username, Password = password})
.ReceiveJson<AccountDto> ();
} catch (FlurlHttpTimeoutException) {
Debug.WriteLine ("Timed out!");
} catch (FlurlHttpException ex) {
if (ex.Call.Response != null)
Debug.WriteLine ("Failed with response code ");
else
Debug.WriteLine ("Totally failed before getting a response! " + ex.Message);
}
return result;
}
But i've got exception.
When using the DownloadFileAsync extension method over a bunch of image urls, I receive a few System.Collections.Generic.KeyNotFoundException exceptions. My assumption is the urls are invalid or have no valid file (since I only get 2-3 of these over a large set of urls), but the errors received here are odd and seem out of place - I would expect a http code or file not found, etc.
{System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
at System.ThrowHelper.ThrowKeyNotFoundException () [0x00000] in <filename unknown>:0
at System.Collections.Generic.Dictionary`2[System.String,System.Object].get_Item (System.String key) [0x00021] in <filename unknown>:0
at Flurl.Http.HttpCall.get_Succeeded () [0x00019] in c:\Aldenteware\Flurl\code\Flurl.Http.Shared\HttpCall.cs:79
at Flurl.Http.Configuration.FlurlMessageHandler+<SendAsync>d__1.MoveNext () [0x0021d] in c:\Aldenteware\Flurl\code\Flurl.Http.Shared\Configuration\FlurlMessageHandler.cs:39
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000b] in <filename unknown>:0
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0004e] in <filename unknown>:0
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x0002e] in <filename unknown>:0
at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x0000b] in <filename unknown>:0
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1+ConfiguredTaskAwaiter[System.Net.Http.HttpResponseMessage].GetResult () [0x00000] in <filename unknown>:0
at System.Net.Http.HttpClient+<SendAsyncWorker>c__async0.MoveNext () [0x000f3] in <filename unknown>:0
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000b] in <filename unknown>:0
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0004e] in <filename unknown>:0
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x0002e] in <filename unknown>:0
at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x0000b] in <filename unknown>:0
at System.Runtime.CompilerServices.TaskAwaiter`1[System.String].GetResult () [0x00000] in <filename unknown>:0
at MyApp.Mobile.Droid.Picture+<SavePictureToDiskAsync>c__async0.MoveNext () [0x00077] in /Users/naterickard/Projects/MyApp/projects/mobile/code/MyApp.Mobile/MyApp.Mobile.Droid/PlatformDependencies/Picture.cs:16 } System.Collections.Generic.KeyNotFoundException
Example code:
var baseUrl = "?q=term&page=2#results"
var output = baseUrl.SetQueryParam("page",3).ToString();
Actual output:
"?q=term&page=3"
Expected output
"?q=term&page=3#results"
I've implemented a customisation to use ModernHttpClient as per https://github.com/tmenier/Flu...
However, we find that no Cookies are never retained ( the call to GetCookies() always returns an empty object ).
Is this a known issue? Can we get cookies to work somehow with ModernHttpClient?
Thanks
Joe
When I try to add Flurl.Http to any Xamarin.iOS project using NuGet on Visual Studio and Xamarin Studio, the installation fails with the message Unable to resolve dependency 'Microsoft.Bcl.Build (โฅ 1.0.14)'.
When I install it on a PCL project, it works, however.
I'd like the installation to work for non-PCL Xamarin.iOS project, too.
I suppose the plain .NET 4.5 Flurl.Http would work just fine on Xamarin.iOS. Yet, the current NuGet configuration tries to enforce additional PCL-dependencies on Xamarin.iOS projects โ and this fails.
Thus I believe it should be possible to just make monotouch use the .NET 4.5 version and be done with it. Haven't tried it, yet, though I will.
When using the library in a Universal Windows Project, I always get the MissingMethodException System.Net.Http.HttpRequestMessage.get_RequestUri()
when calling "http://foo.bar/test.txt".GetStringAsync()
.
Is Windows 10 support planned for the library or am I doing something else wrong?
The Flurl.Http NuGet package carries with it dependencies on several other packages necessary for portability but not all necessary on certain platforms, especially .NET 4.5. Specifically, Microsoft.Bcl.Async (for async/await support), Microsoft.Net.Http (for HttpClient), and PCLStorage (for DownloadFileAsync implementation).
While harmless, I'm concerned that this extra "baggage" may turn off developers targeting .NET 4 and (especially) 4.5. So I'm considering doing separate builds for .NET 4, .NET 4.5, and "everything else" (PCL). When installing via NuGet, only packages required for the platform you are targeting will get downloaded/installed.
I'm very interested in the opinions of others on this. Is it worth the effort? Is the extra "baggage" a deal-breaker or doesn't it bother you?
Just discovered this project and it looks a really good fit for a project I'm working on but I'm having issues installing it into a PCL that targets Windows Phone 8.1. I've tested it by creating a new PCL project in VS2013 that targets .NET45, SL5, Win8, WinPhone SL 8, Android, iOS and the Flurl.Http Nuget package will install fine but adding WP8.1 into the PCL means the package does not install. Nuget doesn't seem to give any error specifically saying why it could not be installed.
This is using Nuget 2.8.50313.46.
it not work when using task.Wait()
var task = "http://www.baidu.com"
.GetStringAsync();
task.Wait();
It would be nice if HttpTest did not overwrite the existing AfterCall handler. Perhaps it could check if an existing handler is in place, and call it as well?
public HttpTest() {
FlurlHttp.Configure(opts => {
opts.HttpClientFactory = new TestHttpClientFactory(this);
Action<HttpCall> existingAfterCall = opts.AfterCall;
opts.AfterCall = call =>
{
CallLog.Add(call);
if (existingAfterCall != null)
existingAfterCall(call);
};
If you agree, I am happy to submit a pull request with tests.
I have an Uri with percent in query params:
http://www.remix-shop.ru/catalog/?set_filter=Y&SEASON=%CD%EE%E2%FB%E9%20%E3%EE%E4
Next i create new Url
var url = new Url(initialUri.OriginalString);
and now my url is
http://www.remix-shop.ru/catalog/?set_filter=Y&SEASON=%25CD%25EE%25E2%25FB%25E9%20%25E3%25EE%25E4
As you can see, we have additional %25 in query params.
Second url it's not the same as first! I have different pages when open this 2 url in browser.
In some environments, like Orleans and, maybe Service Fabric we cannot use .configureAwait(false)
due to task scheduler limitations http://dotnet.github.io/orleans/Advanced-Concepts/External-Tasks-and-Grains.
This means FLurl cannot be used in Orleans, because there are .configureAwait(false)
all over the code base
There a way to use the Fusillade RateLimitedHttpMessageHandler?
For something similar as described here: Resilient network services.
Currently any non-2XX response throws a FlurlHttpException. (The reasoning is that there's no other mechanism to capture a failed status in a convenience method like this: url.GetJsonAsync<T>()
)
It's been suggested to allow configuring other non-exception-thrown statuses. I'm considering something along these lines:
AllowHttpStatuses("202,206,400-499")
AllowAnyHttpStatus()
These would be methods on FlurlClient and would of course be chainable. Comments/suggestions welcomed!
Adding a request for MultiPartform as you suggested in your reply to my question on the documentation pages.
Hey Todd,
When I execute a PostUrlEncodedAsync(object)
on PCL Xamarin project
endPoint
.AppendPathSegments("oauth", "token")
.PostUrlEncodedAsync(new {
username = createToken.Username,
password = createToken.Password,
})
.ReceiveJson<Oauth2AccessToken>();
I got a empty call.RequestBody.
FlurlHttp.Configure(c => {
...
c.BeforeCall = DebugUtils.LogHttpRequestCall;
...
});
public static class DebugUtils
{
public static void LogHttpRequestCall(HttpCall call)
{
Debug.WriteLine(
"Request:\n" +
"\tUrl: " + call.Url + "\n" +
"\tHeader: " + call.Request.Headers + "\n" +
"\tBody: " + call.RequestBody
);
}
}
I figured out that data.ToKeyValuePairs()
returns an empty IEnumerable
and that probably obj.GetType().GetProperties()
don`t work correct with Xamarin PCL.
PostUrlEncodedAsync(IDictionary)
create a correct RequestBody.
+
should be decoded to space, and encoded to either +
or %20
but preferably +
to keep the original encoding.
Here is what it does:
"?q=foo+bar".SetQueryParam("page",2).ToString()
"?q=foo%2Bbar&page=2"
Here is what it should do:
"?q=foo+bar".SetQueryParam("page",2).ToString()
"?q=foo+bar&page=2"
Am I missing something? Flurl does not send headers or token in requests.
var url = new Url("endpoint");
url.WithOAuthBearerToken(token);
url.SetQueryParam("suggestionTypes", "postcode");
url.SetQueryParam("q", "380");
url.SetQueryParam("limit", "20");
url.SetQueryParam("includeUnits", true);
try
{
Suggestion poco = await url.GetJsonAsync<Suggestion>();
}
catch (FlurlHttpException e)
{
}
I'm trying to pass in parameters
var parameters = new {
code = payload.Code, (NULL)
client_id = payload.ClientId, (NULL)
client_secret = Settings.Current.GitHubAppSecret,
redirect_uri = payload.RedirectUri (NULL)
};
and I'm getting the following exception. It seems that we need to do some null checks for this.
at Flurl.Util.CommonExtensions.<ToKeyValuePairs>d__0.MoveNext() in c:\Aldenteware\Flurl\Code\Flurl\Util\CommonExtensions.cs:line 25
at Flurl.Url.SetQueryParams(Object values) in c:\Aldenteware\Flurl\Code\Flurl\Url.cs:line 119
at Flurl.StringExtensions.SetQueryParams(String url, Object values) in c:\Aldenteware\Flurl\Code\Flurl\StringExtensions.cs:line 65
at Exceptionless.Api.Controllers.AuthController.<Google>d__19.MoveNext() in c:\Code\Exceptionless\Source\Api\Controllers\AuthController.cs:line 47
Any chance for a quick fix? I'm a bit under the timeline but I'll try and get a pull request if needed.
First, I love Flurl, it's awesome and I am excited to use it. Great job! ๐
I thought I'd throw in my vote to strong name the Flurl assemblies, if it's not a huge headache. Currently, I believe my only workaround is to use StrongNameSigner to sign Flurl assemblies (or build and sign myself) and then ship the signed assemblies with openstack.net. Which I'm willing to do but would prefer to not redistribute my own signed Flurl assemblies.
I am maintaining openstack.net, a .NET client for the OpenStack API and it ends up being deployed/used in enterprise type scenarios where strong naming is sometimes required. At this point, I'm not sure I could stop strong naming openstack.net anyway as it's been out in the wild for a while... Not your problem I realize, just sharing the context behind the request.
If it's not something you're willing to take on, no worries. I just thought it wouldn't hurt to ask first!
When creating a custom HttpClientFactory that manages a HttpClient the FlurlClient instances keep disposing the used HTTPClient when collected by the GC.
I already made a solution for it, virtual dispose
#65
@tmenier Is there currently a way to fluently call Flurl.Http (and the underlying HttpClient) in a way that disposes of HttpClient? I see that I have direct access to HttpClient from FlurlClient, but then I have to manually dispose of it (and remember to do that), and it breaks up the fluent chain. Thanks!
I'm doing oauth work and I need to post content to google open connect.. It then returns json from the post and I need to parse it.. I can do that but it would be easier if an overload just returned json :).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.