Flurl in Xamarin Forms

0 Comments

In order to update data on client side mobile, Xamarin Forms developers in their apps often use REST services.

And for that we are using HttpClient. It works fine but, unfortunately, it isn't flexible enough and takes time for implementation in each request code like this:

var url = $"{AppConstants.BaseUrl}/Brand";

var resp = await _httpClient.GetAsync(url);
if (resp.IsSuccessStatusCode)
{
   var data = await resp.Content.ReadAsStringAsync();
   result = JsonConvert.DeserializeObject<IList<Brand>>(data);
}
HttpClient example

So this code has some parts:
1) Prepare url;
2) Send request;
3) Read response;
4) Parse data.

If you want to make the process simpler, Flurl can be used! Flurl.Http is a wrapper on HttpClient, which uses pattern builder for preparing the request and parse response. Also Flurl is testable and we can easily write unit tests on Http requests.
Let’s have a look how that code can be rewritten with Flurl:

result = await AppConstants.BaseUrl
               .AppendPathSegment(ApiServices.BrandApi)
               .GetJsonAsync<IList<Brand>>();
Flurl example

Looks simpler, isn’t it? As you must have noticed, we construct url, send request and parse result in one instruction.

And now I’d like to show you how we can use it in the most common situations.

POST:

await AppConstants.BaseUrl
      .AppendPathSegment(ApiServices.BrandApi)
      .PostJsonAsync(brand);
Post example

or

      await AppConstants.BaseUrl
      .AppendPathSegment(ApiServices.BrandApi)
      .PostStringAsync(content)
Post example

GET:

result = await AppConstants.BaseUrl
                .AppendPathSegment(ApiServices.BrandApi)
                .GetAsync()
                .ReceiveJson<IList<Brand>>();
Get example

or

result = await AppConstants.BaseUrl
                .AppendPathSegment(ApiServices.BrandApi)
                .GetJsonAsync<IList<Brand>>();
Get example

PUT:

await AppConstants.BaseUrl
      .AppendPathSegment(ApiServices.BrandApi)
      .PutJsonAsync(brand);
Put example

or

await AppConstants.BaseUrl
      .AppendPathSegment(ApiServices.BrandApi)
      .PutStringAsync(data);
Put example

DELETE:

await AppConstants.BaseUrl
      .AppendPathSegment(ApiServices.BrandApi)
      .SetQueryParam("id", brand.Id)
      .DeleteAsync();
Delete example

Authorization

For some API we need to implement Authorization. For that it is necessary to add headers to HttpClient in each request:

client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");

Flurl JWT authorization:

.WithOAuthBearerToken(token)

or

Flurl Basic authorization:

.WithBasicAuth(username, password)

Parameters

Moreover, you can add any parameters to your request if needed, like:
http://baseurl/Brand?id=1

                await AppConstants.BaseUrl
                    .AppendPathSegment(ApiServices.BrandApi)
                    .SetQueryParam("id", brand.Id)
Parameters

Segments

Segments can be added in the following way :
http://baseurl/Brand/1

.AppendPathSegment("1")
Single segment

or

.AppendPathSegments("api", "V2")
Array segments

Flurl and IoC container

One of the benefits is that we can use Flurl as instance for each service with different base url and config. For that IFlurlClientFactory should be registered in container and inject to constructor of service.

1. Register IFlurlClientFactory in App.cs:

containerRegistry.RegisterSingleton<IFlurlClientFactory, PerBaseUrlFlurlClientFactory>();
Register IFlurlClientFactory in container DryIoC

2. Add dependency injection to our service:

    public class BrandFlurlService : IBrandService
    {
        private readonly IFlurlClient _flurlClient;

        public BrandFlurlService(IFlurlClientFactory flurlClientFac)
        {
            _flurlClient = flurlClientFac.Get(AppConstants.BaseUrl);
        }
Injection 

We inject IFlurlClientFactory to constructor and get IFlurlClient instance with base url for each service different if need.

3.  Use IFlurlClient:

         result = await _flurlClient.Request(ApiServices.BrandApi, id)
                .GetJsonAsync<Brand>();
Use IFlurlClient

Handling exception:

Flurl.Http has its own exceptions:

  1. FlurlHttpException - base Flurl exception, contains HttpCall property with status and other information about request.
  2. FlurlParsingException - returns if body can't be parsed.
  3. FlurlHttpTimeoutException - throw by time out.
        try
        {
            result = await AppConstants.BaseUrl
                .AppendPathSegment(ApiServices.BrandApi)
                .GetJsonAsync<IList<Brand>>();
        }
        catch (FlurlHttpTimeoutException fhte)
        {
            Debug.WriteLine(fhte.StackTrace);
        }
        catch (FlurlParsingException fpe)
        {
            Debug.WriteLine(fpe.StackTrace);
        }
        catch (FlurlHttpException fhe)
        {
            Debug.WriteLine(fhe.StackTrace);
        }
Example handling exception

Summary:

To sum up, Flurl is very useful in mobile app development. It's very flexible, reduces development time and makes it easy to test.

Sample: https://github.com/Tech-Fabric/FlurlInXamarinForms

Flurl documentation: https://flurl.dev/

Oleksandr Bobel

Xamarin Developer

Comments