Adding a Loading Spinner to a Button with Blazor

I’ve been playing with Blazor for several weeks now, and it’s fantastic.  For the uninitiated, Blazor allows you to write .NET code that runs in the browser through the power of WebAssembly (Wasm).  This means that the amount of JavaScript you end up having to write to create a dynamic web UI is significantly reduced, or even eliminated (check out this Tetris clone  @mistermag00 wrote running completely on Blazor with ZERO JavaScript).

Today my objective is much less ambitious than attempting to recreate a classic 1980s-era video game in the browser:  I just want to change the state of a search button to indicate to the user that something is happening while we retrieve their results.  Here’s what we’re shooting for:

Let’s start with the Razor view code:

@if (IsSearching)
{
    <button class="btn btn-primary float-right search disabled" disabled><span class='fa-left fas fa-sync-alt spinning'></span>Searching...</button>
}
else
{
    <button class="btn btn-primary btn-outline-primary float-right search" onclick="@OnSearchAsync">Search</button>
}

Here we have two versions of the button, and we determine which one to display based on an IsSearching boolean property in the Blazor component code.

The first button represents the state of the button while we’re searching.  We set the visual state to disabled with the disabled CSS class and disable clicks with the disabled attribute.  Both buttons have a custom search CSS class which just sets the width of the button so we don’t have a jarring width change when transitioning between states.  I’m using a Font Awesome icon for my spinning icon (so don’t forget the link to their CSS in your HEAD), and animating it with a couple of custom CCS classes that we’ll look at in a minute.

The second button represents the state of the button when we’re not searching.  It has the onclick handler calling into the OnSearchAsync method in my Blazor component code.

Speaking of my Blazor component code, let’s check it out…

public class SearchCriteriaBase : BlazorComponent
{
    protected bool IsSearching { get; set; }

    public async Task OnSearchAsync()
    {
        IsSearching = true;
        StateHasChanged();

        if (CheckIsValid())
        {
            // Make my long-running web service call and do something with the results
        }

        IsSearching = false;
        StateHasChanged();
    }
}

All that’s needed is a simple IsSearching property that  I can set to switch between button states while searching.  I just need to remember to call StateHasChanged() to let Blazor know it needs to update the DOM.

And finally, here’s the custom CSS to make the animation happen:

.fas.spinning {
    animation: spin 1s infinite linear;
    -webkit-animation: spin2 1s infinite linear;
}

@keyframes spin {
    from {
        transform: scale(1) rotate(0deg);
    }

    to {
        transform: scale(1) rotate(360deg);
    }
}

@-webkit-keyframes spin2 {
    from {
        -webkit-transform: rotate(0deg);
    }

    to {
        -webkit-transform: rotate(360deg);
    }
}

.fa-left {
    margin-right: 7px;
}

.btn.search {
    width: 10rem;
}

And that’s it. A simple button state transition with no Javascript, just the way I like it!

Testing Stripe Webhooks in an ASP.NET Core Project

I’m using Stripe for subscription management and payment processing for the the SaaS side project I’m currently working on.  Stripe offers webhook functionality that allows you to register callback endpoints on your API with Stripe that they will call whenever any one of numerous specified events occur on their side.

So, for example, in my SaaS I’m offering customers a free 30-day trial period at the beginning of their subscription before they have to provide credit card information.  I can register for the customer.subscription.trial_will_end event with an endpoint of my choice on my API, and Stripe will call that endpoint three days before the end of any of my customers’ trial periods with details about the given customer and their subscription.  I’ll have logic on my side to check to see if we have a credit card for that customer yet, and, if not, send them a friendly e-mail reminding them that their trial is about to expire and they need to enter a credit card if they’d like to continue to use the service.

Stripe offers the ability through their dashboard to send test events to a webhook endpoint.  As I worked on my integration this past week, I ran into a couple of small issues in getting the test events to reach my service running locally on my machine.  So here’s a quick summary of what it took to get the messages flowing.

StripeEventsController

First we need a controller with an endpoint that Stripe will call into.  I’m using a single endpoint to catch all events.  It then delegates the processing of events to an IStripeEventProcessor.  Here’s my endpoint:

