Tuesday, September 5, 2017

Consuming a WebAPI Service With .NET Core 2.0

Last time, we built a WebAPI service using .NET Core 2.0. This time, we'll write a console application/library that consumes it. Our goal today is to create a console project, make a service call, and use a NuGet package to help us parse the data.

This is part of the initial port of my Task sample code. Today we'll build the console application and get data from the service. In a later article, we'll explore Task in more detail.

Note: This code is available on GitHub: jeremybytes/task-app-core.
We'll also use the service created previously at GitHub: jeremybytes/person-api-core.

As with the service project, I'll be using the command-line interface (CLI) along with Visual Studio Code. I'm on macOS, but this will work the same on Windows 10. Also, as before, I'm starting with (1) the .NET Core SDK, (2) Visual Studio Code, and (3) the Visual Studio Code C# extension already installed. You can check the resources on the Microsoft announcement article to get the environment set up.

Our output isn't too exciting, but we will display data from the service in the terminal window:

Let's get started.

Initializing the Console Application Project
We'll start by building a new console application using .NET Core 2.0. For this, I created a folder for the project named "TaskApp". Once in that folder, we can type

     dotnet new console

to create a new console application project with the same name: TaskApp.csproj.

From here, we can open the "TaskApp" folder in Visual Studio Code.

When we open the folder, we get a popup to add some required assets:

When we click "Yes", Visual Studio Code will add a ".vscode" folder and a couple of files.

Here's how our initial file structure looks:

From here, we can do a build just as a sanity check that everything is working. Back in the terminal window we can use

     dotnet build

to build our console application.

It looks like everything is working so far.

Project Design Decisions
Visual Studio Code supports solutions that allow us to group multiple projects together. So why do we have the WebAPI service and console application completely separated?

This is mainly to make things easier to run. Without setting up any special build/run commands, we can simply navigate to the folder with the WebAPI service and type

     dotnet run

to start up the self-hosted service. Then we can leave this terminal window open (and the service running) while we work on the console application in a different terminal window.

It may not be the best solution, but it was easy for me to set up and keep track of what is running. I can also easily shut down the service to test the error handling of the application (as we'll see in a future article).

In addition, the full project that this code is based on uses a separate library for the repository. To keep things simple, we'll put the repository class right in the console application. As I get more comfortable with the environment, I may change my approach on this. But I think it works out fine for this sample.

Calling the Service
With the project set up, we'll need to write some code to call the service. But before that, we'll add the data class that describes a custom type.

We're dealing with "Person" objects, so we can add a "Person.cs" file to our project.

This class is available on GitHub: Person.cs, and it has the same members as the class in the service project. To keep things simple, we'll just copy the code over and change the namespace to match our console project.

I don't really like the idea of duplicating code from the service. This is something I'll be working to make a bit better in the future. For now it does get the job done.

Now that we have the type of our data, we can make the service call. This code can come almost straight across from the .NET 4.6 project that it is based on. There's just one change we really need to make.

We'll add a "PersonRepository.cs" file to our project and then put in the following code. (This is fairly similar to the completed PersonRepository.cs on GitHub.)

This code uses the HttpClient class to get data from our WebApi service. There's also a bit of asynchronicity added to demonstrate Task and await (including an artificial 3 second delay). We won't worry about those parts too much today; we'll look at them in the future.

Notice that we have some red squigglies. This is because "ReadAsAsync<T>" does not exist in the .NET Core library. Instead we'll need to do a bit of parsing ourselves.

And for that we'll use the Newtonsoft.Json NuGet package.

Adding a NuGet Package
To use Newtonsoft.Json, we'll need to bring the package into our project. We can do this from the command line with

     dotnet add package Newtonsoft.Json

One quirk about using this approach is that we need to know the exact name of the NuGet package. This isn't much different from using the NuGet command line tools in Visual Studio, but I don't know anyone who actually uses those regularly. Most folks I've come across (including myself) use the UI-based tools in VS. If needed, we can always search for the packages using https://www.nuget.org/.

When we flip back to Visual Studio Code, we get prompted to resolve some dependencies.

Just click "Restore".

Deserializing Data
Now we just need to fix our code a bit to use the JSON deserializer. We need to add

     using Newtonsoft.Json

to the top of our file.

Then we use the JsonConvert object in our code:

The reason that we need this is because we do not have the "ReadAsAsync<T>" method available to us. But we do have "ReadAsStringAsync". This gives us the raw JSON that we can then parse using the "DeserializeObject<List<Person>>" method.

The Console Application
Now that we've added our new files (again, these are technically "library" files, but I've put them in the console project for simplicity), we can get to modifying our console application itself.

Here's the "Program.cs" file that was created for us:

Now we replace the contents of "Main" with our own code. (Note: this code is a bit simpler than what is in the completed Program.cs file on GitHub. We'll look at the details of using Task in a future article.)

This creates an instance of the "PersonRepository" class and then calls the "GetAsync" method. Since we're not dealing with cancellation yet, we can pass "CancellationToken.None" as the parameter. (We'll look at what that means when we take a closer look at Task.)

The "ContinueWith" method sets up a continuation, meaning code that will run when the asynchronous task completes. In addition, this continuation only runs if the task completes successfully (as denoted by "TaskContinuationOptions.OnlyOnRanToCompletion").

The continuation will print out the "Person" objects to the screen and then exit the console application.

We have a "Console.ReadLine()" method to make sure that the console application does not exit before the asynchronous process completes. But we can hit "Return" at any time to stop waiting.

Running the Application
With this in place, we can now run the application. Since this calls the WebAPI service, we need to make sure to start the service before running our console application.

To run the service, we navigate to the folder where the service project is located (which is "PersonAPI" based on the previous article) and type

     dotnet run

This will start the service at the location specified in the project.

We'll need to leave the service running, so we'll go to another terminal window to run the console application.

From a terminal in the "TaskApp" folder, we can use

     dotnet run

to kick off the console application. We do not need a separate "build" step here. Just like with our service, when we use "dotnet run" on a folder, it checks for launch instructions in the ".vscode" folder. By default, this includes a "build" step, so our code is always built before it is run. (Note: there are other ways to run the console application that do not require a build, but we won't look at that today.)

After the project builds, we'll get the initial output:

Then after 3 seconds, we get the results from the service:

The 3 second delay is an artificial one. It comes from the "await Task.Delay(3000)" that is in the "GetAsync" method. The delay is useful to help explore some asynchronous features such as cancellation and continued processing while the async method runs. (Again, we'll look at this in the next article.)

We don't have any error handling at the moment. so if we get an exception, then the console application will never exit.

If the console application hangs at "One Moment Please..." (meaning, it doesn't do anything after the 3 second delay), then it is most likely that the service is not running or that it is running on a different port.

You can double-check the service by navigating to http://localhost:9874/api/people in your browser. This should give the following result:

If this result doesn't appear and the service is running, then check the location listed in the terminal where it says "Now listening on:". If it has a different location, check the previous article for instructions on how to change that.

The Completed Project
Just like with our WebAPI project, we only need a few files for the console application. Here's what our folder looks like now:

Both the console application and WebAPI projects are available on GitHub.

Console: https://github.com/jeremybytes/task-app-core
WebAPI: https://github.com/jeremybytes/person-api-core

The console application on GitHub has a bit more code to show cancellation, asynchronicity, and error handling. That will be our next stop.

Wrap Up
There's still a bit more to do with this code, but this gets us the basics for setting up a console application, consuming a web service, and even how to get NuGet packages into our .NET Core projects.

Stay tuned because there is still much more to explore.

Happy Coding!

No comments:

Post a Comment