First, we will create an ASP.NET Core application, then we will set up Webpack and Babel. At the end, we will create a simple react-redux project.

Content

  • Prerequisites
  • Create ASP.NET Core MVC Application
  • Setting up Webpack and Babel
  • Add ReactJS to Project
  • Run Project
  • Adding Hot Module Replacement To Project
    • Adding Webpack middleware
    • Adding hot module replacement
  • Create Required React Components and navigate between them
    • Adding simplified Version of Components
    • Add Navigation Between Components
    • Server-side vs Client-side Routing

Prerequisites

Before we start, you’ll need to make sure you have the following installed pc (or mac).

  • Node JS
  • Visual Studio Code
  • NET Core SDK (I’m using SDK version 2.2)
  • Omni Sharp C# extension

If we have everything set up, let’s start by creating a new ASP.NET Core Web Application.

Create ASP.NET Core MVC Application

We’re going to create a new project from Command Prompt using the command dotnet new mvc of ASP.NET Core CLI.

First, open a Command Prompt window and navigate to the desired folder then run the below commands to create a new project.

mkdir MyApp
cd MyApp
dotnet new mvc

Run the below command to open a newly created project in Visual Studio Code.

Code .

If you already installed C# extensions add-on it will prompt you to create required assets for this project, press “yes”.

To start working with npm packages for this project run below command in terminal.

npm init

It will ask you a few questions, you may leave them blank if you don’t know what to fill.

After finishing this command, packages.json file will be added to the project’s root directory.

Setting up Webpack and Babel

Webpack is an open-source JavaScript module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resources or assets. Webpack takes modules with dependencies and generates static assets representing those modules.
Since we want to use React.js and it is a JavaScript library, we need Webpack to bundle our JavaScript code for browsers.

If you didn’t already install Webpack globally run below:

npm install webpack –g
npm install webpack-cli -g

Then run below to add Webpack to your project:

npm i webpack –-save-dev
npm i webpack-cli --save-dev

open package.json file and add below

{
  "name": "myapp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
  "build": "webpack",
  "test": "echo \"Error: no test specified\" && exit 1"
   },
  "author": "",
  "license": "ISC",
  "devDependencies": {
  "webpack": "^4.20.2",
  "webpack-cli": "^3.1.2"
  }
}

We will write our React app with ES6. ES6 refers to version 6 of the ECMA Script programming language. ECMA Script is the standardized name for JavaScript, and version 6 is the next version after version 5, which was released in 2011. Not all Browsers support es6, so we need a translator for this purpose.
Babel is a JavaScript compiler that includes the ability to compile JSX into regular JavaScript.
Run below commands in CMD to add required Babel libraries to our project:

npm install babel-loader @babel/core --save-dev
npm install @babel/preset-env --save-dev
npm install @babel/preset-react --save-dev

Create .babelrc configuration file in the root folder of the project with below content.

{
  "presets": ["@babel/preset-env", "@babel/preset-react"]
}

Now, we should set up Webpack to use Babel as loader of JavaScript files.
Create webpack.config.js file in root directory of the project and write below code in it.

const path = require('path');

module.exports = {
entry: "./src/index.js",
mode: "development", // "production" | "development" | "none"
output: {
path: path.resolve(__dirname, "./wwwroot/dist"), // string
// the target directory for all output files
// must be an absolute path (use the Node.js path module)
filename: "bundle.js", // the filename template for entry chunks
publicPath: "dist/", // string
// the url to the output directory resolved relative to the HTML page
},
module: {
rules: [
    {
test: /\.js$/,
exclude: /node_modules/,
use: {
     loader: "babel-loader"
     }
   }
 ]
}
}

Add ReactJs to Project

Run the below command to add React to our project.

npm install react react-dom --save-dev

Create src folder in the root directory of the project then create src/index.js file.

import * as ReactDOM from 'react-dom';
import * as React from 'react';

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('root')
);

Edit Views/Home/Index.cshtml file and remove extra content from it to be like below.

@{
    ViewData["Title"] = "Home Page";
}