[code lang=”csharp”]

[Route("api/[controller]")]
public class StripeEventsController : Controller
{
private readonly IStripeEventProcessor _stripeEventProcessor;
private readonly IEnvironmentSettings _environmentSettings;

public StripeEventsController(
IStripeEventProcessor stripeEventProcessor,
IEnvironmentSettings environmentSettings)
{
_stripeEventProcessor = stripeEventProcessor ?? throw new ArgumentNullException(nameof(stripeEventProcessor));
_environmentSettings = environmentSettings ?? throw new ArgumentNullException(nameof(environmentSettings));
}

[HttpPost]
public async Task<IActionResult> IndexAsync()
{
var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();

try
{
var stripeEvent = StripeEventUtility.ConstructEvent(json,
Request.Headers["Stripe-Signature"], _environmentSettings.StripeConfiguration.Value.WebhookSigningSecret);

await _stripeEventProcessor.ProcessAsync(stripeEvent);

return Ok();
}
catch (StripeException)
{
return BadRequest();
}
}
}

[/code]

Note that I’m passing a WebhookSigningSecret to StripeEventUtility.ConstructEvent(..); this verifies that the event was actually sent by Stripe by checking the “Stripe-Signature” header value.  The webhook signing secret can be obtained from Stripe > Developers > Webhooks > select endpoint > Signing secret.

Turn Off HTTPS Redirection in Development

I’m using HTTPS Redirection in my project to redirect any non-secure requests to their corresponding HTTPS endpoint.  This caused me to receive an error whenever sending a test event: “Test webhook error: 307.”

Stripe expects to receive a 200-series status code back on all webhook calls, so the 307 Temporary Redirect status was a problem.  To resolve this, I modified my Startup.cs to only use HTTPS Redirection when not in development mode, like so:

[code lang=”csharp”]
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
// snip…
}
else
{
// snip…
app.UseHttpsRedirection(); // <- Moved from outside to inside else block to allow ngrok tunneling for testing Stripe webhooks
}
// snip…
app.UseMvc();
}
[/code]

ngrok

In order for Stripe to send test webhook events to our service, it needs to be able to connect to it.  ngrok is a neat little utility that allows you to expose your locally running web app or service via a public URL.  Download ngrok and install it, following the four getting started steps here.

When we’re ready to test, we’ll start up ngrok with the following command (where 64768 is the port number of your service):

<span class="pln">ngrok http 64768</span> <span class="pun">-</span><span class="pln">host</span><span class="pun">-</span><span class="pln">header</span><span class="pun">=</span><span class="str">"localhost:64768"</span>

It’s important to note that my service is configured to be accessible via the following bindings:

[code]
<binding protocol="http" bindingInformation="*:64768:localhost" />
<binding protocol="https" bindingInformation="*:44358:localhost" />
[/code]

You want to specify the non-secure (non-HTTPS) port when starting up ngrok.  It’s also important to specify the host-header flag; if you don’t, you’ll get a 400 Bad Request on all of your test calls:

Upon starting ngrok, you’ll see a screen like the following, indicating (in my case) that it is forwarding https://96c1bf3b.ngrok.io to my localhost:64768: 

Configure Stripe Webhook Endpoint and Test

Finally, you need to set up a Stripe webhook that points to your Stripe event handler endpoint exposed publicly via ngrok.  This is done by navigating to the Stripe dashboard > Webhooks, and clicking “Add endpoint”.  In my case, my endpoint looks like this:

Now we test our endpoint by clicking “Send test webhook.”  If all goes as planned, you’ll see a successful response like the following:

You can also fire up http://localhost:4040/inspect/http in your browser and see a nice dashboard where you can inspect and replay all requests made through the ngrok public endpoint:

Congratulations!  With just a few steps you’re now successfully sending test Stripe webhook events to your service running locally.

Configuring AutoMapper for use with the ASP.NET Core DI Container

