New Razor Pages Project Backed with an API

This week we are going to add a Razor Pages project that will utilize the API we created a few weeks ago. This post is part of the revamp of my ASP.NET Core Basics repo that I kicked off when .NET Core 3.0 was released. For details on how we got to the current point in the application check out the following posts.

Swagger/OpenAPI with NSwag and ASP.NET Core 3
ASP.NET Core 3: Add Entity Framework Core to Existing Project

The code before the changes in this post can be found in this GitHub repo.

Razor Pages Project

Add a new directory for the application and then in a terminal navigate to that directory. Then the following command can be used to create the new Razor Pages application.

dotnet new webapp

Next, use the  following command to add the new project to the solution file which is in the root of the repo. Your filenames and paths could vary if you can’t using the same code of course.

dotnet sln ..\..\BasicsRefresh.sln add ContactsRazorPages.csproj

API Access Setup

For API access we are using NSwag to generate a client that our Razor Page application will use. For the actual creation of the API client see the following posts as this post will be skipping the actual client generation process.

Using NSwag to Generate C# Client Classes for ASP.NET Core 3
Use HTTP Client Factory with NSwag Generated Classes in ASP.NET Core 3

With the client-generated and in our local Apis directory in the Razor Pages project we can now work on getting it configured and registered for use in our new project. First, open the apppsetting.json file and add a setting for the URL of our API, which is the ContactsApi value in the following sample.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ContactsApi": "https://localhost:5001"
}

Next, in the ConfigureServices function of the Startup class we need to register a HTTP Client for our API.

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages()
            .AddNewtonsoftJson();

    services.AddHttpClient<IContactsClient, 
                           ContactsClient>(client => 
             client.BaseAddress = new Uri(Configuration.GetSection("ContactsApi").Value));
}

Add Pages

Now that our API access is set up we need to create pages that will allow users to interact with the API. To start add a Contacts directory to the existing Pages directory so all of the pages that deal with interacting with the Contacts API will be together.

CAUTION the next bit may or may not be helpful. I wanted to generate the UI for the Contact pages instead of having to manually create them using the scaffolding, but it needs Entity Framework to work and this new project doesn’t use Entity Framework. This section is going to walk through adding a temporary reference to the API project, since it does use Entity Framework, in order to generate the related UI. Feel free to skip this part if you want to manually create your associated UI.

In the API project add the following temparary changes to the ContactsDbContext class.

public ContactsDbContext() {}

protected override void OnConfiguring(DbContextOptionsBuilder options) => 
          options.UseSqlite("Data Source=app.db");

Now we need to add a temporary reference to the API project from the Razor Pages project. To do this right-click on the Dependencies node in Razor Pages project and select Add Reference.

In the Projects section check the box for the API project and click OK.

Now with the above in place, we can scaffold our UI. Right-click on the folder where you want the resulting UI to live, the Pages/Contacts directory in our case. From the menu select Add > New Scaffolded Item.

On the dialog that shows we want to select Razor Pages using Entity Framework (CRUD) and then click Add.

On the next screen we will be selecting the Model class and Data context class from the API project for the entity we are generating the UI for and then clicking Add.

After a few seconds, all the pages we need to view, create, edit, and delete contacts will exist. Now that we have our pages generated we need to remove the reference to the API project. To do this expand the Dependencies > Projects node and right-click on the API project and select Remove.

Also, revert the changes we made to the DbContext above.

Now that the reference to the API project is gone the Razor Pages application won’t build. This is expected as it was using some classes from the API project. We are going to walk through the edits needed to fix the issues in the Index page in the Contacts directory, but the same type of changes will be needed in all the generated classes.

First, we need to change some usings. Remove any Entity Framework related usings. Then change any related to the Contacts API to instead reference the API client local to the project.

Before:
using Microsoft.EntityFrameworkCore;
using ContactsApi.Data;
using ContactsApi.Models; 

After:
using Apis;

The other big item is to replace the injection of the Entity Framework DB Context with the API Client and update the related calls with calls to the API. The following is the IndexModel with the Entity Framework bits present.

public class IndexModel : PageModel
{
    private readonly ContactsApi.Data.ContactsDbContext _context;

    public IndexModel(ContactsApi.Data.ContactsDbContext context)
    {
        _context = context;
    }

    public IList<Contact> Contact { get;set; }

    public async Task OnGetAsync()
    {
        Contact = await _context.Contacts.ToListAsync();
    }
}

And here is the end result using the API Client.

public class IndexModel : PageModel
{
    private readonly IContactsClient _client;

    public IndexModel(IContactsClient client)
    {
        _client = client;
    }

    public IList<Contact> Contact { get;set; }

    public async Task OnGetAsync()
    {
        Contact = (await _client.GetContactsAsync()).ToList();
    }
}

And as stated above this kind of thing would need to be repeated for the other generated pages.

END CAUTION

Add to Navigation Bar

Now that we have our pages created we need to add a way for the user to get to them. To do this we are going to add a Contacts option to the navigation bar. Open the Pages/Shared/_Layout.cshtml file. The easiest way to locate where the change needs to go is to search for the text of one of the existing navigation links. The following is the links section with the new items added.

<ul class="navbar-nav flex-grow-1">
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="" asp-page="Contacts/Index">Contacts</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
    </li>
</ul>

Wrapping Up

Using Nswag’s generated client makes it super simple to connect an application to an API, not that doing it manually is hard per se. Most of this post ended up being about my detour to generate the UI in the client application. Was it worth it? I’m not sure. I guess either way it is nice to know it is an option when you have the Entity Framework data available.

Here is the code in the final state from this post.

Use HTTP Client Factory with NSwag Generated Classes in ASP.NET Core 3

In last week’s post, Using NSwag to Generate C# Client Classes for ASP.NET Core 3, we left off with a usable client, but we were missing out on using some of the features provided by ASP.NET Core such as the HTTP Client Factory and utilizing dependency injection.

Changes to NSwag Client Generation

This post is only going to point out the difference needed to help enable utilization of the ASP.NET Core features mentioned above and won’t be a full walkthrough of using NSwag. If you need a reference for what this post is covering make sure and read last week’s post.

The one change needed from last week’s post is to check Generate interfaces for Client classes.

With the above checked the client class can be regenerated and the files in the consuming application updated.

Using HTTP Client Factory and Dependency Injection

In the consuming application, we need to add the following to line in the ConfigureServices function of the Startup class to add an HTTP Client specifically for our Contacts API and make it available via the dependency injection system.

services.AddHttpClient<IContactsClient, ContactsClient>(client => 
           client.BaseAddress = new Uri("https://localhost:5001"));

For a production application, I would recommend using the configuration system to store the URL for the API instead of hardcoded like it is above.

For example usage, I’m using the IndexModel. First,  add a class-level field to hold our API client and inject the client via the constructor.

private readonly IContactsClient _contactsClient;

public IndexModel(ILogger<IndexModel> logger, IContactsClient contactsClient)
{
    _logger = logger;
    _contactsClient = contactsClient;
}

Now that we have a contacts client at the class-level we can use it get data from our API. The following example uses the client to get all the contacts from the API and stores them in a variable.

public async Task OnGet()
{
    var contacts = await _contactsClient.GetContactsAsync();
}

Wrapping Up

I highly recommend using this style of client vs. using HTTP client directly. If you do some searching you will find that managing the lifetime of HTTP client in .NET before the HTTP client factory was something that is easy to screw up.

The following posted were used as references:

Generating a Typed Client for use with HttpClientFactory using NSwag
How to add generated HttpClient to ASP.NET Core dependency injection

Using NSwag to Generate C# Client Classes for ASP.NET Core 3

This post is going to use one of the tools provided by NSwag to generate C# client classes to provide access to an API. While the NSwag tooling provides multiple ways to discover the definition of an API we will be using the tooling to generate C# classes from an OpenAPI/Swagger specification.

For details on how to use NSwag to provide OpenAPI/Swagger for your APIs check out my Swagger/OpenAPI with NSwag and ASP.NET Core 3 post. You can grab the API I’m using in the post from this GitHub repo if you need an API to play around with. If you do grab the sample API from GitHub not that it does use Entity Framework Core and SQLite which means you will need to create the associated database. Details of how to do that can be found in the Create and Apply Initial Migration section of my ASP.NET Core 3: Add Entity Framework Core to Existing Project post.

Sample Client Application

For this example, we will spin up a Razor Pages application using the .NET CLI with the following command from your favorite terminal application in the directory you want the application created.

dotnet new webapp

NSwag Client Generation

NSwag provides multiple options for client generation including a CLI option, code, and a Windows application. This post is going to use the Windows application which is called NSwagStudio. Download and install NSwagStudio from here.

Next, make sure your API is running and get the URL of its OpenAPI/Swagger specification URL. For example, I am using a local instance of my API and the URL I need is https://localhost:5001/swagger/v1/swagger.json. If you are using the Swagger UI you can find a link to your swagger.json under the API title.

Now that we have the OpenAPI/Swager specification URL for the API we are dealing with open NSwagStudio. The application will open with a new document ready to go. There are a few options we will need to set. First, we want to use the NetCore30 Runtime. Next, select the OpenAPI/Swagger Specification tab and enter your API’s specification URL in the Specification URL box.

In the Outputs section check the CSharp Client checkbox and then select the CSharp Client tab. As you can see from the screenshot below there are a ton of options to tweak. For this example, we are taking the defaults for all of them except for Namespace, which I set to ContactsApi, and Output file path, which is only needed if you use the Generate Files option. Click the Generate Files button and NSwagStudio will create a file that contains all the code needed to access the API described in the OpenAPI/Swager specification selected in the Input section.

Note, the Generate Outputs button can be used if you want to see what the generated code will look in the Output tab on the same level as Settings.

Use Generated Client from the Sample Project

In the sample project, I created an APIs directory and dropped the ContactsApi.cs created with NSwagStudio there. The files generated with NSwagStudio are expecting JSON.NET to be present so the sample project will need a reference to the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package.

Now that the project has a reference to JSON.NET in the ConfigureServices function of the Startup class we need to tell the app to make JSON.NET available via dependency injection with the following change.

services.AddRazorPages()
        .AddNewtonsoftJson();

Now to test out the client I used the following OnGet function in the Index.cshtml.cs file.

public async Task OnGet()
{
    using (var httpClient = new HttpClient())
    {
        var contactsClient = new ContactsClient(httpClient);
        var contacts = await contactsClient.GetContactsAsync();
    }
}

Note the above is only meant to show that the generated client work and isn’t meant to be a production-grade example. For more production-grade scenarios make sure and following Microsoft’s guidance on HTTP client usage.

Wrapping Up

NSwag’s client generation seems to be an easy way to get started consuming API’s. I’m not sure if the CLI would provide more options for how the client code is generated or not with support of HTTPClientFactory and strongly typed HTTP Clients. This will be something I may explorer more in a future post.

