Skip to content

Instantly share code, notes, and snippets.

@ianrussellsoftwarepark
Last active October 28, 2020 00:11
Show Gist options
  • Save ianrussellsoftwarepark/7f86ea9bddd25c56c495a7a683358b33 to your computer and use it in GitHub Desktop.
Save ianrussellsoftwarepark/7f86ea9bddd25c56c495a7a683358b33 to your computer and use it in GitHub Desktop.
Functional wrapper for HttpClient
let getResponse body (cookie: string option) (client:HttpClient) =
task {
return!
Http.createPostRequest Url
|> withBody (RequestBody.Xml body)
|> fun req ->
match cookie with
| Some c -> withCookie c req
| _ -> req
|> Http.execute client
}
namespace HttpClient.FSharp
// See https://dev.to/jhewlett/creating-a-functional-wrapper-in-f-5hgp for original version
open System
open System.Net.Http
open System.Threading.Tasks
open FSharp.Control.Tasks.V2.ContextInsensitive
open HelperModule
open System.Collections.Generic
type HttpMethod =
| Post
| Put
| Delete
| Get
module HttpMethod =
let value method =
match method with
| Post -> System.Net.Http.HttpMethod.Post
| Put -> System.Net.Http.HttpMethod.Put
| Delete -> System.Net.Http.HttpMethod.Delete
| Get -> System.Net.Http.HttpMethod.Get
[<RequireQualifiedAccess>]
type RequestBody =
| Json of string
| Xml of string
| Form of IDictionary<string, string>
module RequestBody =
let value body =
match body with
| RequestBody.Json json -> new StringContent(json, System.Text.Encoding.UTF8, "application/json") :> HttpContent
| RequestBody.Xml xml -> new StringContent(xml, System.Text.Encoding.UTF8, "text/xml") :> HttpContent
| RequestBody.Form data -> new FormUrlEncodedContent(data |> List.ofSeq) :> HttpContent
type Request = {
Url : string
Method : HttpMethod
Timeout : TimeSpan option
Headers : (string * string) list
Body : RequestBody option
Query : (string * string) list
}
[<AutoOpen>]
module Request =
let withTimeout timeout request =
{ request with Timeout = Some timeout }
let withBody body request =
{ request with Body = Some body }
let withHeader header request =
{ request with Headers = header :: request.Headers }
let withAuthorization input request =
{ request with Headers = ("Authorization", input) :: request.Headers }
let withCookie input request =
{ request with Headers = ("Cookie", input) :: request.Headers }
let withQueryParam param request =
{ request with Query = param :: request.Query }
type Response = {
StatusCode : int
Body : string
Headers : (string * string) list
}
module Http =
let createRequest method url =
{
Url = url
Method = method
Timeout = None
Headers = []
Body = None
Query = []
}
let createGetRequest = createRequest Get
let createPostRequest = createRequest Post
module Url =
let private encodeUrlParam param =
System.Uri.EscapeDataString param
let appendQueryToUrl (url : string) query =
match query with
| [] -> url
| query ->
url
+ if url.Contains "?" then "&" else "?"
+ String.concat "&" [ for k, v in List.rev query -> encodeUrlParam k + "=" + encodeUrlParam v ]
let execute (httpClient:HttpClient) (request:Request) : Task<Response> =
task {
request.Timeout
|> Option.iter (fun t -> httpClient.Timeout <- t)
let fullUrl = Url.appendQueryToUrl request.Url request.Query
use requestMessage = new HttpRequestMessage(request.Method |> HttpMethod.value, fullUrl)
request.Headers
|> List.iter requestMessage.Headers.Add
request.Body
|> Option.iter (fun b ->
let body = RequestBody.value b
requestMessage.Content <- body)
use! response = httpClient.SendAsync(requestMessage)
HttpUtil.ensureSuccessStatusCode response
let! body = response.Content.ReadAsStringAsync()
return
{
StatusCode = int response.StatusCode
Body = body
Headers = [for (KeyValue (k, v)) in response.Headers -> (k, String.concat "," v)]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment