Calling ASP.NET Web API end-points for testing purposes with Refit in .NET Core

Using Refit to call Web API methods via .NET Core

Back in August I wrote Calling ASP.NET Web API end-points for testing purposes where I described a "bare metal" approach to calling Web Api methods using HttpClient by creating a wrapper that could then be used to periodically call methods in an application, which I was using in production to verify the health of certain application components. This code can be massively simplified by using the Refit library to do the heavy lifting.

Using Refit in a .NET Core Console App

First up is creating the console app which I've done this time around using Visual Studio, then adding a reference to Refit via the Package Manager console:

Install-Package Refit

With Refit added to the project, the next thing to do is to add an interface that describes the API methods that will be called, annotating them with an attribute that describes the HTTP method that each method requires along with parameters that describe the, well, parameters that each API method takes when called:

using Refit;
using System.Threading.Tasks;

namespace TestingWebApiMethodsWithRefit
{
    public interface ILoginApi
    {
        [Post("/api/login/authenticatecredentials")]
        Task<LoginResponse> Login([Body]LoginRequest loginRequest);
    }
}

Here's the definition of the API method that this will be used to call:

[Route("api/login/authenticatecredentials")]
public async Task<IHttpActionResult> AuthenticateCredentialsAsync([FromBody] AuthenticateCredentials authenticateCredentials)

So the ILoginApi interface has one member, a method called Login which will be used to call a Web Api method via the URI /api/login/authenticatecredentials returning a response that can be translated into a class called LoginResponse (shown below). This method actually takes multiple parameters (Username and Password) contained in an instance of AuthenticateCredentials via the Body of the POST request, which is why the only parameter to ILoginApi.Login is annotated with the Body attribute.

In my .NET Core console app I've defined LoginResponse and LoginRequest as:

public class LoginRequest
{
    public string Username { get; set; }
    public string Password { get; set; }
}

public class LoginResponse
{
    public int[] SecondFactorLetterPosition { get; set; }
    public string TemporaryAuthenticationToken { get; set; }
}

With these defined it's now a matter of making a call to the API method and seeing it succeed (which does also require me to enter the password correctly in the test code!). This can be achieved by adding two lines to the programs Main method:

var api = RestService.For<ILoginApi>("https://localhost");
var loginResult = await api.Login(new LoginRequest { Username = "[email protected]", Password = "MyPasswordGoesHere" });

This is a lot simpler than the solution I wrote previously as up until this point the only code I've written is boiler-plate that describes the API surface, there's not one single line of code that actually really does anything until we get to the code that goes in the Main method. Unfortunately, building the app throws up a small niggle as I'm using await in a method that isn't async. This is easy to fix by changing the signature of the Main method to:

static async Task Main(string[] args)

But (and there's always a but!) that then throws up CS5001 Program does not contain a static 'Main' method suitable for an entry point from the compiler. Luckily that's quite easily solved by updating the project to target a version of C# that supports async Main:

Using project properties to change the version of C# that a project is targeting

With that out of the way, building and hitting F5 (after setting a breakpoint on the line after the call to api.Login so the result can be seen!) spins for a moment and then hits the breakpoint so we can take a look at the result:

The result of using Refit to call a Web Api method

Looks good! If you want/need to handle failures (which you will!) you can catch instances of Refit.ApiException being thrown and react accordingly.

About Rob

I've been interested in computing since the day my Dad purchased his first business PC (an Amstrad PC 1640 for anyone interested) which introduced me to MS-DOS batch programming and BASIC.

My skillset has matured somewhat since then, which you'll probably see from the posts here. You can read a bit more about me on the about page of the site, or check out some of the other posts on my areas of interest.

No Comments

Add a Comment