<div id="root">Loading...</div>

@section scripts {
  <script src="~/dist/bundle.js"></script>
}

Now, that we added React to our project we don’t need other razor views and controllers came with ASP.NET Core CLI Template. So let get rid of them.

First, lets clear wwwroot folder completely then delete extra views like Privacy.cshtml file from View folder. We only need Index.cshtml file. Then modify layout.cshtml file as below:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - MyApp</title>
    <base href="~/" />

</head>
<body>
    @RenderBody()

    @RenderSection("scripts", required: false)
</body>
</html>

Rename Models folder to ViewModels. Then open ErroViewModel file and change the namespace of it.
Open ViewImports.cshtml and Change namespace there

@using MyApp.ViewModels

And also change using of this namespace in HomeController.cs file.

Run Project

Before testing our application first run below command to create our bundle.js file

Webpack --config webpack.config.js

Then run

dotnet run

you may see below Privacy error:

DOTNET 2.2 templates by default require HTTPS for all requests and redirect all HTTP requests to HTTPS. When we run newly created project, we will see serving pages as below:

.../myapp>dotnet run
Hosting environment: Development
Content root path: C:\Code\imnapo\naravan
Now listening on: https://localhost:5001
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

As you can see, its listening HTTP over port 5000 and HTTPS over port 5001. If we run the application, we may see an above privacy error.

To solve this untrusted SSL cert error on local machine and to trust the certificate run below command:

dotnet dev-certs https --trust

Below message will be shown, press Yes to install the certificate.

You may close your browser and open it again to see trusted badge in your browser. Now you should see “Hello World!” text in your browser.
Now try to change Hello World text and save the file and refresh your browser, but as you can see nothing happened. This happens because we didn’t run our Webpack command to bundle our JavaScript again. There is a solution for this to instantly see changes as you save your file and we will explain it in the next topic.

Adding Hot Module Replacement

What we need are Webpack middleware and Hot Module replacement:

Webpack middleware so that, during development, any webpack-built resources will be generated on demand, without you having to run webpack manually or compile files to disk

Hot module replacement so that, during development, your code and markup changes will be pushed to your browser and updated in the running application automatically, without even needing to reload the page.

  • Adding Webpack middleware

Open Startup.cs file and add this using:

using Microsoft.AspNetCore.SpaServices.Webpack;

Go to configure method and add Webpack middleware to it as below:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
    HotModuleReplacement = true,
    ReactHotModuleReplacement = true
    });
}
else
{
    app.UseExceptionHandler("/Home/Error");
}

Open _ViewImports.cshtml file and add below to it:

@addTagHelper *, Microsoft.AspNetCore.SpaServices

there are other packages to install too:

npm install react-hot-loader
npm install --save-dev webpack-hot-middleware
npm install --save-dev webpack-dev-middleware
  • Adding hot module replacement:

We need two packages to enable hot module replacement in ASP.NET, so run below command:

npm install -D aspnet-webpack aspnet-webpack-react

our webpack configuration file needs some modifications:

    • Change entry point from string of “./src/index.js” to an object with a main property like { main: "./src/index.js"}.
  • Add react-hot-module/babel plugin to babel-loader

So open webpack.config.js file and change it as below:

  const path = require('path');

  module.exports = {
  entry: { main: "./src/index.js"},
  mode: "development", // "production" | "development" | "none"
  output: {
    path: path.resolve(__dirname, "./wwwroot/dist"), // string
    // the target directory for all output files
    // must be an absolute path (use the Node.js path module)
    filename: "bundle.js", // the filename template for entry chunks
    publicPath: "dist/", // string    
  // the url to the output directory resolved relative to the HTML page
  },
  module: {
     rules: [
       {
         test: /\.js$/,
         exclude: /node_modules/,
         use: {
          loader: "babel-loader",
          options: {
            plugins: [
              "react-hot-loader/babel"
            ]
          }
        }
      }
    ]
  }
 }

Add the end open index.js and add this to end of the file.

// Allow Hot Module Replacement
if (module.hot) {
  module.hot.accept();
}

