### Basic GET Request Source: https://flurl.dev/docs/fluent-http Demonstrates how to build a URL and make a basic GET request to retrieve a string response. ```APIDOC ## GET /endpoint ### Description Builds a URL and makes a GET request to retrieve a string response. ### Method GET ### Endpoint `https://some-api.com/endpoint` ### Request Example ```csharp var result = await "https://some-api.com" .AppendPathSegment("endpoint") .GetStringAsync(); ``` ### Response #### Success Response (200) - **result** (string) - The response body as a string. ``` -------------------------------- ### Build URL and Get String Source: https://flurl.dev/docs/fluent-http Builds a URL and makes a GET request to retrieve the response as a string. Requires Flurl.Http. ```csharp using Flurl; using Flurl.Http; var result = await "https://some-api.com" .AppendPathSegment("endpoint") .GetStringAsync(); ``` -------------------------------- ### Chaining Custom Flurl.Http Extensions Source: https://flurl.dev/docs/extensibility Demonstrates how to use custom extension methods on Flurl.Http objects. Examples show calling custom methods after string initialization, Url extension, and IFlurlRequest extension. ```csharp result = await "http://api.com" .DoMyThing() // string extension .GetAsync(); result = "http://api.com" .AppendPathSegment("endpoint") .DoMyThing() // Url extension .GetAsync(); result = "http://api.com" .AppendPathSegment("endpoint") .WithBasicAuth(u, p) .DoMyThing() // IFlurlRequest extension .GetAsync(); ``` -------------------------------- ### Conditional Response Setup Source: https://flurl.dev/docs/testable-http Set up responses that apply only to requests matching specific criteria like URL patterns, HTTP verbs, query parameters, headers, or request bodies. Wildcards are supported. ```csharp httpTest .ForCallsTo("*.api.com*", "*.test-api.com*") // multiple allowed, wildcard supported .WithVerb("put", "PATCH") // or HttpMethod.Put, HttpMethod.Patch .WithQueryParam("x", "a*") // value optional, wildcard supported .WithQueryParams(new { y = 2, z = 3 }) .WithAnyQueryParam("a", "b", "c") .WithoutQueryParam("d") .WithHeader("h1", "f*o") // value optional, wildcard supported .WithoutHeader("h2") .WithRequestBody("*something*") // wildcard supported .WithRequestJson(new { a = "*", b = "hi" }) // wildcard supported in sting values .With(call => true) // check anything on the FlurlCall .Without(call => false) // check anything on the FlurlCall .RespondWith("all conditions met!", 200); ``` -------------------------------- ### Integrate Polly with Flurl using Middleware Source: https://flurl.dev/docs/configuration Integrate Polly, a resiliency library, with Flurl by adding a PolicyHttpMessageHandler as middleware. This example demonstrates configuring Flurl in a DI scenario with Polly for handling request exceptions. ```csharp using Microsoft.Extensions.Http; var policy = Policy .Handle() ... services.AddSingleton(_ => new FlurlClientCache() .WithDefaults(builder => builder .AddMiddleware(() => new PolicyHttpMessageHandler(policy)))); ``` -------------------------------- ### Get or Add Named Flurl Client Source: https://flurl.dev/docs/clients Use GetOrAdd to retrieve a named client, creating and configuring it if it doesn't already exist. This is an alternative to pre-creating clients at startup. ```csharp _flurlCli = clients.GetOrAdd("MyCli", "https://some-api.com"); ``` -------------------------------- ### Clientless Usage: Basic HTTP GET Request Source: https://flurl.dev/docs/clients Perform a GET request without explicitly managing a FlurlClient. Flurl caches and reuses clients automatically. ```csharp var result = await "https://some-api.com".GetJsonAsync(); ``` -------------------------------- ### Other HTTP Verbs (POST, PUT, PATCH, OPTIONS, HEAD) Source: https://flurl.dev/docs/fluent-http Illustrates how to perform common HTTP operations beyond GET, including sending JSON or string data and receiving specific response types. ```APIDOC ## POST /resource ### Description Sends a JSON object in the request body and expects a JSON response. ### Method POST ### Endpoint `http://api.foo.com` ### Parameters #### Request Body - **requestObj** (object) - The object to be sent as JSON in the request body. ### Request Example ```csharp var result = await "http://api.foo.com".PostJsonAsync(requestObj).ReceiveJson(); ``` ### Response #### Success Response (200) - **result** (T) - The deserialized JSON response body. ## PATCH /resource/{id} ### Description Sends a JSON object in the request body and expects a string response. ### Method PATCH ### Endpoint `http://api.foo.com/{id}` ### Parameters #### Path Parameters - **id** (string) - The identifier for the resource. #### Request Body - **requestObj** (object) - The object to be sent as JSON in the request body. ### Request Example ```csharp var resultStr = await "http://api.foo.com/1".PatchJsonAsync(requestObj).ReceiveString(); ``` ### Response #### Success Response (200) - **resultStr** (string) - The response body as a string. ## PUT /resource/{id} ### Description Sends a string in the request body and expects a string response. ### Method PUT ### Endpoint `http://api.foo.com/{id}` ### Parameters #### Path Parameters - **id** (string) - The identifier for the resource. #### Request Body - **("hello")** (string) - The string to be sent in the request body. ### Request Example ```csharp var resultStr2 = await "http://api.foo.com/2".PutStringAsync("hello").ReceiveString(); ``` ### Response #### Success Response (200) - **resultStr2** (string) - The response body as a string. ## OPTIONS /resource ### Description Sends an OPTIONS request. ### Method OPTIONS ### Endpoint `http://api.foo.com` ### Request Example ```csharp var resp = await "http://api.foo.com".OptionsAsync(); ``` ### Response #### Success Response (200) - **resp** (IFlurlResponse) - The HTTP response object. ## HEAD /resource ### Description Sends a HEAD request. ### Method HEAD ### Endpoint `http://api.foo.com` ### Request Example ```csharp await "http://api.foo.com".HeadAsync(); ``` ### Response #### Success Response (200) - (void) - No response body is expected for a HEAD request. ``` -------------------------------- ### Getting Different Response Types Source: https://flurl.dev/docs/fluent-http Shows how to retrieve response bodies as different types like JSON objects, byte arrays, or streams. ```APIDOC ## GET /json ### Description Makes a GET request and deserializes the JSON response body into a specified type `T`. ### Method GET ### Endpoint `http://api.foo.com` ### Parameters #### Query Parameters - **(Implicit)** The endpoint is expected to return JSON. ### Request Example ```csharp T poco = await "http://api.foo.com".GetJsonAsync(); ``` ### Response #### Success Response (200) - **poco** (T) - The deserialized JSON response body. ## GET /image.jpg ### Description Makes a GET request to retrieve the response body as a byte array. ### Method GET ### Endpoint `http://site.com/image.jpg` ### Request Example ```csharp byte[] bytes = await "http://site.com/image.jpg".GetBytesAsync(); ``` ### Response #### Success Response (200) - **bytes** (byte[]) - The response body as a byte array. ## GET /music.mp3 ### Description Makes a GET request to retrieve the response body as a stream. ### Method GET ### Endpoint `http://site.com/music.mp3` ### Request Example ```csharp Stream stream = await "http://site.com/music.mp3".GetStreamAsync(); ``` ### Response #### Success Response (200) - **stream** (Stream) - The response body as a stream. ``` -------------------------------- ### Get Response as Specific Type Source: https://flurl.dev/docs/fluent-http Makes a GET request and deserializes the JSON response body into a specified C# object type. Requires Flurl.Http. ```csharp T poco = await "http://api.foo.com".GetJsonAsync(); byte[] bytes = await "http://site.com/image.jpg".GetBytesAsync(); Stream stream = await "http://site.com/music.mp3".GetStreamAsync(); ``` -------------------------------- ### Get Full HTTP Response Object Source: https://flurl.dev/docs/fluent-http Retrieves the complete IFlurlResponse object for a request, allowing access to status code, headers, and the response body. Useful for inspecting non-error responses or when explicit status code checking is preferred. Requires Flurl.Http. ```csharp var resp1 = await "http://api.foo.com".GetAsync(); var resp2 = await "http://api.foo.com".PostJsonAsync(requestObj); int status = resp1.StatusCode; string headerVal = resp1.Headers.FirstOrDefault("my-header"); T body = await resp1.GetJsonAsync(); ``` -------------------------------- ### Build and Encode URL with Query Parameters Source: https://flurl.dev/docs/fluent-url Demonstrates building a URL by appending a path segment, setting query parameters using object notation, and setting a fragment. Query parameters are automatically encoded. ```csharp using Flurl; var url = "https://some-api.com" .AppendPathSegment("endpoint") .SetQueryParams(new { api_key = _config.GetValue("MyApiKey"), max_results = 20, q = "I'll get encoded!" }) .SetFragment("after-hash"); // result: // https://some-api.com/endpoint?api_key=xxx&max_results=20&q=I%27ll%20get%20encoded%21#after-hash ``` -------------------------------- ### Set Query Parameters in Various Ways Source: https://flurl.dev/docs/fluent-url Illustrates different methods for setting query parameters, including one-by-one, using dictionaries, key-value pairs, tuples, and objects resembling name/value pairs. ```csharp url.SetQueryParam("name", "value"); // one by one url.SetQueryParams(dictionary); // any IDictionary url.SetQueryParams(kvPairs); // any IEnumerable url.SetQueryParams(new[] {(name1, value1), (name2, value2), ...}); // any collection of Tuples url.SetQueryParams(new[] { new { name = "foo", value = 1 }, ...}); // virtually anything resembling name/value pairs ``` -------------------------------- ### Configure Default Clients with IFlurlClientBuilder Source: https://flurl.dev/docs/configuration Configure default settings for all clients using the clientless pattern with IFlurlClientBuilder. This is useful for startup configuration. ```csharp // clientless pattern, all clients: FlurlHttp.Clients.WithDefaults(builder => builder.WithSettings(...)); ``` -------------------------------- ### Explicit Usage: Creating and Using a FlurlClient Source: https://flurl.dev/docs/clients Instantiate and configure a FlurlClient explicitly for scenarios adhering to dependency injection principles. A base URL is optional during client creation. ```csharp var client = new FlurlClient("https://some-api.com") // a base URL is optional .WithSettings(settings => ...) .WithHeaders(...); var result = await client.Request("path").GetJsonAsync(); ``` -------------------------------- ### Configure Clients with Dependency Injection Source: https://flurl.dev/docs/configuration Configure clients, including default settings and specific named clients, within a Dependency Injection container using IFlurlClientCache. ```csharp // DI pattern: services.AddSingleton(_ => new FlurlClientCache() // all clients: .WithDefaults(builder => builder.WithSettings(...)) // specific named client: .Add("MyClient", "https://some-api.com", builder => builder.WithSettings(...)) ``` -------------------------------- ### Fluent Configuration with WithSettings Source: https://flurl.dev/docs/configuration Apply settings fluently to a client or request using the WithSettings method. This allows for chained configuration calls. ```csharp clientOrRequest.WithSettings(settings => { settings.Timeout = TimeSpan.FromSeconds(600); settings.AllowedHttpStatusRange = "*"; settings.Redirects.Enabled = false; })... ``` -------------------------------- ### Client vs. Request Configuration Precedence Source: https://flurl.dev/docs/configuration Demonstrates how settings applied to a client affect all subsequent requests, while settings applied directly to a request only affect that specific request. ```csharp await client .WithSettings(...) // configures the client, affects all subsequent requests .Request("endpoint") // creates and returns a request .WithSettings(...) // configures just this request .GetAsync(); ``` -------------------------------- ### Initialize HttpTest for Testing Source: https://flurl.dev/docs/testable-http Instantiate `HttpTest` to enable Flurl's test mode. All HTTP activity will be faked and recorded. Ensure `HttpTest` is disposed after use. ```csharp using Flurl.Http.Testing; [Test] public void Test_Some_Http_Calling_Method() { using var httpTest = new HttpTest(); // Flurl is now in test mode await sut.CallThingThatUsesFlurlAsync(); // HTTP calls are faked! } ``` ```csharp private HttpTest _httpTest; [SetUp] public void CreateHttpTest() { _httpTest = new HttpTest(); } [TearDown] public void DisposeHttpTest() { _httpTest.Dispose(); } [Test] public void Test_Some_Http_Calling_Method() { // Flurl is in test mode } ``` -------------------------------- ### HTTP Verbs (POST, PATCH, PUT, OPTIONS, HEAD) Source: https://flurl.dev/docs/fluent-http Demonstrates making HTTP requests using various verbs like POST, PATCH, PUT, OPTIONS, and HEAD with Flurl.Http. Supports receiving response body as JSON, string, or other types. ```csharp var result = await "http://api.foo.com".PostJsonAsync(requestObj).ReceiveJson(); var resultStr = await "http://api.foo.com/1".PatchJsonAsync(requestObj).ReceiveString(); var resultStr2 = await "http://api.foo.com/2".PutStringAsync("hello").ReceiveString(); var resp = await "http://api.foo.com".OptionsAsync(); await "http://api.foo.com".HeadAsync(); ``` -------------------------------- ### Register and Use IFlurlClientCache with DI Source: https://flurl.dev/docs/clients Register IFlurlClientCache as a singleton and inject it into services to retrieve named Flurl clients. Clients can be pre-configured during registration. ```csharp services.AddSingleton(sp => new FlurlClientCache() .Add("MyCli", "https://some-api.com")); ``` ```csharp public class MyService : IMyService { private readonly IFlurlClient _flurlCli; public MyService(IFlurlClientCache clients) { _flurlCli = clients.Get("MyCli"); } } ``` -------------------------------- ### Downloading Files Source: https://flurl.dev/docs/fluent-http Shows how to download a file from a URL and save it to a local path. ```APIDOC ## Download File ### Description Downloads a file from a specified URL and saves it to a local directory. ### Method GET ### Endpoint http://files.foo.com/image.jpg ### Parameters #### Query Parameters - **filename** (string) - Optional - The name to save the file as. Defaults to the remote file name. ### Response #### Success Response (200) - **path** (string) - The local path where the file was saved. ### Request Example ```csharp // filename is optional here; it will default to the remote file name var path = await "http://files.foo.com/image.jpg" .DownloadFileAsync("c:\\downloads", filename); ``` ``` -------------------------------- ### Build and Send HTTP Request with Flurl Source: https://flurl.dev Demonstrates building a fluent HTTP request with query parameters, authentication, and JSON payload, then receiving a JSON response. Flurl uses one HttpClient instance per host. ```csharp var person = await "https://api.com" .AppendPathSegment("person") .SetQueryParams(new { a = 1, b = 2 }) .WithOAuthBearerToken("my_oauth_token") .PostJsonAsync(new { first_name = "Claire", last_name = "Underwood" }) .ReceiveJson(); ``` -------------------------------- ### Basic Authentication with Flurl Source: https://flurl.dev/docs/fluent-http Use WithBasicAuth to easily configure Basic authentication for requests by providing username and password. ```csharp await url.WithBasicAuth("username", "password").GetJsonAsync(); ``` -------------------------------- ### Simulating Form Posts Source: https://flurl.dev/docs/fluent-http Demonstrates how to simulate HTML form posts using URL-encoded or multipart data, including file uploads. ```APIDOC ## Simulate HTML Form Post ### Description Simulates an HTML form post with URL-encoded data. ### Method POST ### Endpoint http://site.com/login ### Request Body - **user** (string) - Required - The username. - **pass** (string) - Required - The password. ### Request Example ```csharp await "http://site.com/login".PostUrlEncodedAsync(new { user = "user", pass = "pass" }); ``` ## Simulate Multipart Form Post ### Description Simulates a multipart form POST, typically used for file uploads or sending complex data. ### Method POST ### Endpoint http://api.com ### Request Body Supports various content types including strings, files, JSON, URL-encoded data, and custom HttpContent. ### Request Example ```csharp var resp = await "http://api.com".PostMultipartAsync(mp => mp .AddString("name", "hello!") // individual string .AddStringParts(new {a = 1, b = 2}) // multiple strings .AddFile("file1", path1) // local file path .AddFile("file2", stream, "foo.txt") // file stream .AddJson("json", new { foo = "x" }) // json .AddUrlEncoded("urlEnc", new { bar = "y" }) // URL-encoded .Add(content)); // any HttpContent ``` ``` -------------------------------- ### Parse URL Components Source: https://flurl.dev/docs/fluent-url Illustrates how to parse an existing URL string into its constituent parts like scheme, host, path, query, and fragment using `Flurl.Url`. ```csharp var url = new Url("https://user:pass@www.mysite.com:1234/with/path?x=1&y=2#foo"); Assert.Equal("https", url.Scheme); Assert.Equal("user:pass", url.UserInfo); Assert.Equal("www.mysite.com", url.Host); Assert.Equal(1234, url.Port); Assert.Equal("user:pass@www.mysite.com:1234", url.Authority); Assert.Equal("https://user:pass@www.mysite.com:1234", url.Root); Assert.Equal("/with/path", url.Path); Assert.Equal("x=1&y=2", url.Query); Assert.Equal("foo", url.Fragment); ``` -------------------------------- ### Respond with Custom Body and Status Source: https://flurl.dev/docs/testable-http Configure fake HTTP responses with specific bodies and status codes. Use `RespondWith` for plain text or `RespondWithJson` for JSON. ```csharp httpTest.RespondWith("some response body"); ``` ```csharp httpTest.RespondWithJson(new { x = 1, y = 2 }); ``` ```csharp httpTest.RespondWith("server error", 500); ``` ```csharp httpTest.RespondWithJson(new { message = "unauthorized" }, 401); ``` ```csharp httpTest.SimulateTimeout(); ``` -------------------------------- ### Setting Request Headers Source: https://flurl.dev/docs/fluent-http Demonstrates how to add custom headers to HTTP requests. ```APIDOC ## Set Request Headers ### Description Adds custom headers to an HTTP request. ### Method GET ### Endpoint [URL] ### Parameters #### Path Parameters - **url** (string) - Required - The URL to send the request to. #### Request Headers - **Accept** (string) - Optional - The value for the Accept header. - **User_Agent** (string) - Optional - The value for the User-Agent header (automatically converted to `User-Agent`). ### Request Example ```csharp // one header: await url.WithHeader("Accept", "text/plain").GetJsonAsync(); // multiple headers: await url.WithHeaders(new { Accept = "text/plain", User_Agent = "Flurl" }).GetJsonAsync(); ``` ``` -------------------------------- ### Fluent Configuration with Specific Extension Methods Source: https://flurl.dev/docs/configuration Utilize specific fluent extension methods for common settings like timeout, status codes, and redirects. This offers a more concise syntax. ```csharp clientOrRequest .WithTimeout(600) .AllowAnyHttpStatus() .WithAutoRedirect(false) ... ``` -------------------------------- ### Configure Test Settings Source: https://flurl.dev/docs/testable-http Modify Flurl's behavior during testing using `WithSettings` or specific shortcut methods like `WithAutoRedirect`. ```csharp httpTest.WithSettings(settings => settings.Redirects.Enabled = false); ``` ```csharp httpTest.WithAutoRedirect(false); ``` -------------------------------- ### Set Multiple Query Parameter Values Source: https://flurl.dev/docs/fluent-url Demonstrates how to set multiple values for the same query parameter name by passing a collection to `SetQueryParam`. ```csharp "https://some-api.com".SetQueryParam("x", new[] { 1, 2, 3 }); // https://some-api.com?x=1&x=2&x=3 ``` -------------------------------- ### Clientless Usage: Pre-configuring Clients for URLs Source: https://flurl.dev/docs/clients Configure client settings and headers for a specific URL base before making requests. This configuration is applied automatically when the clientless pattern is used. ```csharp FlurlHttp.ConfigureClientForUrl("https://some-api.com") .WithSettings(settings => ...) .WithHeaders(...); ``` -------------------------------- ### Explicitly Create Flurl.Url Object Source: https://flurl.dev/docs/fluent-url Shows how to explicitly create a `Flurl.Url` object from a string, which can then be used with builder methods. ```csharp var url = new Url("http://www.some-api.com").AppendPathSegment(...) ``` -------------------------------- ### Attach Synchronous and Async Event Handlers Source: https://flurl.dev/docs/configuration Use fluent shortcuts to attach synchronous or asynchronous handlers for events like `BeforeCall` and `OnError`. The `call` object provides detailed information about the HTTP call. ```csharp clientOrRequest .BeforeCall(call => DoSomething(call)) // attach a synchronous handler .OnError(call => LogErrorAsync(call)) // attach an async handler ``` -------------------------------- ### Timeouts and Error Handling Source: https://flurl.dev/docs/fluent-http Explains how to set request timeouts and handle potential HTTP exceptions, including timeouts. ```APIDOC ## Set Request Timeout ### Description Specifies a timeout duration for an HTTP request. ### Method GET ### Endpoint [URL] ### Parameters #### Path Parameters - **url** (string) - Required - The URL to send the request to. #### Query Parameters - **timeout** (integer or TimeSpan) - Required - The timeout duration in seconds or as a TimeSpan object. ### Request Example ```csharp await url.WithTimeout(10).GetAsync(); // 10 seconds await url.WithTimeout(TimeSpan.FromMinutes(2)).GetAsync(); ``` ## Handle Timeout and HTTP Errors ### Description Provides a mechanism to catch and handle `FlurlHttpTimeoutException` and general `FlurlHttpException`. ### Request Example ```csharp try { var result = await url.GetStringAsync(); } catch (FlurlHttpTimeoutException) { // handle timeouts } catch (FlurlHttpException) { // handle error responses } ``` ``` -------------------------------- ### Configure Specific Client with IFlurlClientBuilder Source: https://flurl.dev/docs/configuration Configure settings for a specific URL or named client using IFlurlClientBuilder. This allows for targeted configuration. ```csharp // clientless pattern, for a specific site/service: FlurlHttp.ConfigureClientForUrl("https://some-api.com") .WithSettings(...); ``` -------------------------------- ### Access Query Parameters with Duplicates Source: https://flurl.dev/docs/fluent-url Demonstrates using `Url.QueryParams` to access query parameters, which supports ordered access and duplicate names, and provides methods like `FirstOrDefault` and `GetAll`. ```csharp var url = new Url("https://www.mysite.com?x=1&y=2&y=3"); Assert.Equal("1", url.QueryParams.FirstOrDefault("x")); Assert.Equal(new[] { "2", "3" }, url.QueryParams.GetAll("y")); ``` -------------------------------- ### Simulate HTML Form Post with Flurl Source: https://flurl.dev/docs/fluent-http Use PostUrlEncodedAsync for simulating HTML form posts. This is useful for submitting data in a key-value format. ```csharp await "http://site.com/login".PostUrlEncodedAsync(new { user = "user", pass = "pass" }); ``` -------------------------------- ### Download File with Flurl Source: https://flurl.dev/docs/fluent-http Use DownloadFileAsync to download files. The filename parameter is optional and defaults to the remote file name if not provided. ```csharp // filename is optional here; it will default to the remote file name var path = await "http://files.foo.com/image.jpg" .DownloadFileAsync("c:\\downloads", filename); ``` -------------------------------- ### Extend Flurl.Http with Custom Methods Source: https://flurl.dev/docs/extensibility Create custom chainable extension methods for Flurl.Http. Typically, four overloads are provided for Url, Uri, string, and IFlurlRequest, all returning the current IFlurlRequest to enable further chaining. ```csharp public static IFlurlRequest DoMyThing(this IFlurlRequest req) { // do something interesting with req.Settings, req.Headers, req.Url, etc. return req; } // keep these overloads DRY by constructing a Url and deferring to the above method public static IFlurlRequest DoMyThing(this Url url) => new FlurlRequest(url).DoMyThing(); public static IFlurlRequest DoMyThing(this Uri uri) => new FlurlRequest(uri).DoMyThing(); public static IFlurlRequest DoMyThing(this string url) => new FlurlRequest(url).DoMyThing(); ``` -------------------------------- ### Configure Flurl Client Lazily Source: https://flurl.dev/docs/clients Configure specific settings for a named Flurl client lazily using an Action during Add or GetOrAdd. ```csharp .Add("MyCli", "https://some-api.com", builder => builder .WithSettings(settings => ...) .WithHeaders(...) ``` -------------------------------- ### Configure HttpClientHandler in Flurl Source: https://flurl.dev/docs/configuration Configure the default HttpClientHandler for Flurl clients. This is useful for setting up proxies or other handler-specific configurations. Use either the clientless or DI pattern. ```csharp // clientless pattern: FlurlHttp.Clients.WithDefaults(builder => builder .ConfigureHttpClient(hc => ...) .ConfigureInnerHandler(hch => { hch.Proxy = new WebProxy("https://my-proxy.com"); hch.UseProxy = true; })); // DI pattern: services.AddSingleton(_ => new FlurlClientCache() .WithDefaults(builder => builder. .ConfigureHttpClient(hc => ...) .ConfigureInnerHandler(hch => ...))); ``` -------------------------------- ### Cookie Management Source: https://flurl.dev/docs/fluent-http Illustrates how to send cookies with requests and manage cookie jars for session persistence. ```APIDOC ## Sending Cookies ### Description Sends cookies with an HTTP request. ### Method GET ### Endpoint https://cookies.com ### Parameters #### Query Parameters - **name** (string) - Required - The name of the cookie. - **value** (string) - Required - The value of the cookie. ### Request Example ```csharp var resp = await "https://cookies.com" .WithCookie("name", "value") .WithCookies(new { cookie1 = "foo", cookie2 = "bar" }) .GetAsync(); ``` ## Managing Cookie Jars ### Description Manages cookies across multiple requests using a `CookieJar` or `CookieSession`. ### Method POST, GET ### Endpoint https://cookies.com/login, https://cookies.com/a, https://cookies.com/b ### Request Example ```csharp // Using an output parameter for the cookie jar await "https://cookies.com/login".WithCookies(out var jar).PostUrlEncodedAsync(credentials); await "https://cookies.com/a".WithCookies(jar).GetAsync(); await "https://cookies.com/b".WithCookies(jar).GetAsync(); // Using a CookieSession using var session = new CookieSession("https://cookies.com"); // set any initial cookies on session.Cookies await session.Request("a").GetAsync(); await session.Request("b").GetAsync(); // read cookies at any point using session.Cookies ``` ## Cookie Jar Persistence ### Description Persists and reloads `CookieJar` instances to string or file. ### Request Example ```csharp // string-based persistence: var saved = jar.ToString(); var jar2 = CookieJar.LoadFromString(saved); // file-based persistence: using var writer = new StreamWriter("path/to/file"); jar.WriteTo(writer); using var reader = new StreamReader("path/to/file"); var jar2 = CookieJar.LoadFrom(reader); ``` ``` -------------------------------- ### Send Custom HTTP Content with Flurl Source: https://flurl.dev/docs/fluent-http Use lower-level methods like PostAsync, SendJsonAsync, or SendAsync to send custom HttpContent, specify HTTP methods like TRACE or CONNECT, and control cancellation and completion options. ```csharp await url.PostAsync(content); // content is a System.Net.Http.HttpContent await url.SendJsonAsync(HttpMethod.Trace, data); await url.SendAsync( new HttpMethod("CONNECT"), httpContent, // optional cancellationToken, // optional HttpCompletionOption.ResponseHeaderRead); // optional ``` -------------------------------- ### Extend URL Builder with Custom Methods Source: https://flurl.dev/docs/extensibility Define custom chainable methods for the URL builder. Overloads for Url, Uri, and string are recommended for consistency. These methods should return the modified Url object. ```csharp public static Url DoMyThing(this Url url) { // do something interesting with url return url; } // keep these overloads DRY by constructing a Url and deferring to the above method public static Url DoMyThing(this Uri uri) => new Url(uri).DoMyThing(); public static Url DoMyThing(this string url) => new Url(url).DoMyThing(); ``` -------------------------------- ### Asserting Basic HTTP Calls Source: https://flurl.dev/docs/testable-http Use HttpTest methods to verify if specific URLs were called or if any calls were made. These assertions throw an exception on failure. ```csharp sut.DoThing(); // were calls to specific URLs made? httpTest.ShouldHaveCalled("http://some-api.com/*"); httpTest.ShouldNotHaveCalled("http://other-api.com/*"); // were any calls made? httpTest.ShouldHaveMadeACall(); httpTest.ShouldNotHaveMadeACall(); ``` -------------------------------- ### Handle Redirects with OnRedirect Source: https://flurl.dev/docs/configuration The `OnRedirect` event allows for precise handling of 3xx responses. You can control whether to follow redirects, change the HTTP verb, and log redirect information. ```csharp clientOrRequest.OnRedirect(call => { if (call.Redirect.Count > 5) { call.Redirect.Follow = false; } else { log.WriteInfo($"redirecting from {call.Request.Url} to {call.Redirect.Url}"); call.Redirect.ChangeVerbToGet = (call.Response.Status == 301); call.Redirect.Follow = true; } }); ``` -------------------------------- ### Asserting Specific HTTP Call Details Source: https://flurl.dev/docs/testable-http Fluently assert details of a specific HTTP call, including query parameters, verb, content type, headers, request body, and the number of times it was called. Wildcards (*) are supported. ```csharp httpTest.ShouldHaveCalled("http://some-api.com/*") .WithQueryParam("x", "1*") .WithVerb(HttpMethod.Post) .WithContentType("application/json") .WithoutHeader("my-header-*") .WithRequestBody("{\"a\":*,\"b\":*}") .Times(3); ``` -------------------------------- ### Flurl URL Encoding Utility Methods Source: https://flurl.dev/docs/fluent-url Utilize Flurl's static methods for URL encoding and decoding, offering alternatives to .NET's built-in methods. ```csharp Url.Encode(string s, bool encodeSpaceAsPlus); // includes reserved characters like / and ? Url.EncodeIllegalCharacters(string s, bool encodeSpaceAsPlus); // reserved characters aren't touched Url.Decode(string s, bool interpretPlusAsSpace); ``` -------------------------------- ### Combining URL Parts with Flurl Source: https://flurl.dev/docs/fluent-url Use the static Url.Combine method to safely join multiple URL parts, ensuring correct separators. ```csharp var url = Url.Combine( "http://foo.com/", "/too/", "/many/", "/slashes/", "too", "few?", "x=1", "y=2"); // result: "http://www.foo.com/too/many/slashes/too/few?x=1&y=2" ``` -------------------------------- ### Chain Multiple Fake Responses Source: https://flurl.dev/docs/testable-http Chain multiple `RespondWith*` calls to queue a sequence of fake responses. Flurl will use these in order for subsequent HTTP calls. ```csharp httpTest .RespondWith("some response body") .RespondWithJson(someObject) .RespondWith("error!", 500); ``` -------------------------------- ### Clientless Request Configuration Source: https://flurl.dev/docs/configuration Configure and send a request without explicitly referencing a client. Settings are applied directly to the URL string. ```csharp var result = await "https://some-api.com/endpoint" .WithSettings(...) // configures just this request .WithTimeout(...) // ditto .GetJsonAsync(); ``` -------------------------------- ### Clientless Usage: Custom Client Caching Strategy Source: https://flurl.dev/docs/clients Implement a custom strategy for caching Flurl clients, allowing for more granular control beyond simple host-based caching. This is useful for scenarios like versioned APIs. ```csharp FlurlHttp.UseClientCachingStrategy(request => // get the default host-based key by invoking the default caching strategy: var name = FlurlHttp.BuildClientNameByHost(request); // append the service version from a custom request header, if it exists: if (request.Headers.TryGetFirst("X-Service-Version", out var version)) return $"{name}:{version}"; return name; }); ``` -------------------------------- ### Authentication Source: https://flurl.dev/docs/fluent-http Covers basic authentication and OAuth 2.0 bearer token authentication. ```APIDOC ## Basic Authentication ### Description Authenticates a request using Basic Authentication. ### Method GET ### Endpoint [URL] ### Parameters #### Path Parameters - **url** (string) - Required - The URL to send the request to. #### Query Parameters - **username** (string) - Required - The username for authentication. - **password** (string) - Required - The password for authentication. ### Request Example ```csharp await url.WithBasicAuth("username", "password").GetJsonAsync(); ``` ## OAuth 2.0 Bearer Token ### Description Authenticates a request using an OAuth 2.0 bearer token. ### Method GET ### Endpoint [URL] ### Parameters #### Path Parameters - **url** (string) - Required - The URL to send the request to. #### Query Parameters - **token** (string) - Required - The OAuth 2.0 bearer token. ### Request Example ```csharp await url.WithOAuthBearerToken("mytoken").GetJsonAsync(); ``` ``` -------------------------------- ### Send Cookies with Flurl Requests Source: https://flurl.dev/docs/fluent-http Use WithCookie and WithCookies to send cookies with requests. These methods allow adding individual cookies or multiple cookies from an anonymous object. ```csharp var resp = await "https://cookies.com" .WithCookie("name", "value") .WithCookies(new { cookie1 = "foo", cookie2 = "bar" }) .GetAsync(); ``` -------------------------------- ### Append Multiple Query Parameter Values Source: https://flurl.dev/docs/fluent-url Shows how to use `AppendQueryParam` and `AppendQueryParams` to add multiple values for a query parameter without overwriting existing ones. ```csharp "https://some-api.com" .AppendQueryParam("x", 1); .AppendQueryParam("x", 2); .AppendQueryParams("x", new[] { 3, 4 }); // https://some-api.com?x=1&x=2&x=3&x=4 ``` -------------------------------- ### Test HTTP Calls with Flurl.Http.Testing Source: https://flurl.dev Shows how to fake and record HTTP calls within a test subject using HttpTest. This allows for asserting that specific requests were made with certain parameters. ```csharp // fake & record all http calls in the test subject using (var httpTest = new HttpTest()) { // arrange httpTest.RespondWith("OK", 200); // act await sut.CreatePersonAsync(); // assert httpTest.ShouldHaveCalled("https://api.com/*") .WithVerb(HttpMethod.Post) .WithContentType("application/json"); } ``` -------------------------------- ### Manage Cookie Jar with Flurl Source: https://flurl.dev/docs/fluent-http Capture response cookies into a CookieJar using 'out var jar' and reuse them in subsequent requests. This simulates browser cookie management. ```csharp await "https://cookies.com/login".WithCookies(out var jar).PostUrlEncodedAsync(credentials); await "https://cookies.com/a".WithCookies(jar).GetAsync(); await "https://cookies.com/b".WithCookies(jar).GetAsync(); ``` -------------------------------- ### Simulate Multipart Form POST with Flurl Source: https://flurl.dev/docs/fluent-http Use PostMultipartAsync for multipart form POST requests, commonly used for file uploads. It supports adding strings, files, JSON, URL-encoded data, and custom HttpContent. ```csharp var resp = await "http://api.com".PostMultipartAsync(mp => mp .AddString("name", "hello!") // individual string .AddStringParts(new {a = 1, b = 2}) // multiple strings .AddFile("file1", path1) // local file path .AddFile("file2", stream, "foo.txt") // file stream .AddJson("json", new { foo = "x" }) // json .AddUrlEncoded("urlEnc", new { bar = "y" }) // URL-encoded .Add(content)); // any HttpContent ``` -------------------------------- ### Set Default and Override Request Settings Source: https://flurl.dev/docs/configuration Configure default settings on a Flurl client and override them for specific requests. This affects subsequent calls made with the client or request. ```csharp // set default on the client: var client = new FlurlClient("https://some-api.com"); client.Settings.Timeout = TimeSpan.FromSeconds(600); client.Settings.Redirects.Enabled = false; // override on the request: var request = client.Request("endpoint"); request.Settings.Timeout = TimeSpan.FromSeconds(1200); request.Settings.Redirects.Enabled = true; ``` -------------------------------- ### Handling Errors and Inspecting Responses Source: https://flurl.dev/docs/fluent-http Explains how Flurl.Http throws exceptions for 4xx/5xx errors and how to catch them to inspect the error response, or how to access response details like status code and headers. ```APIDOC ## Error Handling with FlurlHttpException ### Description Demonstrates how to catch `FlurlHttpException` for 4xx or greater status codes and inspect the error response body. ### Method POST, PUT, PATCH, DELETE (or any method that might return an error status) ### Endpoint `url` (variable representing the target URL) ### Parameters #### Request Body - **requestObj** (object) - The object to be sent in the request body. ### Request Example ```csharp try { var result = await url.PostJsonAsync(requestObj).ReceiveJson(); } catch (FlurlHttpException ex) { var err = await ex.GetResponseJsonAsync(); // or GetResponseStringAsync(), etc. logger.Write($"Error returned from {ex.Call.Request.Url}: {err.SomeDetails}"); } ``` ### Response #### Error Response (4xx or 5xx) - **ex** (FlurlHttpException) - The exception thrown for error status codes. - **ex.Call.Request.Url** (Url) - The URL that was requested. - **ex.GetResponseJsonAsync()** - Method to get the error response body as JSON. - **ex.GetResponseStringAsync()** - Method to get the error response body as a string. ## Inspecting IFlurlResponse ### Description Shows how to get an `IFlurlResponse` object to inspect status codes, headers, and consume the body manually, without exceptions for non-success statuses. ### Method GET, POST, PUT, PATCH, DELETE ### Endpoint `http://api.foo.com` ### Parameters #### Request Body (for non-GET requests) - **requestObj** (object) - The object to be sent in the request body (for POST, PUT, PATCH). ### Request Example ```csharp var resp1 = await "http://api.foo.com".GetAsync(); var resp2 = await "http://api.foo.com".PostJsonAsync(requestObj); // Inspecting the response int status = resp1.StatusCode; string headerVal = resp1.Headers.FirstOrDefault("my-header"); T body = await resp1.GetJsonAsync(); ``` ### Response #### Success Response (2xx or 3xx) - **resp1, resp2** (IFlurlResponse) - The HTTP response object. - **resp.StatusCode** (int) - The HTTP status code. - **resp.Headers** (Headers) - The response headers. - **resp.GetJsonAsync()** - Method to get the response body as JSON. - **resp.GetStringAsync()** - Method to get the response body as a string. ## Allowing Specific HTTP Status Codes ### Description Demonstrates how to configure Flurl to not throw exceptions for specific HTTP status codes, allowing them to be handled like success responses. ### Method GET, POST, PUT, PATCH, DELETE ### Endpoint `http://api.foo.com` ### Request Example ```csharp // Allow specific status codes var resp2 = await "http://api.foo.com".AllowHttpStatus(400, 401).GetAsync(); // Allow a range and specific codes using string format var resp3 = await "http://api.foo.com".AllowHttpStatus("400-403,5xx").GetAsync(); // Allow all non-2xx/3xx status codes var resp1 = await "http://api.foo.com".AllowAnyHttpStatus().GetAsync(); ``` ### Response #### Success Response (2xx or 3xx, or allowed non-success statuses) - **resp1, resp2, resp3** (IFlurlResponse) - The HTTP response object. ``` -------------------------------- ### Implement Event Handler with DI Source: https://flurl.dev/docs/configuration Create a custom event handler class that inherits from `FlurlEventHandler` and inject dependencies like `ILogger` using constructor injection. This is useful when handlers require external services. ```csharp public interface IFlurlErrorLogger : IFlurlEventHandler { } public class FlurlErrorLogger : FlurlEventHandler, IFlurlErrorLogger { private readonly ILogger _logger; public FlurlErrorLogger(ILogger logger) { _logger = logger; } } ``` -------------------------------- ### OAuth 2.0 Bearer Token Authentication with Flurl Source: https://flurl.dev/docs/fluent-http Use WithOAuthBearerToken to set an OAuth 2.0 bearer token for authentication. ```csharp await url.WithOAuthBearerToken("mytoken").GetJsonAsync(); ``` -------------------------------- ### Custom JsonSerializerOptions Source: https://flurl.dev/docs/configuration Configure the default JSON serializer with custom JsonSerializerOptions. This allows fine-tuning of JSON serialization behavior. ```csharp clientOrRequest.Settings.JsonSerializer = new DefaultJsonSerializer(new JsonSerializerOptions { PropertyNameCaseInsensitive = true, IgnoreReadOnlyProperties = true }); ``` -------------------------------- ### Specify Request Timeout with Flurl Source: https://flurl.dev/docs/fluent-http Configure request timeouts using WithTimeout, accepting either seconds as an integer or a TimeSpan object. ```csharp await url.WithTimeout(10).GetAsync(); // 10 seconds await url.WithTimeout(TimeSpan.FromMinutes(2)).GetAsync(); ``` -------------------------------- ### HttpTest Configuration Precedence Source: https://flurl.dev/docs/configuration HttpTest settings always take precedence over any other client or request configurations. This is useful for testing scenarios. ```csharp using var test = new HttpTest.AllowAnyHttpStatus(); await sut.DoThingAsync(); // no matter how things are configured in the SUT, // test settings always win ``` -------------------------------- ### Allow Specific HTTP Status Codes Source: https://flurl.dev/docs/fluent-http Configures Flurl.Http to not throw exceptions for specific HTTP status codes (e.g., 400, 401) or ranges (e.g., '400-403,5xx'). Use AllowAnyHttpStatus() to disable exception throwing for all statuses. Requires Flurl.Http. ```csharp var resp2 = await "http://api.foo.com".AllowHttpStatus(400, 401).GetAsync(); var resp3 = await "http://api.foo.com".AllowHttpStatus("400-403,5xx").GetAsync(); var resp1 = await "http://api.foo.com".AllowAnyHttpStatus().GetAsync(); ``` -------------------------------- ### Immutable URL Pattern in C# Source: https://flurl.dev/docs/fluent-url Use a string type for base URLs to ensure immutability when building URLs in C#. ```csharp public class MyServiceClass { private readonly string _baseUrl; public Task CallServiceAsync(string endpoint, object data) { return _baseUrl .AppendPathSegment(endpoint) .PostAsync(data); // requires Flurl.Http package } } ``` -------------------------------- ### Allow Real HTTP Calls Source: https://flurl.dev/docs/testable-http Configure `HttpTest` to allow real HTTP calls for specific endpoints, useful for testing integrations with external services. ```csharp httpTest .ForCallsTo("https://api.thirdparty.com/*") .AllowRealHttp(); ``` -------------------------------- ### Register Event Handler with DI Container Source: https://flurl.dev/docs/configuration Register your custom event handler with the DI container and then add it to Flurl's event handlers, using `IServiceProvider` to resolve the handler and its dependencies. ```csharp // register ILogger: services.AddLogging(); // register service that implements IFlurlEventHander and has dependency on ILogger services.AddSingleton(); // register event handler with Flurl, using IServiceProvider to wire up dependencies: services.AddSingleton(sp => new FlurlClientCache() .WithDefaults(builder => builder.EventHandlers.Add((FlurlEventType.OnError, sp.GetService())) ``` -------------------------------- ### Cloning a URL Object in Flurl Source: https://flurl.dev/docs/fluent-url Create a mutable copy of a URL object using the Clone() method to modify it without affecting the original. ```csharp var url2 = url1.Clone().AppendPathSegment("next"); ``` -------------------------------- ### Set Request Headers with Flurl Source: https://flurl.dev/docs/fluent-http Set request headers using WithHeader for a single header or WithHeaders for multiple headers. Underscores in C# identifiers are automatically converted to hyphens for header names. ```csharp // one: await url.WithHeader("Accept", "text/plain").GetJsonAsync(); // multiple: await url.WithHeaders(new { Accept = "text/plain", User_Agent = "Flurl" }).GetJsonAsync(); ``` -------------------------------- ### Persist and Load CookieJar with Flurl Source: https://flurl.dev/docs/fluent-http Persist and reload CookieJar instances using string or file-based methods. This allows for saving and restoring cookie state between application sessions. ```csharp // string-based persistence: var saved = jar.ToString(); var jar2 = CookieJar.LoadFromString(saved); // file-based persistence: using var writer = new StreamWriter("path/to/file"); jar.WriteTo(writer); using var reader = new StreamReader("path/to/file"); var jar2 = CookieJar.LoadFrom(reader); ```