Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Creating a Chat Application Using React and ASP.NET Core - Part 5

DZone's Guide to

Creating a Chat Application Using React and ASP.NET Core - Part 5

We wrap up this great series by deploying the application we've created by using Azure App Services and Cake. Let's get started!

· Web Dev Zone ·
Free Resource

Learn how error monitoring with Sentry closes the gap between the product team and your customers. With Sentry, you can focus on what you do best: building and scaling software that makes your users’ lives better.

In this blog series, I'm going to create a small chat application using React and ASP.NET Core, to learn more about React and to learn how React behaves in an ASP.NET Core project during development and deployment. This Series is divided into 5 parts, which should cover all relevant topics:

  1. React Chat Part 1: Requirements & Setup
  2. React Chat Part 2: Creating the UI & React Components
  3. React Chat Part 3: Adding Websockets using SignalR
  4. React Chat Part 4: Authentication & Storage
  5. React Chat Part 5: Deployment to Azure

I also set-up a GitHub repository where you can follow the project: https://github.com/JuergenGutsch/react-chat-demo. Feel free to share your ideas about that topic in the comments below or in issues on GitHub. Because I'm still learning React, please tell me about significant and conceptual errors, by dropping a comment or by creating an Issue on GitHub. Thanks.

Intro

In this post, I will write about the deployment of the app to Azure App Services. I will use CAKE to build pack and deploy the apps, both the identity server and the actual app. I will run the build on AppVeyor, which is a free build server for open source projects and works great for projects hosted on GitHub.

I'll not go deep into the AppVeyor configuration, the important topics are Cake, Azure, and the app itself.

BTW: SignalR was going into the next version the last few weeks. It is no longer in alpha. The current version is 1.0.0-preview1-final. I updated the version in the package.json and in the ReactChatDemo.csproj. Also, the NPM package name changed from "@aspnet/signalr-client" to "@aspnet/signalr." I needed to update the import statement in the WebsocketService.ts file as well. After updating SignalR I got some small breaking changes, which are easily fixed (Please see the GitHub repo, to learn about the changes).

Setup Cake

Cake is a build DSL, that is built on top of Roslyn to use C#. Cake is open source and has a huge community, who creates a ton of add-ins for it. It also has a lot of built-in features.

Setting up Cake is easy. Just open the PowerShell and cd to the solution folder. Now you need to load a PowerShell script that bootstraps the Cake build and loads more dependencies if needed.

Invoke-WebRequest https://cakebuild.net/download/bootstrapper/windows -OutFile build.ps1 

Later on, you'll need to run the build.ps1 to start your build script. Now the Setup is complete and I can start to create the actual build script.

I created a new file called build.cake. To edit the file, it makes sense to use Visual Studio Code, because @code also has IntelliSense. In Visual Studio 2017, you only have syntax highlighting. Currently, I don't know of an add-in for VS to enable IntelliSense.

My starting point for every new build script is the simple example from the quick start demo:

var target = Argument("target", "Default");

Task("Default")
  .Does(() =>
  {
    Information("Hello World!");
  });

RunTarget(target);

The script then gets started by calling the build.ps1 in a PowerShell.

.\build.ps1


If this is working, I'm able to start hacking the Cake script in. Usually, the build steps I use look like this.

  • Cleaning the workspace.
  • Restoring the packages.
  • Building the solution.
  • Running unit tests.
  • Publishing the app.
    • in the context of non-web applications, this means packaging the app.
  • Deploying the app.

To deploy the app, I use the Cake Kudu client add-in and I need to pass in some Azure App Service credentials. You get this credentials, by downloading the publish profile from the Azure App Service. You can just copy the credentials out of the file. Be careful and don't save the secrets in the file. I usually store them in environment variables and read them from there. Because I have two apps (the actual chat app and the identity server) I need to do it twice:

#addin nuget:?package=Cake.Kudu.Client

string  baseUriApp     = EnvironmentVariable("KUDU_CLIENT_BASEURI_APP"),
        userNameApp    = EnvironmentVariable("KUDU_CLIENT_USERNAME_APP"),
        passwordApp    = EnvironmentVariable("KUDU_CLIENT_PASSWORD_APP"),
        baseUriIdent   = EnvironmentVariable("KUDU_CLIENT_BASEURI_IDENT"),
        userNameIdent  = EnvironmentVariable("KUDU_CLIENT_USERNAME_IDENT"),
        passwordIdent  = EnvironmentVariable("KUDU_CLIENT_PASSWORD_IDENT");;

var target = Argument("target", "Default");

Task("Clean")
    .Does(() =>
          {
              DotNetCoreClean("./react-chat-demo.sln");
              CleanDirectory("./publish/");
          });

Task("Restore")
.IsDependentOn("Clean")
.Does(() => 
          {
              DotNetCoreRestore("./react-chat-demo.sln");
          });

Task("Build")
.IsDependentOn("Restore")
.Does(() => 
          {
              var settings = new DotNetCoreBuildSettings
              {
                  NoRestore = true,
                  Configuration = "Release"
              };
              DotNetCoreBuild("./react-chat-demo.sln", settings);
          });