Swagger/OpenAPI with NSwag and ASP.NET Core 3

Now that .NET Core 3 is out I thought it would be a good time to revisit exposing API documentation using Swagger/OpenAPI. In the past, I have written posts on using Swashbuckle to expose Swagger documentation, but for this post, I’m going to try out NSwag.

What is OpenAPI vs Swagger?

To quote the Swagger docs:

OpenAPI Specification (formerly Swagger Specification) is an API description format for REST APIs. An OpenAPI file allows you to describe your entire API. API specifications can be written in YAML or JSON. The format is easy to learn and readable to both humans and machines.

Swagger is a set of open-source tools built around the OpenAPI Specification that can help you design, build, document and consume REST APIs.

What is NSwag?

Quoting the NSwag GitHub readme:

NSwag is a Swagger/OpenAPI 2.0 and 3.0 toolchain for .NET, .NET Core, Web API, ASP.NET Core, TypeScript (jQuery, AngularJS, Angular 2+, Aurelia, KnockoutJS and more) and other platforms, written in C#. The OpenAPI/Swagger specification uses JSON and JSON Schema to describe a RESTful web API. The NSwag project provides tools to generate OpenAPI specifications from existing ASP.NET Web API controllers and client code from these OpenAPI specifications.

One neat thing about NSwag is it also has the tooling to help generate the API consumer side in addition to the OpenAPI specs.

Sample Project

For this post, I created a new API project via the .NET CLI using the following command. Not that all this can be done via the Visual Studio UI if that is your preference.

dotnet new webapi

For me, this project is going to be the start of a new series of posts so I also added a solution file and added the project created above to it. These commands are optional.

dotnet add sln
dotnet sln add src\ContactsApi\ContactsApi.csproj

Add NSwag

Using the CLI in the same directory as the project file use the following command to add a reference to NSwag.AspNetCore to the project.

dotnet add package NSwag.AspNetCore

Next, in your favorite editor open the project/directory we created and open the Startup.cs file. In the ConfigureServices function add services.AddOpenApiDoccument.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddOpenApiDocument();
}

Then at the end of the Configure function add calls to app.UseOpenApi and app.UseSwaggerUi3.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment()) app.UseDeveloperExceptionPage();

    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });

    app.UseOpenApi();
    app.UseSwaggerUi3();
}

Note that NSwag also supports ReDoc if you prefer that over Swagger UI.

Sample Model and Controller

Now that we have NSwag installed let’s create a new endpoint for it to display. As per my norm, I will be doing this using contacts as an example. First I created a Models directory and then added the following Contact class to it.

public class Contact
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string PostalCode { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
}

Next, in the Controllers directory add a ContactsController, which in the following code returns a list of 5 generic contacts.

[ApiController]
[Route("[controller]")]
public class ContactsController : ControllerBase
{
    private readonly ILogger<ContactsController> _logger;