I’m generally a strong proponent of using separate models for commmunicating across service boundaries that are separate and distinct from my core domain model. In a typical web application that is backed by a database, this usually means that I have a domain model, a storage model, and a view model, and I map between storage model ↔ domain model and between domain model ↔ view model when crossing their respective boundaries. This keeps my domain model isolated, ensures that it is not forced to change due to concerns in another layer, and promotes adherence to the Single Responsibility Principle (a class should have one, and only one, reason to change).

Having multiple models implies that there is some mechanism for mapping between models. My go-to tool for this task is AutoMapper. Configuring AutoMapper for use in an ASP.NET Core project using the ASP.NET Core DI container differs slightly from my prior experiences with it on past projects. Here’s what I did to get up and running…

Install Nuget Packages

Let’s first install the Nuget packages that we’ll need.  I’m using AutoMapper 7.0.1 and AutoMapper.Extensions.Microsoft.DependencyInjection 5.0.1, which provides AutoMapper extensions on IServiceCollection that allow us to wire-up AutoMapper in Startup.ConfigureServices(IServiceCollection services).

Create Models

Suppose we have a Stock domain model class that looks like this:

public class Stock
{
    private readonly IStockRepository _repository;

    public Stock(IStockRepository repo)
    {
        _repository = repo ?? throw new ArgumentNullException(nameof(repo));
    }

    public string Name { get; set; }

    public string Symbol { get; set; }
}

Note that we’re injecting an IStockRepository.  (I often have the aggregate roots in my domain models take a reference to their corresponding repository so I can do neat things like stock.SaveAsync(), but that’s another topic…)

For our purposes here it doesn’t really matter what IStockRepository and StockRepository look like – we’ll just leave them empty:

public interface IStockRepository
{
}

public class StockRepository : IStockRepository
{
}

We have a StockViewModel class that roughly corresponds with our Stock domain model and looks like this:

public class StockViewModel
{
    public string Name { get; set; }

    public string Symbol { get; set; }
}

We’d also probably have a Stock storage model class and perhaps other corresponding view model representations of a stock, but we’ll keep it to just these two classes to keep things simple.

Create Mapping Profile

Next we’ll create our mapping profile, which is where we’ll configure our specific type mappings.  Usually I’ll create a profile per bounded context per layer I’m mapping.  So, for example, if I have a single bounded context but I map between storage model ↔ domain model and between domain model ↔ view model, I’ll have a ViewModelMappingProfile and a StorageModelMappingProfile.

Our ViewModelMappingProfile in this case looks like this:

public class ViewModelMappingProfile : Profile
{
    public ViewModelMappingProfile()
    {           
        CreateMap&lt;StockViewModel, Stock&gt;().ConstructUsingServiceLocator();
    }
}

It derives from AutoMapper.Profile.  I’ve just got a single, simple mapping from StockViewModel to Stock, and I’ve called ConstructUsingServiceLocator() on the mapping to enable the resolution and injection of dependencies into the target.  Comprehensive documentation on configuring individual mappings can be found here.

Configure Services

Next we configure our services and add AutoMapper to the service collection.   In Startup.ConfigureServices(IServiceCollection services)we add the following after services.AddMvc():

services.AddAutoMapper();

The line above does a couple of things: it registers IMapper with our DI container, and it searches our assembly for classes that inherit from AutoMapper.Profileand automatically loads them.  So our ViewModelMappingProfile gets loaded automatically at startup, and we can confirm that by putting a breakpoint in its constructor.

We also must register with the DI container any dependencies that will be injected in at map time (like our IStockRepository) as well as any type mappings that are configured with ConstructUsingServiceLocator()(like our Stock domain class).  This part is important when working with the ASP.NET Core DI container – other third-party containers that I’ve worked with in the past (such as Unity) will resolve concrete types automatically without requiring an explicit type mapping.  Not so with the ASP.NET Core DI container, and I spent a bit of time figuring this out.  Since we define the mapping to Stock with ConstructUsingServiceLocator(), it must be explicitly registered with the container.

So, we have the following somewhere in our Startup.ConfigureServices(IServiceCollection services) method:

services.AddScoped<IStockRepository, StockRepository>();
services.AddTransient<Stock, Stock>();

Map!

With our configuration complete, all that’s left to do is start using our mappings.  We can inject an instance of IMapper into any of our classes that are resolved using the DI container.

