Vlad's blog

In programming veritas

Archive for January 2018

Components of good Web API client. Part 1.

leave a comment »

There are a lot of posts and guides devoted to the subject of creating Web API, but most of them are focused on server side part. The client code is often covered superficially making impression that this subject is trivial and is not worth discussing. In this post I describe some important aspects of developing Web API client. The example code written in C# but many concepts are language agnostic.

For the sake of illustration let’s consider JIRA like system that allows to create,delete and update projects.

Resource: a collection of projects
Url: http://localhost:54531/api/projects
HTTP methods: GET,POST

Resource: a project
Url: http://localhost:54531/api/projects/{id}
HTTP methods: GET,PUT,DELETE,PATCH

Example:
Url: http://localhost:54531/api/projects
HTTP method: GET

Response:

[
 {
 "id": "a3260d06-4c19-4422-bf36-d2e90bd5c85b",
 "name": "Project 1",
 "state": "InProgress",
 "created": "2018-01-13T13:15Z",
 "updated": "2018-02-14T15:07Z" 
 }
]

Also API must support paging and data shaping.
Example:
Url: http://localhost:54531/api/projects?pageNumber=1&pageSize=20&fields=id,name

First version

If we follow REST approach our API should be focused on resources which are projects. Let’s start from a collection of projects.
Url: http://localhost:54531/api/projects
HTTP methods: GET,POST
First thing you want in your client API is a method for getting a collection of resources. The first version simple returns a list of projects.

public static async Task Test()
{
    List<ProjectDto> projects = await ProjectsResource.Get();
}

class ProjectsResource
{
    public static Task<List<ProjectDto>> Get()
}

class ProjectDto
{
    public ProjectResource This { get; private set; }

    public string Id { get; set; }
    public string Name { get; set; }
    public DateTime Created { get; set; }
    public DateTime Updated { get; set; }
    public ProjectState State { get; set; }

    public enum ProjectState
    {
        NotStarted,
        InProgress,
        Finished
    }
}

Read the rest of this entry »

Advertisements

Written by vsukhachev

January 29, 2018 at 3:09 am

Posted in Development

Tagged with

Don’t use exceptions for validation

leave a comment »

Exceptions are often used for validation. In the code below we check if a name of a customer is already exist in a repository and throw an exception if it is true. The exception is used to pass validation error message.

public Customer AddCustomer(string firstName, string lastName)
{
    if(customers.Any(x => x.FirstName == firstName && x.LastName == lastName))
        throw new Exception($"Failed to add a customer: the customer with this name already exists");

    var customer = new Customer(firstName, lastName);
    customers.Add(customer);

    return customer;
}

The client code is below.

try
{
    Customer customer = repository.AddCustomer("Vlad", "Sukhachev");

    repository.Save();
}
catch (Exception e)
{
    ShowValidationError(e.Message);
}

There are two problems with this approach. First, the signature of AddCustomer is dishonest. In order to realize that this method throws an exception the developer has to review the source code. The second concern is more subtle. By the definition, the exception is used to signal an exceptional situation which is a result of a bug in code. Wrong user input certainly does not fit into this category.

In order to fix this issue we need to use Result class from CSharpFunctionalExtensions. Now the repository code looks like this.

public Result<Customer> AddCustomer(string firstName, string lastName)
{
    if (customers.Any(x => x.FirstName == firstName && x.LastName == lastName))
        return Result.Fail<Customer>($"Failed to add a customer: the customer with this name already exists");

    var customer = new Customer(firstName, lastName);
    customers.Add(customer);

    return Result.Ok(customer);
}

And the client code.

Result<Customer> result = repository.AddCustomer("Vlad", "Sukhachev");

if(result.IsFailure)
    ShowValidationError(result.Error);

repository.Save();

Now when we look at the signature of AddCustomer method we can say that it can return a result indicating failure and we don’t use an exception to pass validation error. That does not mean that we should avoid using exceptions. Even in this simplified example there is a legal case where an exception is relevant. CustomerRepository.Save can raise an exception in case of any database related issue.

Written by vsukhachev

January 20, 2018 at 6:57 pm