    public ContactsController(ILogger<ContactsController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IEnumerable<Contact> Get()
    {
        return Enumerable.Range(1, 5).Select(index => new Contact
        {
            Id = index,
            Name = $"Test{index}",
            Address = $"{index} Main St.",
            City = "Nashville",
            State = "TN",
            PostalCode = "37219",
            Phone = "615-555-5555",
            Email = $"test{index}@test.com"
        });
    }
}

Results

Run your project and then in a browser navigate to your base URL /swagger. For example my for my project that is https://localhost:5001/swagger. You should see something like the following that will let you explore your API and even execute requests against your API using the Try it out button you see in the UI.

Wrapping Up

Just like with Swashbuckle, NSwag makes it very easy to get started providing API documentation. This post just covers the very basics and I’m looking forward to digging into some of the more advanced features that NSwag has such as client generation.

Microsoft has a great article on Getting Started with NSwag on their docs site that I recommend reading. This is a preview of something I plan to cover in the future, but there are attributes that can be added to controllers that help NSwag provide better details about what your API can return and Microsoft has a doc on Use web API conventions that makes it easy to apply some of the common conventions.

ASP.NET Core 2.1: ActionResult

This post is going to take the Contacts API from my ASP.NET Basics set of posts and move it from using IActionResultto ActionResult<T> which was introduced with the 2.1 release. The changes are really simple, but if you are using OpenAPI/Swagger I have a call out later in the post about something I noticed. The code before any changes can be found here.

IActionResult vs ActionResult<T>

The official docs explain the three different ways to return data in an API which are a specific type, IActionResult type, or ActionResult<T> type.

A specific type is great if you don’t have to do any sort of validation or the like, but as soon as you need to return a different HTTP status than OK is no longer sufficient. This is where you would have to move to IActionResult.

IActionResult allows different HTTP statuses to be returned. In the following example, NotFound is returned if a contact with the supplied ID isn’t found or OK(contact) if a contact is found.

public async Task<IActionResult> GetContact([FromRoute] int id)
{
     var contact = await _context.Contact
                                 .SingleOrDefaultAsync(m => m.Id == id);

     if (contact == null)
     {
        return NotFound();
     }
    
     return Ok(contact);
}

The advantage of ActionResult<T> it is the return type of the function is clear. You can see in the following example where GetContact has been changed to use ActionResult<T> that if all goes well you will be dealing with a Contact object in the end without the need to wrap the result in an OK.

public async Task<ActionResult<Contact>> GetContact([FromRoute] int id)
{
     var contact = await _context.Contact
                             .SingleOrDefaultAsync(m => m.Id == id);

     if (contact == null)
     {
        return NotFound();
    }

    return contact;
}

OpenAPI/Swagger

If you are using OpenAPI/Swagger in your project with a function with the following definition it will automatically pick up the return type if you switch to using ActionResult<T>.

public async Task<ActionResult<Contact>> GetContact([FromRoute] int id)

The above function results in the following in OpenAPI/Swagger UI.

This is awesome and saves you from having to ProducesResponseType attributes to your API functions. Just note that as soon as you do add a ProducesResponseType for say a NotFound response you will still need include a response for OK with the proper type or you will lose the return type in the OpenAPI/Swagger UI.

I’m calling that last bit out because I spent time trying to figure out why all the samples I saw the return type was automatically picked up, but in my sample application it wasn’t.

Wrapping Up

I’m a huge fan of ActionResult<T> mostly because of the clarity it adds to API function definitions. The fact that OpenAPI/Swagger can pick up on it in the simple cases is an added bonus.

If you are looking for more info check out the Exploring ActionResult<T> in ASP.NET Core 2.1 post by Joonas Westlin in which there is more info on how the functionality is actually implemented. If you didn’t already make sure and check out the Controller action return types in ASP.NET Core Web API page in the official docs for a detailed comparison of the return type options for APIs.

The completed code can be found here.

Pass ASP.NET Core Appsettings Values to Angular via an API Call

There have been a few issues opened on the repo I have showing usage of Angular, Identity Server 4, and ASP.NET Core together that related to incompatibilities with the newer versions of Angular. In an effort to fix this issue the plan was to recreate the client application using the new Angular template from Microsoft which from what I read should address the issue.

The code before any changes can be found here, but in this case, the whole client application has been recreated so the starting point may not be super helpful.

The Problem

For the most part, this worked well, but the problem can when I needed to use some configuration values from ASP.NET Core in my new Angular application. The previous versions of the template used server-side rendering which I utilized to pass the configuration values. The new template doesn’t use server-side rendering by default and I wanted to find a way to solve the issue without requiring server-side rendering.

The other issue is that I want to be able to run this application in Azure and set the configuration values as environment variables. While Angular seems to have support for environment files finding a solution that used a systems environment variables turned out too not be simple.

Configuration API Endpoint

Since the configuration values I need to get to the client application are secret I decided to go the route of pulling them via an API call back to the same ASP.NET Core application that is hosting the Angular Application, which is the Client App project in the sample solution.

I added a ConfigurationController.cs class to the Controller directory with the following contents.

[Produces("application/json")]
[Route("api/Configuration")]
public class ConfigurationController : Controller
{
    private readonly IConfiguration _configuration;

    public ConfigurationController(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    [HttpGet("[action]")]
    public IActionResult ConfigurationData()
    {
        return Ok(new Dictionary<string, string>
        {
            { "IdentityServerAddress", _configuration["IdentityServerAddress"] },
            { "ApiAddress", _configuration["ApiAddress"] }
        });
    }
}

This controller gets constructed with a reference to the application’s configuration which is then used to populate a dictionary with the values my Angular application needs. For completeness, the following is the contents of the application’s appsettings.json file.

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "IdentityServerAddress": "http://localhost:5000",
  "ApiAddress": "http://localhost:5001/api/"
}

Angular Changes

This is the part that I really struggled to get right. I needed the configuration values from the API above to be available as soon as possible. Thankfully I came across this blog post by Juri Strumpflohner which covers using Angular’s APP_INITIALIZER.

The first thing I need was to create a class in Angular to get the configuration values from the API and serve to them the rest of the Angular application. To do this I added a configuration.service.ts into a new ClientApp/src/app/configuration directory. The full class follows.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class ConfigurationService {

  private configuration: IServerConfiguration;

  constructor(private http: HttpClient) { }

  loadConfig() {
    return this.http.get<IServerConfiguration>('/api/Configuration/ConfigurationData')
      .toPromise()
      .then(result => {
        this.configuration = <IServerConfiguration>(result);
      }, error => console.error(error));
  }

  get apiAddress() {
    return this.configuration.ApiAddress;
  }

  get identityServerAddress() {
    return this.configuration.IdentityServerAddress;
  }

}

export interface IServerConfiguration {
  ApiAddress: string;
  IdentityServerAddress: string;
}

This class hits the API to get the configuration values in the loadConfig function and maps it to a class level field. It also provides properties to get the individual configuration values.

As I mentioned above, getting the application to get these configuration values in a timely matter was something I really struggled to do. The first step to using Angular’s APP_INITIALIZER to solve this issue is to change the import from @angular/core to include APP_INITIALIZER and to import the ConfigurationService.  All these changes are being made in the app.module.ts file.

import { NgModule, APP_INITIALIZER } from '@angular/core';
import { ConfigurationService } from "./configuration/configuration.service";

Next, we need to define a function that will call the ConfigurationService.loadConfig function.

const appInitializerFn = (appConfig: ConfigurationService) => {
  return () => {
    return appConfig.loadConfig();
  };
};

Finally, in the providers array add an element for the APP_INITIALIZER and the ConfigurationService.

providers: [
  ConfigurationService,
  {
    provide: APP_INITIALIZER,
    useFactory: appInitializerFn,
    multi: true,
    deps: [ConfigurationService]
  }]