Here’s an example usage in an OnPostAsync() method on a Razor page model that maps from a posted view model to the domain model and then saves the data:

public async Task<IActionResult> OnPostAsync(CancellationToken cancellationToken)
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    var stock = _mapper.Map<StockViewModel, Stock>(Stock);
    await stock.SaveAsync(cancellationToken);

    return RedirectToPage($"./Stock?id={stock.Id}");
}

Wrapping Up

We’ve shown how to configure and start using AutoMapper with the ASP.NET Core DI container, for a quick and easy way to get model mapping with DI working in your ASP.NET Core project.

It’s worth noting, though, that while the core DI container may be adequate for small, simple projects, it’s not as feature-rich as most other third-party DI containers.  More complex projects might require a more robust and capable DI container to meet their specific needs.  (This post on Stack Overflow gives a nice overview of some of its shortcomings.)

Adding Simple Pagination to a Bootstrap Table in ASP.NET Core

I recently needed to add simple pagination to a Bootstrap table on a page in my ASP.NET Core project. I’m using MVC Razor Pages, but a similar approach will work with Razor Views.

My data set contains 270+ rows, and originally I was returning all 270+ rows to the client and displaying them in a table that scrolled well past the end of the page. I only want to show ten at a time with a simple paging control. Here’s what the final product will look like:

The following is a standard Bootstrap table generated from items in Model.Stocks from our page model:

<table class="table table-striped table-bordered table-sm table-responsive">
<thead>
    <tr>
        <th scope="col">Symbol</th>
        <th scope="col">Name</th>
        <th scope="col">Sector</th>
        <th scope="col">Price</th> 
    </tr> 
    </thead> 
    <tbody> 
        @foreach (var item in Model.Stocks) 
        { 
            <tr> 
                <th scope="row">@item.Symbol</th> 
                <td>@item.Name</td> 
                <td>@item.Sector</td> 
                <td>@item.Price</td> 
            </tr> 
        } 
    </tbody> 
</table>

Here is the page model:

public class DividendPicksModel : PageModel 
{ 
    private readonly IStockService _stockService;
    
    public DividendPicksModel(IStockService stockService)
    {
        _stockService = stockService ?? throw new ArgumentNullException(nameof(stockService));
    }
    
    public PaginatedList<StockViewModel> Stocks { get; private set; }
    
    public async Task<IActionResult> OnGetAsync(int? pageIndex, CancellationToken cancellation)
    {
        var stocks = await _stockService.GetCachedStockResources(cancellation) ?? new List<Stock>();
        Stocks = PaginatedList<StockViewModel>.Create(stocks.Select(s => new StockViewModel(s)), pageIndex ?? 1, 10, 5);
        return Page();
    }   
}

The page implements public async Task<IActionResult> OnGetAsync(int? pageIndex, CancellationToken cancellation)

This is responsible for handling GET requests for the page. There’s not too much to it: retrieve the data from a domain service, set the Stocks collection property on the page model, and return the page.

Note that the method takes int? pageIndex as a parameter which indicates which page of the table is being requested by the client. Not passing this parameter will cause the the first page of the table to be returned.

The interesting thing to note here is that the Stocks property is not a standard .NET collection. It’s a PaginatedList<Stock>. PaginatedList<T> derives from List<T> and represents a “page” of our data, and it exposes the properties needed to enable paging functionality for the collection on the view. Let’s take a look at it:

public class PaginatedList<T> : List<T> 
{ 
    private PaginatedList(
        List<T> items, int count, int pageIndex, int pageSize, int countOfPageIndexesToDisplay) 
    { 
        PageIndex = pageIndex; 
        TotalPages = (int)Math.Ceiling(count / (double)pageSize);

        SetPageIndexesToDisplay(pageIndex, countOfPageIndexesToDisplay); AddRange(items); 
    }

    public int PageIndex { get; }
    
    public int TotalPages { get; }
    
    public bool HasPreviousPage => (PageIndex > 1);
    
    public bool HasNextPage => (PageIndex < TotalPages);
    
    public List<PageIndex> PageIndexesToDisplay { get; private set; }
    
