Tutorial: Get started with RiotBlossom using ASP.NET Core and Visual Studio

This razor-sharp tutorial will show you how to install RiotBlossom and make a request to the Riot Games API using ASP.NET Core and Visual Studio!

You will learn how to:

  • Create an ASP.NET Core Web API project
  • Setup RiotBlossom
  • Configuring services with dependency injection
  • Edit a Razor page
  • Fetching data from summoner-v4
  • Run the app

Prerequisites

Create an ASP.NET Core Web API project

Open Visual Studio 2022 and select Create a new project:

create-new

You will be given project templates to start from. Select the ASP.NET Core Web App project template:

web-app

Select .NET 6 or higher for the Framework option and press Create:

additional

Name the project my-riotblossom-app and press Next!

configure

The ASP.NET Core project template will be created with the necessary files needed for a web application! :3

Setup RiotBlossom

We will first need to add RiotBlossom to the project Dependencies.

Under Solution Explorer on the right-hand side, right-click on the Dependencies folder icon and select Manage NuGet Packages.

In the NuGet Package Manager window, search for BlossomiShymae.RiotBlossom and install the latest version:

install

With the package installed as a dependency, we should now be able to proceed with configuring the services needed!

Configuring services with dependency injection

Modify and save the following code below to Program.cs:

using BlossomiShymae.RiotBlossom.Core;
using BlossomiShymae.RiotBlossom.Middleware;
using BlossomiShymae.RiotBlossom.Type;
using my_riotblossom_app.Converters;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();

// ADD the following lines below
builder.Services.AddHttpClient();
builder.Services.AddSingleton<IRiotBlossomClient>(p =>
{
    IHttpClientFactory factory = p.GetRequiredService<IHttpClientFactory>();
    HttpClient client = factory.CreateClient();
    client.Timeout = TimeSpan.FromSeconds(5);
    string key = Environment.GetEnvironmentVariable("RIOT_API_KEY")
        ?? throw new NullReferenceException("RIOT_API_KEY is not set!");
    return RiotBlossomCore.CreateClientBuilder()
        .AddHttpClient(client)
        .AddRiotApiKey(key)
        .AddRiotMiddlewareStack(new MiddlewareStack(true, "rb-riot-cache"))
        .AddDataMiddlewareStack(new MiddlewareStack(false, "rb-data-cache"))
        .Build();
});
// END

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

The following line below is used to add a RiotBlossom instance of the type IRiotBlossomClient to IServiceCollection. We used AddSingleton since we need only one instance throughout the lifetime of our web application.

builder.services.AddSingleton<IRiotBlossomClient>(p => {});

What we are doing is registering an instance of IRiotBlossomClient as a service for dependency injection. We can inject/pass this service through the constructor of anything hooked to IServiceCollection e.g. controllers, Razor pages, services, and more!

That should be it with configuring services with dependency injection!

Edit a Razor page

For Page/Index.cshtml, modify and save the code below:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

<form action="/summoner"  method="get" class="row g-3 align-items-center justify-content-center">
    <div class="col-auto">
        <input type="search" name="summonerName" />
    </div>
    <div class="col-auto">
        <button type="submit" class="btn btn-primary">Search</button>
    </div>
    <div class="d-flex flex-wrap justify-content-between align-content-around">
        @foreach (PlatformViewModel platform in Model.Platforms)
        {
            <input type="radio" class="btn-check" name="platformId" id="@platform.Id" value="@platform.Id" autocomplete="off" @(platform.Id.Equals("na1") ? "checked" : string.Empty) />
            <label class="btn btn-outline-secondary mb-1" for="@platform.Id"> @platform.Abbreviation </label>
        }
    </div>
</form>

When pressing the Search button, the form will totes make a web request to /summoner with query parameter values for summonerName and platformId!

So something like this:

'GET /summoner?summonerName=uwuie%20time&platformId=na1 HTTP/1.1'

For Page/Index.cshtml.cs, modify and save the code below:

using BlossomiShymae.RiotBlossom.Core;
using BlossomiShymae.RiotBlossom.Extensions;
using BlossomiShymae.RiotBlossom.Type;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace my_riotblossom_app.Pages
{
    public record PlatformViewModel
    {
        public string Abbreviation { get; set; } = default!;
        public string Id { get; set; } = default!;
    }

    public class IndexModel : PageModel
    {
        private readonly ILogger<IndexModel> _logger;
        public List<PlatformViewModel> Platforms { get; }

        public IndexModel(ILogger<IndexModel> logger)
        {
            _logger = logger;
            Platforms = Enum.GetValues(typeof(Platform))
                .Cast<Platform>()
                .Select(p => new PlatformViewModel
                {
                    Abbreviation = p.GetAbbreviation(),
                    Id = p.GetId()
                })
                .ToList();
        }

        public void OnGet()
        {
        }
    }
}