 Wrapping Up

This is one of those things that turned out to be way more complicated than I expected. Thankfully with the above changes, I was able to get it working. I hope this saves you all some time. The code with all the changes can be found here.

Basic ASP.NET Core API Test with Postman

I had a reader email me about using Postman with ASP.NET Core API base on this post from a couple of years ago. Rather than working through that their specific issues are with that code, I thought it might be more helpful to write a post on creating a super basic ASP.NET Core API and use Postman to test it.

API Creation

We are going to use the .NET CLI to create and run API project so no Visual Studio or other IDE will be needed. The first step is to open a command prompt and navigate to (or create) the directory where you want the API project to live. Run the following command to create the API project.

dotnet new webapi

The webapi template creates a ValuesController with a Get action that returns an array with two values in it which we will be using as our test endpoint.

After the process finished we can now run the project using the following command.

dotnet run

After the run command, you should see something like the following.

Hosting environment: Production
Content root path: C:\YourProjectPath\ApiTest
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

The key bit in the above you need to look for is the Now listening on line as that is the URL we will need to use in Postman to test.

Testing with Postman

Postman is a great tool that to use when developing an API. It allows me to exercise all the functions of the API before any clients have been built. You can do some of the same things using a browser, but Postman was built for this type of usage and it shows. Postman is free and you can grab it here.

Run Postman and you will see something similar to the following screenshot.

For our simple test we want to do a Get request, which is the default, so all we need to do is past the URL from above into the address box and add in the route to the controller we are trying to test. For our sample to test the Get action on the ValuesController our URL ends up being http://localhost:5000/api/values.

Click the Send button and the results will show the lower area Postman (the large red box in the screenshot).

Wrapping Up

This is the simplest setup I could think of to get up and going with Postman and ASP.NET Core. Postman has so many more functions than I showed in this post so I hope this will be a good jumping off point for you all to learn more about this great tool.

Electron.NET with a Web API

This post will be expanding on the introduction to Electron.NET that I did here to add in a Web API hit to pull some data as well as the UI on the Electron side to use this data. The code before any changes can be found here.

API Creation

To create the API I used the following from the command prompt in the folder where I wanted the new project to be created.

dotnet new webapi

API Data

Now that the API project is created we need to add in the ability to interact with a database with Entity Framework Core. Adding in Entity Framework Core ended up turning into a post of its own when you can read here.

The model and DB Context of the API project match what was in the blog post I linked above, but I am going to include them here. The following is the model.

public class Contact
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string Subregion { get; set; }
    public string PostalCode { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
}

Next, is the DB Context, which is empty other than the DB Set for the contacts table.

public class ContactsDbContext : DbContext
{
    public DbSet<Contact> Contacts { get; set; }

    public ContactsDbContext(DbContextOptions<ContactsDbContext> options)
        : base(options)
    {

    }
}

With our model and context setup, we can run the following two commands to add the initial migration and apply the migration to the database.

dotnet ef migrations add Contacts
dotnet ef database update

API Endpoints

The API is just going to handle the basic CRUD (create, read, update, delete) operations for contact. Instead of hand coding the controller we are going to use some code generation provided by Microsoft. First, we need to add the Microsoft.VisualStudio.Web.CodeGeneration.Design NuGet package to the API project using the following command in a command prompt set to the root of the API project.

dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design

Now with the above package installed, we can use the following command to generate a controller with the CRUD operations already implemented.

dotnet aspnet-codegenerator controller -name ContactsController --model Contact --dataContext ContactsDbContext -outDir Controllers -api

There is a lot of switches when using aspnet-codegenerator. The following is a rundown of the ones used above.

  • controller tells the code generator we are creating a controller
  • name defines the name of the resulting controller
  • model is the model class that will be used for the generation
  • dataContext is the DB Context that will be used for the generation
  • outDir is the directory the output will be in relative to the current directory of your command prompt
  • api tells the code generator this controller is for a REST style API and that no views should be generated

With the code generation complete the API should be good to go.

Electron Model

Over in the Electron project, we need a model to match the data the API is returning. This could be the point where a third project is added to allow the API and the Electron app to share common items, but just to keep the example simple I’m just going add a copy of the contact model from the API project to the Electron project.  The following is the full contact model class.

public class Contact
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string Subregion { get; set; }
    public string PostalCode { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
}

Electron Views

Now that we have a model in our Electron project we need to create the views that go along with it. Start by adding the code generation package like we did above using the following command.

dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design

Unfortunately, controller generation needs a DBContext to work which our project doesn’t have, so we have to take the long way about to generate our views and then manually create a controller to go with them. In order to get view generation to work, I had to add references to the Entity Framework Core Tools package using the following command.

dotnet add package Microsoft.EntityFrameworkCore.Tools

In the csproj file add the following .NET CLI tool reference.

<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.2" />

Now the project is ready to use the command prompt to generate the views we will need for our CRUD operations related to our contacts. Use the following commands to create the full range of views needed (Create, Edit, List, Delete, Details).

dotnet aspnet-codegenerator view Create Create --model Contact --useDefaultLayout -outDir Views/Contacts

dotnet aspnet-codegenerator view Edit Edit --model Contact --useDefaultLayout -outDir Views/Contacts

dotnet aspnet-codegenerator view Index List --model Contact --useDefaultLayout -outDir Views/Contacts

dotnet aspnet-codegenerator view Delete Delete --model Contact --useDefaultLayout -outDir Views/Contacts

dotnet aspnet-codegenerator view Details Details --model Contact --useDefaultLayout -outDir Views/Contacts

Again there is a lot of switches when using aspnet-codegenerator. The following is a rundown of the ones used above.

