Razor Pages

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.

New Razor Pages Project Backed with an API Read More Ā»

ASP.NET Core Basics: Razor Pages

As part of the ASP.NET Core 2.0 release, a new feature called Razor Pages was added to better handle page-focused scenarios. This post is going to cover adding usage of Razor Pages to an existing ASP.NET Core MVC application. This is part of the ASP.NET Core Basics series and the repo before any changes for this post can be found here. All the changes will be in the Contacts project if you are using the sample repo.

Add the end of this post the Razor Pages example will have the same level of functionality as the existing Razor example in the project.

Setup

To follow the conventions of a new Razor Pages project add aĀ PagesĀ directory to the root of the project. As another level of grouping add a ContactsRazorPagesĀ directory under theĀ PagesĀ directory. The strange name of the directory is just to make it super clear this is different than the existing Contact List already in the application.

Scaffolding

Razor Pages supports the same level of scaffolding as the existing Razor/MVC setup we are all used to in Visual Studio. To start, right-click on theĀ ContactsRazorPagesĀ directory and select Add > Razor Page.

This will open the Add Scaffold dialog. We are using entity framework and want to generate a full set of CRUD operations so select Razor Pages using Entity Framework (CRUD) and click Add.

Next, we have to select the Model and DB Context. We want to use the same model and context as our existing Razor/MVC setup so we are going to select the ContactĀ class and the ContactsContextĀ DB Context. Finally, since this is part of an existing application make sure and check the Use a layout page and use the existingĀ _Layout.cshtmlĀ page used by the rest of the application. Click the Add button and hang out while the magic happens.

After a couple of minutes, the directory will be filled with a set of pages to do contact CRUD operations. Since this is a page-centric method of working all the files you need are in this one directory. No separateĀ controller and views as in traditional MVC. By default, routing is handled via the folder structure.

View Imports

I had to aĀ _ViewImports.cshtmlĀ file to theĀ PagesĀ to get things working. I tried adding the needed imports to the existing import, but it didn’t work for some reason. Here are the contentsĀ of the file.

@using RazorPages
@namespace RazorPages.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Add to the Navigation Menu

Our last step is going to be to add the new contact list to the site’s navigation menu. Open theĀ _Layout.cshtmlĀ file in theĀ Views/SharedĀ directory and a link to the new contact list.

Existing MVC based contact list:
<li><a asp-area="" asp-controller="Contacts" asp-action="Index">Contact List</a></li>

New Razor Pages based contact list:
<li><a asp-page="/ContactsRazorPages/Index">Contact List (Razor Pages)</a></li>

Notice that the link uses theĀ asp-pageĀ tag helper instead ofĀ asp-controller.

Wrapping Up

The above covers adding Razor Pages to an existing MVC application and rounds out the all the major UI options for the ASP.NET Basics series. Keep in mind that Razor Pages functionality is fully on par with tradition MVC and has been recommended by Microsoft as the way to do when page-centric work. If you haven’t check out the getting started docs to get a better feel for how to use pages.

The completed code can be found here.

ASP.NET Core Basics: Razor Pages Read More Ā»