Task("Test")
.IsDependentOn("Build")
.Does(() =>
          {
              var settings = new DotNetCoreTestSettings
              {
                  NoBuild = true,
                  Configuration = "Release",
                  NoRestore = true
              };
              var testProjects = GetFiles("./**/*.Tests.csproj");
              foreach(var project in testProjects)
              {
                  DotNetCoreTest(project.FullPath, settings);
              }
          });

Task("Publish")
.IsDependentOn("Test")
.Does(() => 
          {
              var settings = new DotNetCorePublishSettings
              {
                  Configuration = "Release",
                  OutputDirectory = "./publish/ReactChatDemo/",
                  NoRestore = true
              };
              DotNetCorePublish("./ReactChatDemo/ReactChatDemo.csproj", settings);
              settings.OutputDirectory = "./publish/ReactChatDemoIdentities/";
              DotNetCorePublish("./ReactChatDemoIdentities/ReactChatDemoIdentities.csproj", settings);
          });

Task("Deploy")
.IsDependentOn("Publish")
.Does(() => 
          {
              var kuduClient = KuduClient(
                  baseUriApp,
                  userNameApp,
                  passwordApp);
              var sourceDirectoryPath = "./publish/ReactChatDemo/";
              var remoteDirectoryPath = "/site/wwwroot/";

              kuduClient.ZipUploadDirectory(
                  sourceDirectoryPath,
                  remoteDirectoryPath);

              kuduClient = KuduClient(
                  baseUriIdent,
                  userNameIdent,
                  passwordIdent);
              sourceDirectoryPath = "./publish/ReactChatDemoIdentities/";
              remoteDirectoryPath = "/site/wwwroot/";

              kuduClient.ZipUploadDirectory(
                  sourceDirectoryPath,
                  remoteDirectoryPath);
          });

Task("Default")
    .IsDependentOn("Deploy")
    .Does(() =>
          {
              Information("Your build is done :-)");
          });

RunTarget(target);

To get this script running locally, you need to set each of the environment variables in the current PowerShell session:

$env:KUDU_CLIENT_PASSWORD_APP = "super secret password" 
# and so on... 

If you only want to test the compile and publish stuff, just set the dependency of the default target to "Publish" instead of "Deploy." By doing it this way, the deploy part will not run, you don't deploy on accident, and you save some time while trying this.

Use Cake in AppVeyor

In AppVeyor, the environment variables are set in the UI. Don't set them in the YAML configuration, because they are not properly saved and everybody can see them.

The simplest appveyor.yml file looks like this.

version: 1.0.0-preview1-{build}
pull_requests:
  do_not_increment_build_number: true
branches:
  only:
  - master
skip_tags: true
image: Visual Studio 2017 Preview
build_script:
- ps: .\build.ps1
test: off
deploy: off
# this is needed to install the latest node version
environment:
  nodejs_version: "8.9.4"
install:
  - ps: Install-Product node $env:nodejs_version
  # write out version
  - node --version
  - npm --version

This configuration only builds the master and the develop branch, which makes sense if you use git flow, as I used to do. Otherwise, change it to just use the master branch or whatever branch you want to build. I skip tags to build and any other branches.

The image is Visual Studio 2017 (preview only if you want to try the latest features).

I can switch off tests because this is done in the Cake script. The good thing is that the XUnit test output, built by the test runs in Cake, gets published to the AppVeyor reports anyway. Deploy is also switched off because it's done in Cake too.

The last thing that needs to be done is to install the latest version of Node.js. Otherwise, the already installed, pretty much outdated, version is used. This is needed to download the React dependencies and to run Webpack to compile and bundle the React app.

You could also configure the Cake script in a way that tests, deploys, and build calls different targets inside Cake. But this is not really needed and makes the build a little less readable.

If you now push the entire repository to your repository on GitHub, you need to go to AppVeyor and set up a new build project by selecting your GitHub repository. A new AppVeyor account is easily set up using an existing GitHub account. When the build project is created, you don't need to set up more. Just start a new build and see what happens. Hopefully, you'll also get a green build like this:

Closing Words

This post was finished one day after the Global MVP Summit 2018 on a pretty sunny day in Seattle.

I spent two nights before the summit started in downtown Seattle and the two nights after. Both times it was unexpectedly sunny.

I finish this series with this fifth blog post and learned a little bit about React and how it behaves in an ASP.NET Core project. And I really like it. I wouldn't really do a single page application using React, this seems to be much easier and faster using Angular, but I will definitely use React in the future to create rich HTML UIs.

It works great using the React ASP.NET Core project in Visual Studio. It is great that Webpack is used here because it saves a lot of time and avoids hacking around the VS environment.

What’s the best way to boost the efficiency of your product team and ship with confidence? Check out this ebook to learn how Sentry's real-time error monitoring helps developers stay in their workflow to fix bugs before the user even knows there’s a problem.

Topics:
web dev ,react ,web application development ,asp.net core

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}