  • view  tells the code generator we are creating a view
  • the next two items are the name of the view and the name of the view template
  • model is the model class that will be used for the generation
  • useDefaultLayout uses the default layout (surprise!)
  • outDir is the directory the output will be in relative to the current directory of your command prompt

The Index.cshtml generated above comes with links for Edit, Details, and Delete that won’t work as generated. Open the file and make the following changes to pass the key of the contact trying to be opened.

Before:
@Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
@Html.ActionLink("Details", "Details", new {/* id=item.PrimaryKey */ }) |
@Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })

After:
@Html.ActionLink("Edit", "Edit", new {  id=item.Id }) |
@Html.ActionLink("Details", "Details", new { id=item.Id }) |
@Html.ActionLink("Delete", "Delete", new { id=item.Id })

Electron Controller

With the views complete let’s add a ContactsController.cs to the Controllers directory. The code for the controller follows, but I’m not going to go into the details. I took a controller from another contact base project and just replaces all the Entity Framework stuff with calls to the API we created above. Please don’t use this as an example of how something like this should be done it is just quick and dirty to show that it can work.

public class ContactsController : Controller
{
    private string _apiBaseUrl = "http://localhost:5000/api/contacts/";

    // GET: Contacts
    public async Task<IActionResult> Index()
    {
        using (var client = new HttpClient { BaseAddress = new Uri(_apiBaseUrl) })
        {
            return View(JsonConvert.DeserializeObject<List<Contact>>(await (await client.GetAsync("")).Content.ReadAsStringAsync()));
        }
    }

    // GET: Contacts/Details/5
    public async Task<IActionResult> Details(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }

        using (var client = new HttpClient { BaseAddress = new Uri(_apiBaseUrl) })
        {
            var contact = JsonConvert.DeserializeObject<Contact>(await (await client.GetAsync(id.ToString())).Content.ReadAsStringAsync());

            if (contact == null)
            {
                return NotFound();
            }

            return View(contact);
        }
    }

    // GET: Contacts/Create
    public IActionResult Create()
    {
        return View();
    }

    // POST: Contacts/Create
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create([Bind("Id,Address,City,Email,Name,Phone,PostalCode,State")] Contact contact)
    {
        if (ModelState.IsValid)
        {
            using (var client = new HttpClient { BaseAddress = new Uri(_apiBaseUrl) })
            {
                await client.PostAsync("", new StringContent(JsonConvert.SerializeObject(contact), Encoding.UTF8, "application/json"));
            }

            return RedirectToAction("Index");
        }
        return View(contact);
    }

    // GET: Contacts/Edit/5
    public async Task<IActionResult> Edit(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }

        using (var client = new HttpClient { BaseAddress = new Uri(_apiBaseUrl) })
        {
            var contact = JsonConvert.DeserializeObject<Contact>(await (await client.GetAsync(id.ToString())).Content.ReadAsStringAsync());

            if (contact == null)
            {
                return NotFound();
            }

            return View(contact);
        }
    }

    // POST: Contacts/Edit/5
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Edit(int id, [Bind("Id,Address,City,Email,Name,Phone,PostalCode,State")] Contact contact)
    {
        if (id != contact.Id)
        {
            return NotFound();
        }

        if (ModelState.IsValid)
        {
            using (var client = new HttpClient { BaseAddress = new Uri(_apiBaseUrl) })
            {
                await client.PutAsync(id.ToString(), new StringContent(JsonConvert.SerializeObject(contact), Encoding.UTF8, "application/json"));
            }
            return RedirectToAction("Index");
        }
        return View(contact);
    }

    // GET: Contacts/Delete/5
    public async Task<IActionResult> Delete(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }

        using (var client = new HttpClient { BaseAddress = new Uri(_apiBaseUrl) })
        {
            var contact = JsonConvert.DeserializeObject<Contact>(await (await client.GetAsync(id.ToString())).Content.ReadAsStringAsync());

            if (contact == null)
            {
                return NotFound();
            }

            return View(contact);
        }

    }

    // POST: Contacts/Delete/5
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> DeleteConfirmed(int id)
    {
        using (var client = new HttpClient { BaseAddress = new Uri(_apiBaseUrl) })
        {
            await client.DeleteAsync(id.ToString());
            return RedirectToAction("Index");
        }
    }

    private async Task<bool> ContactExists(int id)
    {
        using (var client = new HttpClient { BaseAddress = new Uri(_apiBaseUrl) })
        {
            return JsonConvert.DeserializeObject<Contact>(await (await client.GetAsync("id")).Content.ReadAsStringAsync()) != null;
        }
    }
}

Electron Add Link To Navigation

The final step to add a link to the list of contacts to the navigation bar of the application. Open the _Layout.cshtml and in the unordered list for the nav bar add the following line.

<li><a asp-area="" asp-controller="Contacts" asp-action="Index">Contacts</a></li>

Wrapping Up

That is all the changes to get the application up and running. If you run the API and then use dotnet electronize start from a command prompt in the ElectronTest project root all should be good to go.

The completed code can be found here.

Refit Basics

A few weeks ago I was watching this episode of the ASP.NET Community Standup and they had Ryan Nowak on to talk about the new HttpClientFactory coming in the 2.1 release and a question came up about compatibility with Refit. I had been meaning to check out Refit but had honestly forgotten about it. This post is going to be a very basic introduction to Refit.

What is it?

