@@ 4686,6 4686,103 @@ Now testing our two properties is trivial:
#+include: "../static/blog/ironing-out-bugs-with-fscheck/main.fsx" src fsharp :lines "56-58"
+** Dealing with HttpClient and HttpRequestMessage in F# :fsharp:dotnet:
+:PROPERTIES:
+:EXPORT_DATE: 2023-06-07
+:EXPORT_FILE_NAME: dealing-with-httpclient-and-httprequestmessage-in-f
+:EXPORT_HUGO_SLUG: dealing-with-httpclient-and-httprequestmessage-in-f
+:END:
+
+#+begin_description
+For God's sake, how many times do I need to look this up?
+#+end_description
+
+I simply cannot explain how many times I had to read the documentation
+in order to remember how to do HTTP calls using [[https://learn.microsoft.com/pt-br/dotnet/api/system.net.http.httpclient?view=net-7.0][HttpClient]] and
+[[https://learn.microsoft.com/pt-br/dotnet/api/system.net.http.httprequestmessage?view=net-8.0][HttpRequestMessage]]. Let's put an end to this madness now!
+
+*** Defining a Base Address
+
+This is useful if you intend to use the same HttpClient to make
+multiple calls to the same API. If that's the case, *do not remove*
+the trailing-slash as it will cause problems when concatenating endpoint
+paths!
+
+#+begin_src fsharp
+let client = new HttpClient()
+client.BaseAddress <- "https://example.com/api/"
+
+// joining our endpoint path
+let uri = Uri(client.BaseAddress, "login")
+#+end_src
+
+*** GET Request
+
+In order to make GET requests, you need to setup an HttpRequestMessage
+instance with the desired method and your endpoint Uri.
+
+#+begin_src fsharp
+let message = new HttpRequestMessage(HttpMethod.Get, uri)
+// send request
+use! response = client.SendAsync(message)
+// read json
+let! body = response.Content.ReadFromJsonAsync<T>()
+#+end_src
+
+*** Sending JSON on a POST request
+
+This process is a bit more involved as you need to serialize the JSON
+into a string before sending the message. Fortunately, we have
+everything we need without relying on external libraries!
+
+#+begin_src fsharp
+let credentials =
+ { Username = "gluer"
+ Password = "secret" }
+
+// serialize the credentials object into a string
+let json =
+ let serialized =
+ credentials |> JsonSerializer.Serialize
+
+ new StringContent(serialized, Encoding.UTF8, "application/json")
+
+// prepare the request with a POST method and our Uri
+let message = new HttpRequestMessage(HttpMethod.Post, uri)
+message.Content <- json
+
+// send request
+use! response = state.Client.SendAsync(message)
+
+// read json response
+let! body = response.Content.ReadFromJsonAsync<T>()
+#+end_src
+
+*** Error handling
+
+Unfortunately, by default, there is no F#-friendly way of handling
+errors on HTTP calls. However, the good news is that dealing with them
+is not difficult either!
+
+#+begin_src fsharp
+type ResponseError =
+ | NetworkError of Exception
+ | TimeoutError of Exception
+ | Unknown of Exception
+//...
+try
+ use! responseMessage = httpClient.SendAsync(message)
+ let! body = responseMessage.Content.ReadFromJsonAsync<T>()
+ return Ok body
+with
+| :? HttpRequestException as ex ->
+ return Error(NetworkError ex)
+| :? TaskCanceledException as ex ->
+ return Error(TimeoutError ex)
+| ex ->
+ return Error(Unknown ex)
+#+end_src
+
* Footnotes