Share via



September 2018

Volume 33 Number 9

[Web Development]

C# in the Browser with Blazor

By Jonathan Miller

Blazor is the new Microsoft experimental framework that brings C# into any browser without a plug-in. It holds the promise of modern single-page applications, combined with the ability to use C# and its vast base-class library. Blazor takes C# development to a new level. It’s the final piece necessary to make the language a full-stack development tool. It will have all the power of the popular JavaScript frameworks, but based on the familiar languages, APIs and tooling of the Microsoft .NET Framework.

If you come from a traditional Microsoft background and are familiar with ASP.NET Web Forms or Model-View-Controller (MVC), getting up to speed on Blazor is surprisingly easy, especially when compared to the mountain a Microsoft developer must climb to gain the equivalent knowledge in a JavaScript framework such as Angular or React.

It’s important to understand that Blazor runs completely inside the browser. Once a Blazor app is compiled, it’s essentially a set of files that gets loaded into the browser and runs. Unlike traditional ASP.NET applications, there’s no need for anything special on the back end to serve it. A Blazor site can be served by any Web server on any platform. As for clients, any browser that supports the WebAssembly standard supports Blazor. That includes all the major browsers shipping today.

When running in the browser, an application isn’t really all that useful without access to external data and services. Just like standard JavaScript single-page applications, Blazor apps access Web services using HTTP REST APIs. Those APIs can be created using Microsoft tools, such as Web API, or any technology that can present an HTTP REST endpoint. In this article, I’m going to demonstrate the ability of a Blazor app to call freely available Web services on the Web.

Getting Set Up

The first step is getting the current build of Blazor installed. As of this writing, Blazor is still an unsupported experimental framework. That means you shouldn’t use it in production. The Blazor install is very low-impact, but you may not want to install it on your everyday work machine.  Consider installing it on another machine or a virtual environment. The main requirements are Visual Studio 2017 with ASP.NET and Web development workload installed, the .NET Core SDK, and the Blazor Language extension. Please review the “Get Started” steps on blazor.net. The Blazor team is advancing quite rapidly, and sometimes you need specific versions of Visual Studio or .NET Core to use the current experimental version.

Creating a New Blazor App

I’m going to start by creating the sample Blazor application and then modify it to call some Web services. First, let’s create a new ASP.NET Core Web Application in Visual Studio.

Next, choose a Blazor application and click OK. If you don’t see Blazor in the list of choices, you may be missing the Blazor Language Services Extension.

The default Blazor application that’s created is far from an empty canvas. It includes a basic Web site that utilizes Bootstrap. There are a few sample pages that make it easy to get up and running and start experimenting with Blazor right away. The Fetch data tab displays some dummy weather data from a JSON file embedded into the default application.

How Is This Even Possible?

The concept of C# running in a browser has been a dream since the inception of .NET Silverlight. It worked very well for line-of-business applications, but the fact that it required a plug-in and the emerging iOS model didn’t allow browser plug-ins severely limited the future of Silverlight.

The magic that makes this all possible is a new standard called WebAssembly (WASM). WASM is a binary format that can be loaded and run directly in the browser and is currently supported by all the major browsers.  The Mono team is working hard on a version of the .NET runtime that runs in WASM. Blazor itself is a framework that builds on top of that Mono runtime for WASM. When the project is compiled, it’s compiled to a .NET assembly that gets loaded and executed by the Common Language Runtime (CLR) running inside the browser. It’s important to understand that the .NET code running in the browser is running in the same JavaScript security sandbox. See Dino Esposito’s Cutting Edge column in this issue for more on this topic.

Getting a Real Weather Forecast

The forecast is fake data loaded from a file embedded in the project. I’m going to replace the entire Fetch data page with real data from real Web services. First, I need to replace the HTML with something simple that prompts the user for a ZIP code. Inside the FetchData.cshtml, I replace the HTML code with the following:

<h1>Weather Forecast</h1>
<div class="input-group col-md-3">
  <input type="text" class="form-control" placeholder="Zip code"
    bind="@zip" maxlength="5" />
  <div class="input-group-append">
    <button class="btn btn-secondary" type="button"
      onclick="@GetWeather">Get Weather</button>
  </div>
</div>
<br /><span style="color:red">@errorMessage</span>

Notice the Razor syntax embedded in the script. The @ sign signals code and variables. The input tag captures the ZIP code and binds it to a variable called zip. The button tag has its onclick method bound to @GetWeather, which calls the GetWeather method in C# (not JavaScript). There’s also a little @errorMessage that can be used if the user enters an invalid ZIP. These variables and methods are defined in the same FetchData.cshtml inside the @functions block:

@functions {
  String zip = String.Empty;
  String errorMessage = String.Empty;
  private async Task GetWeather()
  {
  }
}

Running the application now gives the user the ability to enter the ZIP code and click the Get Weather button. The GetWeather method is empty, so nothing happens. In the next section, I’ll add the code that will call the Web services and retrieve the current weather conditions.

Storing the Data Returned from the Web Service

Next, I need some local variables in my page to store the data returned from the Web service calls. The Razor pages will bind to these variables so the data can be displayed. These variables get added to the @functions block in the page, like so:

Models.CurrentConditions currentcondition;
Models.Alert alerts;
Models.ZipLookup ziplookup;
String imgurl = "";

Adding the ZIP Code Web Service

Once the user clicks the Get Weather button, the ZIP code in the input box must be validated. The first public Web service is from zippopotam.us. When the https://api.zippopotam.us/US/\<zip> API URL is called, it returns information about the specified ZIP code. The information needed from this Web service is the name of the city and state. This data will be displayed to the user in the forecast, and the state abbreviation will be used in subsequent Web service calls. The Web service code should look familiar because it uses the familiar HttpClient class.

Calling the ZIP Lookup Web Service The script in Figure 1 downloads the ZIP code info from the API and places it into a local variable called ziplookup. I can use this variable in my Razor code to display the city name. The API will return an exception if the ZIP is invalid. If that happens, an error message is displayed.

Figure 1 Getting the ZIP Code Information

try
{
  errorMessage = "";
  var zipresultStr = await Http.GetStringAsync($"https://api.zippopotam.us/US/{zip}");
  zipresultStr = zipresultStr.Replace("place name", "city").Replace(
    "state abbreviation", "stateabbr");
  ziplookup = JsonUtil.Deserialize<Models.ZipLookup>(zipresultStr);
}
catch
{
  errorMessage = "Invalid zip code";
  return;
}

Deserializing the ZIP Lookup Data In the previous code snippet, I’m retrieving data from the Web service and deserializing it into a Models.ZipLookup class. This is a class I’ve created to match the schema of the JSON data being returned:

public class ZipLookup
{
  public Place[] places { get; set; }
}
public class Place
{
  public String city { get; set; }
  public String stateabbr { get; set; }
}

Much more data is returned, but I’ve only created properties and classes for the data I want to use. The current implementation has issues with dealing with spaces in the JSON field names. As a temporary workaround, I’m using String.Replace to remove the spaces.

Displaying the City and State Now that the data has been downloaded and deserialized, I can display it in the Web page. The following code block displays the city and state abbreviation in the page:

<h1>
  @ziplookup.places[0].city, @ziplookup.places[0].stateabbr<br />
</h1>

Adding the Weather Conditions Web Service

The next Web service will retrieve the current conditions for the ZIP code from the openweathermap.org Web service. You’ll need to create an account in order to receive a special key that’s used when calling the Web service.

Calling the Current Conditions Web Service The call to get the current conditions works much like the previous Web service call. The exception here is the apikey parameter in the call. The openweathermap.org service requires a key to authenticate the caller:

currentcondition = await Http.GetJsonAsync<Models.CurrentConditions>(
  $"https://api.openweathermap.org/data/2.5/
  weather?zip={zip},us&appid=<apikey>");
imgurl = $"https://openweathermap.org/img/w/{currentcondition.weather[0].icon}.png";

The result of the current conditions call is stored in a local variable called currentcondition. That result also passes the name of an icon to be displayed that corresponds to the current conditions. The name of the image is encoded into the imgurl variable so it can be displayed in the Web page.

Deserializing Current Conditions Data Once again, the raw JSON data needs to be deserialized into a class so it can be used, as shown in Figure 2. The class definition looks a little odd, but it’s designed to match the schema of the JSON data being returned from the Web service. There’s a lot more data being returned than what’s shown here. Only the properties that are needed have to be implemented.

Figure 2 Deserializing the Raw JSON Data

public class CurrentConditions
{
  public CurrentConditions() { }
  public List<Weather> weather { get; set; }
  public Main main { get; set; }
  public String name { get; set; }
}
public class Weather
{
  public String main { get; set; }
  public String description { get; set; }
  public String icon { get; set; }
}
public class Main
{
  public decimal temp { get; set; }
  public decimal temp_min { get; set; }
  public decimal temp_max { get; set; }
}

Converting the Temperatures The temperatures returned from the Web service are in kelvins, so the values need to be converted to degrees Fahrenheit and rounded. The current temperature will be rounded to the nearest tenth of a degree. The high and low temperatures will be rounded to the nearest whole degree.

private decimal ConvertKtoF(decimal kelvin, int decimals)
{
  return Math.Round(kelvin * 9 / 5 - 459.67M, decimals);
}

Displaying the Current Conditions The Razor script in Figure 3 will now be updated to include the current temperature, the high and low, a description of the current conditions, and an icon that corresponds to the current conditions. Notice how the temperatures are being passed to the ConvertKtoF function created earlier.

Figure 3 Updating the Razor Script

<h1>
  @ziplookup.places[0].city, @ziplookup.places[0].stateabbr<br />
  @ConvertKtoF(currentcondition.main.temp, 1) &#176;F
</h1>
<h2>
  @currentcondition.weather[0].main <img src="@imgurl" style="display:inline" />
</h2>
<h3>
  <span style="display:inline;color:red">HI
    @ConvertKtoF(currentcondition.main.temp_max, 0)  &#176;F</span> /
  <span style="color:blue">LO @ConvertKtoF(
    currentcondition.main.temp_min, 0)  &#176;F
    </span><br />
</h3>

Adding National Weather Service Alerts

The final Web service will retrieve alerts from the National Weather Service (NWS) for the state the ZIP code is in.

Calling the NWS Web Service The following block of code retrieves all the current severe weather alerts from the NWS:

alerts = await Http.GetJsonAsync<Models.Alert>(
  $"https://api.weather.gov/alerts/active/area/{ziplookup.places[0].stateabbr}");

The state abbreviation retrieved from the ziplookup Web service is used to filter the alerts to the same state.

Deserializing NWS Alerts Data The JSON data needs to be deserialized into a set of classes that aligns with the NWS JSON schema. The code snippet in Figure 4 holds the alerts returned from the NWS Web service.

Figure 4 Deserializing the JSON Data

public class Alert
{
  public String type { get; set; }
  public String title { get; set; }
  public Feature[] features { get; set; }
}
public class Feature
{
  public String type { get; set; }
  public PropertyInfo properties { get; set; }
}
public class PropertyInfo
{
  public String headline { get; set; }
  public String description { get; set; }
  public DateTime effective { get; set; }
  public DateTime expires { get; set; }
}

Displaying the Severe Weather Alerts Now that the alerts have been retrieved, the code in Figure 5 is added to the Razor view to display them. It creates a table to display the alerts and uses @foreach to loop through and display each alert. The effective date, headline, and description are displayed for each alert. If there are no alerts for the state, the table will be empty.

Figure 5 Displaying Weather Alerts

<table class="table">
  <thead>
  <tr>
    <th>Date</th>
    <th>Alert</th>
  </tr>
  </thead>
  <tbody>
    @foreach (var alert in alerts.features)
    {
      <tr>
        <td>@alert.properties.effective.ToString("MM/dd/yyyy hh:mmt")</td>
        <td>
          <span style="font-weight:600">@alert.properties.headline</span><br />
          <span>@alert.properties.description</span>
        </td>
      </tr>
    }
  </tbody>
</table>

Putting It All Together

The application is now complete, as shown in Figure 6. It runs entirely in the browser and calls three external Web services to display the current weather conditions and alerts.

The Finished Application
Figure 6 The Finished Application

Publishing the Blazor Application

Publishing a Blazor application is just as easy as publishing any other ASP.NET application. Doing this from Visual Studio will compile the application code and generate a complete Web application with all of the final HTML, CSS, scripts and Blazor binaries needed. A good starting place is to use the Publish to Folder option. All of the files of the application get put there. If you look inside the Dist folder, you’ll find a standard index.htm page and an _framework folder. The _framework folder contains all of the compiled assemblies, as well as the Blazor and Mono runtime components.

Next Steps

Blazor is still an experimental framework, though the team is moving forward at a very fast rate of release. The Blazor roadmap details a full framework with routing, components, layouts and more. The Blazor project is being developed in the open on GitHub. Visit the blazor.net page to keep up-to-date and start experimenting with the current bits.


Jonthan Miller is a senior architect. He’s been developing products on the Microsoft stack for a decade and programming on .NET since its inception. Waldman is a full-stack product developer with expertise in front-end technologies (Windows Forms, Windows Presentation Foundation, Silverlight, ASP.NET, AngularJS/Bootstrap), middleware (Windows services, Web API), and back ends (SQL server, Azure).

Thanks to the following technical experts who reviewed this article: Dino Esposito (BaxEnergy), Daniel Roth (Microsoft)


Discuss this article in the MSDN Magazine forum