In the author’s (Paul Betts) words, Refit is the automatic type-safe REST library for .NET Core, Xamarin and .NET. Cool, but what does that mean? Basically, Refit allows you to define an interface for an API that your application wants to call and using that is hides way all the HTTP and JSON serialization/deserialization bits.

Sample project creation

To test Refit out I created a very simple .NET Core console application. To do the same open a command prompt in the directory you want the project using the following command.

dotnet new console

For this project, I am using Visual Studio Code as my editor. Since VS Code doesn’t have a NuGet UI build in (maybe there is an extension?) I used the following command to add Refit to the project.

dotnet add package Refit

Or if you prefer you can add the following to your csproj file.

<ItemGroup>
  <PackageReference Include="Refit" Version="4.3.0" />
</ItemGroup>

The API

Instead of creating an API I searched the internet for a free one I could use. I ended up using CountryAPI. The following is a sample of what a response from the API looks like.

{  
   "IsSuccess":true,
   "UserMessage":null,
   "TechnicalMessage":null,
   "TotalCount":1,
   "Response":[  
      {  
         "Name":"Afghanistan",
         "Alpha2Code":"AF",
         "Alpha3Code":"AFG",
         "NativeName":"افغانستان",
         "Region":"Asia",
         "SubRegion":"Southern Asia",
         "Latitude":"33",
         "Longitude":"65",
         "Area":652230,
         "NumericCode":4,
         "NativeLanguage":"pus",
         "CurrencyCode":"AFN",
         "CurrencyName":"Afghan afghani",
         "CurrencySymbol":"؋",
         "Flag":"https://api.backendless.com/2F26DFBF-433C-51CC-FF56-830CEA93BF00/473FB5A9-D20E-8D3E-FF01-E93D9D780A00/files/CountryFlags/afg.svg",
         "FlagPng":"https://api.backendless.com/2F26DFBF-433C-51CC-FF56-830CEA93BF00/473FB5A9-D20E-8D3E-FF01-E93D9D780A00/files/CountryFlagsPng/afg.png"
      }]
}

Classes

Now that we know what the API response looks like classes can be created to match its structure. In this case, I have two classes one for response and one for the actual country data.

public class ApiResponse<T>
{
    public bool IsSuccess {get; set;}
    public string UserMessage {get; set;}
    public string TechnicalMessage {get; set;}
    public int TotalCount {get; set;}
    public List<T> Response {get; set;}
}

public class Country
{
    public string Name { get; set; }
    public string Alpha2Code { get; set; }
    public string Alpha3Code { get; set; }
    public string NativeName { get; set; }
    public string Region { get; set; }
    public string SubRegion { get; set; }
}

API Interface for Refit

With the classes for the response setup, we can now define the interface that will be used by Refit when calling the API. The following interface defines a function to get all countries and another function that gets countries that speak a specific native language.

public interface ICountryApi
{
    [Get("/v1/Country/getCountries")]
    Task<ApiResponse<Country>> GetCountries();
    
    [Get("/v1/Country/getCountries")]
    Task<ApiResponse<Country>> GetCountriesByLanguage([AliasAs("pNativeLanguage")]string language);
}

The attributes on the functions are part of the magic of Refit. In the cases above both of the calls are HTTP Get requests which is why they are using the Get attribute. Refit does support other verbs as well.

The other thing of note here is the AliasAs on the parameter of the second function call. This attribute can be used to control what gets put in the query string and keeps cryptic names from the API from spreading to other places in your code.

Calling the API

The following is the full code from my Program class that shows the usage of Refit with both of the API calls defined above.

public static async Task Main(string[] args)
{
    var api = RestService.For<ICountryApi>(" http://countryapi.gear.host");
    
    var countries = await api.GetCountries();
    OutputCountires(countries.Response);

    Console.WriteLine("Enter a language to filter by:");    
    var language = Console.ReadLine();
    var filteredCountries = await api.GetCountriesByLanguage(language);
    OutputCountires(filteredCountries.Response);

    Console.ReadLine();
}

private static void OutputCountires(List<Country> countries)
{
   countries.ForEach(c => Console.WriteLine($"{c.Name} - {c.Region} - {c.SubRegion}"));
}

The following line is defining a call to a rest API for a specific interface.

var api = RestService.For<ICountryApi>(" http://countryapi.gear.host");

Now that we have a reference to the API it can be called asynchronously to get the data from the API.

var countries = await api.GetCountries();

The rest of the app is more of the same just using the other API call.

Gotchas

In order to use async Task Main a change is needed to the project file to set the LangVersion. I just set it to latest, but I believe the minimum for this feature is 7.1.

<PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>netcoreapp2.0</TargetFramework>
  <LangVersion>latest</LangVersion>
</PropertyGroup>

If you are using VS Code and are using Console.ReadLine() like I am above then a change will be needed for the launch.json file found in the .vscode directory. Look for the console property and set the value to either integratedTerminal or externalTerminal otherwise, the app will be connected to the debug console which will show the output of the application, but doesn’t allow for input.

Wrapping up

Using Refit to makes using APIs super simple. There is a level of magic that I would like to dig into more. I would also like to see how it handles issues and what sort of hooks are provided to address those issue. Based on the Github page Paul has addressed a wide range of the challenges faced with dealing with an API.

As part of writing this posts, I came across two other posts on Refit that might be helpful, one by Jerrie Pelser and the other from Scott Hanselman.

 

Broadcast from an ASP.NET Web API to a UWP Client using SignalR 2

