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);
}

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>>();

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);

or

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

GET:


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

or

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

PUT:

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

or

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

DELETE:

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

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>();

2. Add dependency injection to our service:

    public class BrandFlurlService : IBrandService
    {
        private readonly IFlurlClient _flurlClient;

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

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>();

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);
        }

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