I simply cannot explain how many times I had to read the documentation in order to remember how to do HTTP calls using HttpClient and 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!
let client = new HttpClient()
client.BaseAddress <- "https://example.com/api/"
// joining our endpoint path
let uri = Uri(client.BaseAddress, "login")
GET Request
In order to make GET requests, you need to setup an HttpRequestMessage instance with the desired method and your endpoint Uri.
let message = new HttpRequestMessage(HttpMethod.Get, uri)
// send request
use! response = client.SendAsync(message)
// read json
let! body = response.Content.ReadFromJsonAsync<T>()
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!
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>()
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!
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)