2
\$\begingroup\$

I'm trying to keep my sanity while working with a legacy winforms application that uses an web api, both maintained by my team.

In the past we have multiple cases of methods using wrong api endpoints, or even having different names in both client and server implementation, so we came with the idea of having an interface to represent the API available methods:

public interface IUser
{
    Task<List<Data>> Fetch(string login);
}

Using this approach, the server API implementation is pretty straightforward:

public class UserController : MyController<User>
{
    [HttpGet, ResponseType(typeof(List<Data>))]
    public async Task<IHttpActionResult> Fetch(string login)
    {
        var business = GetBusiness();
        var result = await business.Fetch(login);
        return Ok(result);
    }
}

public class User: MyBusiness, IUser
{
    public async Task<List<Data>> Fetch(string login)
    {
        using (var context = GetContext(...))
        {
            var result = await context...;
            result.SomethingElse = await SomethingElse();
            return result;
        }
    }

    public async Task<Foo> SomethingElse()
    {
        using (var context = GetContext(...))
        {
            return await ...;
        }
    }
}

In the client side, we don't want the presentation layer dealing with web api request URL formatting, so we encapsulate those details inside a web api class wrapper which implements the same interface:

public class User: IUser
{
    private User()
    {
    }

    private static class ApiConstants
    {
        public const string Fetch = "api/{0}/User/Fetch";
    }

    public static readonly User Data = new User();

    public async Task<List<Data>> Fetch(string login)
    {
        var result = new List<Data>();
        try
        {
            result = await ApiHelper.GetAsync<List<Data>>(
                User.ApiConstants.Fetch, new NameValueCollection
                {
                    {"login", login},
                });
        }
        catch (Exception ex)
        {
            Logger.Error(ex);
        }
        return result;
    }
 }

Whenever I do need to use this in the presentation layer, I would do something like this:

private void ok_Click(object sender, EventArgs e)
{
    var data = Extensions.BlockWithoutLockup(
        async () => await Api.User.Data.Fetch(login));

    // do stuff
}

Things there are currently bugging me:

  • Swallowing the error in the client api wrapper;

    • fixing this involves creating a generic error handling to tell apart business validations from infrastructure issues.
    • We're planning to throw the exception all way to the interface and it will take the proper action based on the exception type.
  • Having the ApiConstants inner class

    • We're considering using StackTrace to get the caller method/class and compose the URL but StackTrace doesn't play nice with async calls
    • as having that constant could be useful for dealing with endpoint naming exceptions, we don't feel like we should provide such level of customization by now
  • Have to type several times the API call return type:

    • fair occurrences:
      • in the common interface
      • in the API business implementation
      • in the wrapper method declaration
    • in the Controller ResponseType attribute, for generating dynamic documentation help pages
    • in the result variable within the wrapper method
    • passing the same T to the ApiHelper.GetAsync method
  • Initializing the database context several times in the API layer:

    • we're considering create a MyBusiness.Context protected property
      • using this we could create it only once and pass it through dependant calls;
      • this would also provide support to transactions down in the road
\$\endgroup\$
  • \$\begingroup\$ I didn't understand why swallow the exception. Why not just let it be thrown and add a middleware that log the exception? \$\endgroup\$ – shanif Apr 11 at 20:51
  • \$\begingroup\$ @shanif, in the past they tried a quick and dirt path to start using the api; that went bad as winforms UI doesn't play nice with async/await and at some point they starting getting flooded with error messages; at that moment the introduced this idea on do not fail in the api level just return empty responses and that was implemented in the wrapper level; i think it is time now to remove that ugly fix \$\endgroup\$ – Rubens Farias Apr 12 at 12:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.