Now, run the project and try to change the “hello world!” Text one more time and save the file. You should instantly see changes in your browser (you may run  webpack –config webpack.config.js command ones more before run the app).

Create Required React Components and navigate between them

Now, that we initialized our ASP.NET Application, let create our React app to register and login users. To simplify, we are going to create 4 components. We are not going to define the real implementation of them. We will just return simple text in render method of each component.

  • App.js : our main component (Home Page)
  • Signin.js : our login page
  • Signup.js: our register page
  • Feature.js: our special component that should be shown only when a user is logged in.

Add body of Required Components

We are going to create simple components first and not their real implementation. let us create “components” folder under “src” folder:

…\src> mkdir components

Then let’s create our first component named app.js:

…\src>cd components

…\src\components> echo.>App.js

Modify app.js file as below:

import React, { Component} from "react";

class App extends Component {
    constructor(props) {
         super(props);
    }

    render() {
       return (<div>This is App.js</div>);
    } 
} 
export default App;

Then let’s create the Feature component:

…\src\components> echo.> Feature.js

Modify the Feature.js file as below:

import React, { Component} from "react";

class Feature extends Component {
  
  constructor(props) {
    super(props);
    
  }
  

  render()
  {
    return (
      <div>This is Feature.js. Only authenticated Users allowed.</div>
    );
  }
}

export default Feature;

For the sake of having a better structure, we will create a sub-folder for related components.
Create a new folder named auth, under the components folder and add Signup.js and Signin.js files to it.

…\src\components> mkdir auth
…\src\components\auth> echo.> Signin.js
…\src\components\auth> echo.> Signin.js

Create the sign-in component as below:

import React, { Component } from 'react';
class Signin extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        return("This is signin page!") 
    } 
}
 
export default Signin;

Next, let’s create our signup component:

import React, { Component } from 'react';
class Signup extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        return("This is signup page!") 
    } 
} 

export default Signup;

Add Navigation Between Components

Next step is to create navigation between these 4 components.

Add react router to project.

npm install react-router-dom

modify the index.js file to add the ability to navigate between components.

import * as ReactDOM from 'react-dom';
import * as React from 'react';
import { BrowserRouter as Router, Route, Link,Switch } from "react-router-dom";
import App from './components/App';
import Signup from './components/auth/Signup';
import Signin from './components/auth/Signin';


ReactDOM.render(
    <Router>
      <div>
          <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/Signin">Signin</Link>
          </li>
          <li>
            <Link to="/Signup">Signup</Link>
          </li>
        </ul>

        <hr />
        <Route exact path="/" component={App} />  
        <Route path="/Signin" component={Signin} />                                             
        <Route path="/Signup" component={Signup} />   
          
      </div>              
  </Router>
  ,
  document.getElementById('root')
);

// Allow Hot Module Replacement
if (module.hot) {
  module.hot.accept();
}

Run the application and if everything is set as I said you should be able to navigate between these components using provided links inside home page. Now, try to type desired page (Ex. http://localhost:5000/Signin) on the browsers address bar. As you can see, it will not load the page. So why we access our pages from links inside pages but we can not access them when typing directly on browsers address bar?

Server-side vs Client-side Routing

The first big thing to understand about this is that there are now 2 places where the URL is interpreted. The very first request will always be to the server. That will then return a page that contains the needed script tags to load React and React Router etc. Only when those scripts have loaded does phase 2 start.

So in our case when we type directly on browsers address bar no react-router is running yet. So, it will make a server request and that is where our problem starts.

To fix this we should add single page application fallback rout to our ASP.NET application routes. Open startup.cs file and modify below section in Configure method:

       app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");

                    routes.MapSpaFallbackRoute(
                    name: "spa-fallback",
                    defaults: new { controller = "Home", action = "Index" });
            });

Run the application once again and now if we type our desired page like http://localhost:5000/Signin on browser’s address bar, we will navigate to the sign in page.

In the next chapter, we will create an Authentication Controller using OpenIddict package. We will also create an Accounts controller to register and manage users.