    public static PaginatedList<T> Create(
        IEnumerable<T> source, int pageIndex, int pageSize, int countOfPageIndexesToDisplay = 3)
    {
        var list = source.ToList();
        var count = list.Count;
        var items = list.Skip((pageIndex - 1) * pageSize)
                        .Take(pageSize).ToList();

        return new PaginatedList<T>(items, count, pageIndex, pageSize, countOfPageIndexesToDisplay);
    }
    
    private void SetPageIndexesToDisplay(int pageIndex, int countOfPageIndexesToDisplay)
    {
        PageIndexesToDisplay = new List<PageIndex>();  
  
        if (pageIndex > TotalPages - countOfPageIndexesToDisplay + Math.Floor(countOfPageIndexesToDisplay / 2.0m))
        {
            for (var i = Math.Max(TotalPages - countOfPageIndexesToDisplay + 1, 1); i <= TotalPages; i++)
            {
                PageIndexesToDisplay.Add(new PageIndex(i, i == pageIndex));
            }
        }
        else if (pageIndex < (countOfPageIndexesToDisplay + 1) - Math.Floor(countOfPageIndexesToDisplay / 2.0m))
        {
            for (var i = 1; i <= Math.Min(countOfPageIndexesToDisplay, TotalPages); i++)
            {
                PageIndexesToDisplay.Add(new PageIndex(i, i == pageIndex));
            }
        }
        else
        {
            var startIndex = pageIndex - (int)Math.Floor(countOfPageIndexesToDisplay / 2.0m);
            for (var i = startIndex; i <= startIndex + countOfPageIndexesToDisplay - 1; i++)
            {
                PageIndexesToDisplay.Add(new PageIndex(i, i == pageIndex));
            }
        }
    }  
} 

We create a PaginatedList by calling into the static Create method and passing it the collection that will be paged, the index of the page we need to display, the page size, and the number of page indexes we want to display in the pagination control at a time (in the screenshot at the beginning of the post it’s five).

PaginatedList exposes several properties about the page of data that it represents:

  • PageIndex
  • TotalPages
  • HasPreviousPage
  • HasNextPage
  • PageIndexesToDisplay

… and, of course, since it derives from List<T>, we have access to the list of items on our page.