Now we need to set up a Summoner page that receives our form GET submission for /summoner!

Fetching data from summoner-v4

To set up a summoner page for receiving form data, we will need a new Razor page.

To create a new one, right-click on Pages and Add > Razor Page.

For the Select Scaffolded Item window, select Add Razor Page - Empty and continue.

Name the page Summoner.cshtml and click Add:

add-razor

Now modify and save the following code for Summoner.cshtml and Summoner.cshtml.cs:

@page
@model my_riotblossom_app.Pages.SummonerModel
@{
    ViewData["Title"] = Model.ViewModel.Name;
}

<div class="card mb-3" style="max-width: 320px;">
    <div class="row g-0">
        <div class="col-auto">
            <div class="card border-0">
                <img src="@Model.ViewModel.ProfileIconUrl" class="img-fluid" style="max-width: 100px;" />
                <div class="card-img-overlay p-1 d-flex align-items-end justify-content-center">
                    <span class="card-text badge rounded-pill bg-dark text-light">@Model.ViewModel.Level</span>
                </div>
            </div>
        </div>
        <div class="col-auto">
            <div class="card-body">
                <h5 class="card-title">@Model.ViewModel.Name</h5>
                <span class="badge rounded-pill bg-secondary text-light">@Model.ViewModel.Platform</span> 
            </div>
        </div>
    </div>
</div>
using BlossomiShymae.RiotBlossom.Api;
using BlossomiShymae.RiotBlossom.Api.Riot;
using BlossomiShymae.RiotBlossom.Core;
using BlossomiShymae.RiotBlossom.Extensions;
using BlossomiShymae.RiotBlossom.Type;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace my_riotblossom_app.Pages
{
    public record SummonerViewModel
    {
        public string Name { get; set; } = default!;
        public string ProfileIconUrl { get; set; } = default!;
        public long Level { get; set; }
        public string Platform { get; set; } = default!;
    }

    public class SummonerModel : PageModel
    {
        private readonly ILogger<SummonerModel> _logger;
        private readonly ISummonerApi _summonerApi;
        private readonly IDataDragonApi _dataDragonApi;
        public SummonerViewModel ViewModel { get; set; } = new();

        public SummonerModel(ILogger<SummonerModel> logger, IRiotBlossomClient client)
        {
            _logger = logger;
            _summonerApi = client.Riot.Summoner;
            _dataDragonApi = client.DataDragon;
        }

        [BindProperty(SupportsGet = true)]
        public string SummonerName { get; set; } = default!;
        [BindProperty(SupportsGet = true)]
        public string PlatformId { get; set; } = default!;

        public async Task<IActionResult> OnGetAsync()
        {
            if (!ModelState.IsValid)
                return Redirect("/");

            try
            {
                var platform = PlatformMapper.FromId(PlatformId);
                var summoner = await _summonerApi.GetByNameAsync(PlatformMapper.FromId(PlatformId), SummonerName);
                var version = await _dataDragonApi.GetLatestVersionAsync();
                _logger.LogInformation("Received summoner, {summoner}", summoner);
                ViewModel = new()
                {
                    Name = summoner.Name,
                    Level = summoner.SummonerLevel,
                    ProfileIconUrl = $"https://ddragon.leagueoflegends.com/cdn/{version}/img/profileicon/{summoner.ProfileIconId}.png",
                    Platform = platform.GetAbbreviation()
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Request failed for summoner, {tuple}", (SummonerName, PlatformId));
                return StatusCode(500);
            }

            return Page();
        }
    }
}

Whew. Now that we are able to fetch a summoner, let us run the app we made! <3

Run the app

Go ahead and press the Start Debugging button or press F5 to run the server app.

The following web page should be displayed:

index

Test the summoner search form by typing in uwuie time with the region set to NA and pressing enter or clicking on search.

We should be able to get a minimal functioning summoner page similar to the one below!

summoner

Note

This web application is far from perfect and can be improved! This for example:
/summoner?summonerName=uwuie%20time&platformId=na1
Could have used route parameters instead:
/summoner/na1/uwuie%20time

Yay! If you made it this far, thank you very much for completing this tutorial!

You just learned how to get started with RiotBlossom in an ASP.NET Core web application!

lux