I recently had to take an existing ASP.NET Web API (not core) that was used by a UWP (Universal Windows Platform) client to pull information and modify it to receive notifications from the Web API under some situations. This post is going to cover the creation of a Web API and UWP projects and the modification of the projects to allow notification from the Web API to the UWP client.

Web API Project Creation

In Visual Studio we need to create a new project for the Web API using File > New > Project.

We will be creating an ASP.NET Web Application (.NET Framework) and I’m just naming the project Web API.

On the next screen select that we want a Web API application.

UPW Project Creation

As above in Visual Studio we need to create a new solution and project for the UWP Client using File > New > Project from a new instance of Visual Studio. Select the Blank App (Universal Windows) template.

Next, you will be prompted for the versions of Window 10 to support with the UWP application. I just took the defaults.

You may also want to set your PC into Developer mode.

Add SignalR to the Web API

Now that the initial project creation is done let’s add SignalR to the WebApi project. Inside of Visual Studio right-click on the project and select Manage NuGet Packages.  Select the Browse tab and in the search box enter SignalR and we are looking for the Microsoft.AspNet.SignalR package. Select it and click the Install button.

Configuration

Now that SignalR is installed it needs to be configured on startup of the application. To do this add a Startup class if it doesn’t exist with the following contents.

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var hubConfiguration = new HubConfiguration
        {
            EnableDetailedErrors = true,
            EnableJavaScriptProxies = false
        };
        app.MapSignalR(hubConfiguration);

    }
}

The hubConfiguration bit is only needed if you want to change the default settings. In this example, we don’t have any JavaScript clients so we don’t need those proxies and we are learning so detailed errors will come in handy.

Hub

The first thing we need to do is create a new hub which is what SignalR uses to do all of its magic. Hubs are what enable the server (and/or client depending on how you are using it) to make remote calls. Since we are just doing a broadcast the hub class is empty except for a reference to the class that will be handling when to send the broadcast. This property is very important since it will be the trigger for Broadcaster to get instantiated when a client connects.  The following is the full class.

using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;

namespace WebApi.Hubs
{
    [HubName("BroadcastHub")]
    public class BroadcastHub : Hub
    {
        private readonly Broadcaster _broadcaster = Broadcaster.Instance;
    }
}

Make note of the hub name used as we will need it on the client side.

Next, we have the class that actually does the broadcasting. The following is the full class.

using System;
using System.Threading;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;

namespace WebApi.Hubs
{
    public class Broadcaster
    {
        private readonly TimeSpan _updateInterval = 
            TimeSpan.FromMilliseconds(250);
        private Timer _timer;

        private static readonly Lazy<Broadcaster> _instance = 
            new Lazy<Broadcaster>(() => new Broadcaster(GlobalHost.ConnectionManager.GetHubContext<BroadcastHub>().Clients));
        public static Broadcaster Instance => _instance.Value;

        public IHubConnectionContext<dynamic> Clients { get; set; }

        public Broadcaster(IHubConnectionContext<dynamic> clients)
        {
            Clients = clients;
            _timer = new Timer(Broadcast, null, _updateInterval, _updateInterval);
        }

        public void Broadcast(object state)
        {
            Clients.All.Broadcast(DateTime.Now);
        }
    }
}

The above is a singleton which gets created with the list of clients for a specific hub which can be seen in the following line. It has been reformatted from above to try and make it a bit more readable.

private static readonly Lazy<Broadcaster> _instance = 
  new Lazy<Broadcaster>(() => 
             new Broadcaster(GlobalHost
                            .ConnectionManager
                            .GetHubContext<BroadcastHub>()
                            .Clients));

The constructor of the class sets up a timer that calls a Broadcast function which sends the current date and time to all the connected clients. This could be any sort of data, but for this sample, I’m keeping it simple.

Add SignalR to the UWP Client

Now over in the UWP project, we need to add the SignalR client by right-clicking on the project and selecting Manage NuGet Packages. As in the Web API instructions above search for SignalR using the Browse tab. Select the Microsoft.AspNet.SignalR.Client package and click Install.

UI

To keep the sample as simple as possible we are just going to add a TextBlock to the MainPage.xaml to display the results of the broadcast from the Web API. The following is the full code for the page.

<Page
    x:Class="UwpClient.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:UwpClient"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Name="BroadcastResults" />
    </Grid>
</Page>

Now in the code behind we need to connect to the hub on the Web API and define what happens when a broadcast is received. The following is all of the code in the code behind which will be followed up with some comments.

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        InitilizeHub();
    }

    private async void InitilizeHub()
    {
        var hubConnection = new HubConnection("http://localhost:50869");
        var hubProxy = hubConnection.CreateHubProxy("BroadcastHub");

        hubProxy
        .On<DateTime>("Broadcast",
                      async data => 
                            await Dispatcher
                                  .RunAsync(CoreDispatcherPriority.Normal,
                                            () => BroadcastResults.Text = 
                                                          data.ToString()));
        await hubConnection.Start();
    }
}

The hubConnection as you might expect controls the connection with the server and the hubProxy deals with the interaction with a specific hub. In our case, we are saying with the Broadcast function is called on the server we want the client to update the BroadcastResults.Text with the value from the server.

Wrapping Up

Run the Web API and then UWP application and you will see the date time from the server being shown on the client. This is, of course, is the simplest of use cases for SignalR, but it is a great jumping off point to go deeper.

This tutorial from the official docs helped a lot when getting my sample up and running.

The code for this sample can be found here.