The most complex part of PaginatedList<T> is SetPageIndexesToDisplay(..), which builds the collection of page indices to display in the Bootstrap pagination control based on the current page index and the total count of indices to display. PageIndex is a simple POCO that contains the index number and a boolean indicating whether that page index is the active (displayed page:

public class PageIndex 
{ 
    public PageIndex(int index, bool isActive) 
    { 
        Index = index; 
        IsActive = isActive; 
    }

    public int Index { get; }
    
    public bool IsActive { get; }
    

} 

The final piece to pull this all together is the actual pagination control on the view. Immediately below the table in our HTML we have the following:

@{
    var prevDisabled = !Model.Stocks.HasPreviousPage ? "disabled" : "";
    var nextDisabled = !Model.Stocks.HasNextPage ? "disabled" : "";
}
<nav aria-label="Pagination" class="col-12">
    <ul class="pagination justify-content-end">
        <li class="page-item @prevDisabled">
            <a class="page-link" 
                asp-page="./MyPage" 
                asp-route-pageIndex="@(Model.Stocks.PageIndex - 1)"
                aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
                <span class="sr-only">Previous</span>
            </a>
        </li>
        @foreach (var i in Model.Stocks.PageIndexesToDisplay)
        {
            var activeClass = i.IsActive ? "active" : "";
            
            <li class="page-item @activeClass"><a class="page-link"  
                                        asp-page="./DividendPicks"
                                        asp-route-pageIndex="@(i.Index)">@i.Index</a></li>
        }
        <li class="page-item @nextDisabled">
            <a class="page-link" 
                asp-page="./MyPage"
                asp-route-pageIndex="@(Model.Stocks.PageIndex + 1)"
                aria-label="Next">
                <span aria-hidden="true">&raquo;</span>
                <span class="sr-only">Next</span>
            </a>
        </li>
    </ul>
</nav>

Here we use the properties exposed by our PaginatedList<Stock> to control how the pagination control is rendered and functions. We enable/disable the previous/next buttons based on the HasPreviousPage and HasNextPage properties, respectively. We build the numeric page index buttons from the PageIndexesToDisplay collection, and we set the link appropriately for the given index.

Clicking one of the links in the pagination control requests the page again passing the given page index and the PaginatedList<Stock> is rebuilt for the requested page.

Wrapping Up

We’ve explored here a simple, no-frills way to add pagination to a Bootstrap table. There are a couple of improvements that could be made:

  • Interacting with the pagination control causes a refresh of the entire page. This could be modified to execute a little Javascript to hit an endpoint that returns the new page of data and update the table and pagination control in-place without reloading the whole page.
  • This implementation retrieves the entire data set from the service and then applies paging within our app service. This is fine for my purposes since my dataset is small, but for larger datasets I would push the execution of the paging (skip/take operators) to the database. (If using Entity Framework, this could be as simple as deferring query execution until after the Skip and Take are applied in PaginatedList<T>.Create(..).)

I may consider these enhancements in the future, but for now this implementation suits my needs just fine.

Switching Your Website to Use HTTPS, the Free and Easy Way

Google has announced that with their July 2018 release of Chrome 68 they’ll start marking all non-HTTPS websites as “not secure.”  Thankfully, these days there are several cheap, and even free, options for securing your site’s traffic.  There’s really no reason not to do it.

One such option is to use CloudFlare’s free SSL.  Security expert Troy Hunt has a fantastic 4-part video series called Https Is Easy! that takes you through the simple steps to secure your site.

Fixing Mixed Content Issues on WordPress Sites

While I was able to get a couple of my websites switched to HTTPS with no issues in a matter of minutes, I ran into issues with one of my Azure-hosted WordPress sites.  Much of the site’s functionality broke in the switch and I was now getting a host of mixed content errors, particularly with plugins that I was using, like the following:

The fix for this is simple: install the SSL Insecure Content Fixer plugin.

Once installed, open the Settings for the plugin.  I was able to leave all of the settings at their default except for HTTPS detection.  Change this setting to use HTTP_X_FORWARDED_PROTO (e.g. load balancer, reverse proxy, NginX), as this is the header that CloudFlare uses.

After applying this change, my mixed content errors went away, my site was secured, and I was good to go.

Side Project

It’s been awhile since I’ve done any serious side-project work.  I’ve not had the energy or time to work on a personal project since I started working for my current employer almost three years ago, and, honestly, I haven’t felt much of a need.

When I joined my current employer three years ago, any side projects that I had in flight stopped dead in their tracks because – after years of consulting for the state – I was all of sudden working on interesting problems, being challenged daily, playing with cool new technologies, and building something new and innovative.  The needs I had satisfied previously after hours working on side projects (i.e., the desires to create, innovate, and learn new things) were now being satisfied at work.

I’ve got a bit more free time these days, and while I’m still being challenged at work, I’ve been looking for a project that I can spend some free time on with a few goals in mind:

  • Sharpen my skills on familiar (to me) tech
  • Work on new problems
  • Create something neat (and perhaps profitable)
  • Learn some new (to me) tech when appropriate

I’ve got some ideas for my next project(s).  I’m not ready to reveal what they are yet, but here’s a peek at just a few of the things I intend to use/work with in the coming months:

  • Natural language processing
  • Machine learning
  • Azure Service Fabric
  • ASP.Net Core
  • Some sort of API management to support monetization
  • Xamarin.Forms to target iOS/Android/Windows

So why am I sharing this publicly?  A few reasons…

  1. I wish I would have kept a journal of projects that I’ve worked on in the past to serve as my own personal reference for problems I’ve encountered and lessons learned.  This will be that journal.
  2. Communicating what I’m working on helps to sharpen my thoughts and aid in my understanding and retention.
  3. Sharing my progress will help to keep me accountable when my motivation wanes or my progress slows.
  4. It may be helpful to someone out there.  I hope it is.

I will work on this as time and energy permits.  I hope to post somewhat regularly and frequently, but I make no promises.

That said, thank you for joining me on this ride!

– Jon