Click here to Skip to main content
14,209,318 members
Click here to Skip to main content
Article
Posted 26 May 2016

Stats

63.5K views
44 bookmarked

ASP.NET Core RC2 Using WEB API And AngularJS - Part 1

,
Rate this:
4.94 (22 votes)
Please Sign up or sign in to vote.
4.94 (22 votes)
20 Jun 2016     CPOL    
In this article you will learn about ASP.NET Core RC2 using WEB API and AngularJS.

Introduction

In this article we will see in detail how to create ASP.NET Core RC2 using WEB API and AngularJS.

Key point is still using .NET Framework 4.5.2 instead of .NET Core.

You can also view my next articles:

  • How to replace default Views location of ASP.NET Core?
  • How to publish ASP.NET Core RC2 to IIS?
  • How to intergrate NLog to ASP.NET Core project?

Prerequisites

Visual Studio 2015: You can download it from here.

.NET Core SDK: download .NET Core SDK from this link.

.NET Framework 4.5.2: download .NET Framework 4.5.2 from this link.

.NodeJS: download NodeJS from this link.

Sourcecode: download source from this link.

In this article we will see in detail how to:

  • Create our ASP.NET Core RC 2 Web Application.
  • How to add our WEB API Controller.
  • How to add javascript package using NPM.
  • How to configure Gulp File.
  • How to Run the Gulp file using Visual Studio Task Runner Explorer
  • How to create our AngularJS Module, Controller and Service file and get WEB API data for bind in MVC page.

Next part will more concept: Part 2

  • Authentication
  • Mocking service API using json file
  •  

Using the code

Create a Database

In this version, i won't using Database, let it for the next version. Just make this very very simple.

Create our ASP.NET Core RC 2 Web Application

After installed both Visual Studio 2015 and .NET Core SDK, run your Visual Studio 2015. Click New, then Project, select Web and select ASP.NET Web Application. Enter your Project Name and click OK.

Let see a little about my project/solution name:

  • Solution name: AspNetCoreSPA
  • Project name: AspNetCoreSPA.Web

This is my rule when i create new web project. Solution name stand for whole project, it should be simple and single (not any dot included). Project name stand for purpose of each project (for this, it's web for MVC). Project name can be more later like: AspNetCoreSPA.Business, AspNetCoreSPA.DataAccess, AspNetCoreSPA.Core. I will write another article about creating solution with multiple project using .NET Core.

Then select Empty template. We will start from scratch to understand what we will install, what we will use.

After project created, we will create structure folder like this:

I will explain more detail why i created this structure:

  • wwwroot will contain all client code (js, html, css).
    • A big note here: We won't use ".cshtml" (sound crazy huh?), we will use just ".html" and AspNet just for data service.
  • app: include html + js file (Controller, view, template,...).
  • styles: contain css files
  • images: image files
  • scripts: contain our own js files: common js, ...ect
  • libraries: contain libraries. Those libraries were copied from node_modules (detail).
  • Configurations: Stand for any configuration class.
  • Controllers: contain ASP.NET Controller.

Content of project.json (What is the project.json file?)

{
  "userSecretsId": "aspnet-AspNetCoreSPA-c23d27a4-eb88-4b18-9b77-2a93f3b15119",

  "webroot": "wwwroot",
  "version": "1.0.0-*",

  "dependencies": {
    "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Diagnostics": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Razor.Tools": {
      "version": "1.0.0-preview1-final",
      "type": "build"
    },
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.StaticFiles": "1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-rc2-final",
    "Microsoft.Extensions.Logging": "1.0.0-rc2-final",
    "Microsoft.Extensions.Logging.Console": "1.0.0-rc2-final",
    "Microsoft.Extensions.Logging.Debug": "1.0.0-rc2-final",
    "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-rc2-final",
    "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
      "version": "1.0.0-preview1-final",
      "type": "build"
    },
    "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": {
      "version": "1.0.0-preview1-final",
      "type": "build"
    }
  },

  "tools": {
    "Microsoft.AspNetCore.Razor.Tools": {
      "version": "1.0.0-preview1-final",
      "imports": "portable-net45+win8+dnxcore50"
    },
    "Microsoft.AspNetCore.Server.IISIntegration.Tools": {
      "version": "1.0.0-preview1-final",
      "imports": "portable-net45+win8+dnxcore50"
    },
    "Microsoft.Extensions.SecretManager.Tools": {
      "version": "1.0.0-preview1-final",
      "imports": "portable-net45+win8+dnxcore50"
    },
    "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
      "version": "1.0.0-preview1-final",
      "imports": [
        "portable-net45+win8+dnxcore50",
        "portable-net45+win8"
      ]
    }
  },

  "frameworks": {
    "net452": { }
  },

  "buildOptions": {
    "emitEntryPoint": true,
    "preserveCompilationContext": true
  },

  "publishOptions": {
    "include": [
      "wwwroot",
      "appsettings.json",
      "web.config"
    ]
  },

  "scripts": {
    "prepublish": [ "npm install", "bower install", "gulp clean", "gulp min" ],
    "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
  }
}

Create package.json (What is the package.json file?): You can understand that package.json is similar to Nuget Package Manger. Before using npm command, you should learn a little about NodeJS.

Content of package.json is stand for the lib we used for this project.

  • angular: 1.5.5
  • bootstrap: 3.3.6
  • devDependencies is stand for libs we used in development time.
{
  "version": "1.0.0",
  "name": "asp.net",
  "private": true,
  "dependencies": {
  },
  "devDependencies": {
    "gulp": "3.8.11",
    "gulp-inject": "4.1.0",
    "stream-series": "0.1.1",
    "wiredep": "4.0.0"
  }
}

Create bower.json too: Add New Item -> Bower Configuration File

{
  "name": "asp.net",
  "private": true,
  "dependencies": {
    "jquery": "2.2.4",
    "bootstrap": "3.3.6",
    "angular": "1.5.5",
    "angular-ui-router": "0.3.0"
  },
  "overrides":{
      "bootstrap":{
        "main": [
          "less/bootstrap.less",
          "dist/css/bootstrap.css",
          "dist/js/bootstrap.js"
        ]
      }
   }
}

Why using Bower? Bower is a javascript package manager with dependencies: Example: Bootstrap depends on jquery,....

Remember change .bowerrc content:

Content of .bowerrc: This mean all dependencies in bower.json will be copied to "wwwroot/libraries" folder

{
    "directory": "wwwroot/libraries"
}

We will use wiredep to automatically inject bower libraries to index.html, we will see more detail later.

Create appsettings.json: App setting like web.config in previous version of ASP.NET MVC. I will explain how to use content of this file later. Firstly, you can see DefaultConnection is empty because we don't use any connection to DB.

{
  "ConnectionStrings": {
    "DefaultConnection": ""
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

Our Program.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;

namespace AspNetCoreSPA.Web
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();

            host.Run();
        }
    }
}

Our Startup.cs

using AspNetCoreSPA.Web.Configurations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace AspNetCoreSPA.Web
{
    public class Startup
    {
        public IConfigurationRoot Configuration { get; }

        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);

            Configuration = builder.Build();
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.ReplaceDefaultViewEngine();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseStaticFiles();
   
            app.UseMvcWithDefaultRoute();
        }
    }
}

Create gulpfile.js: Right Click on our project -> Add New Item

Content of gulpfile.js:

/// <binding BeforeBuild='inject:index' />
"use strict";

var gulp = require("gulp"),
    series = require('stream-series'),
    inject = require('gulp-inject'),
    wiredep = require('wiredep').stream;

var webroot = "./wwwroot/";

var paths = {
    ngModule: webroot + "app/**/*.module.js",
    ngRoute: webroot + "app/**/*.route.js",
    ngController: webroot + "app/**/*.controller.js",
    script: webroot + "scripts/**/*.js",
    style: webroot + "styles/**/*.css"
};

gulp.task('inject:index', function () {
    var moduleSrc = gulp.src(paths.ngModule, { read: false });
    var routeSrc = gulp.src(paths.ngRoute, { read: false });
    var controllerSrc = gulp.src(paths.ngController, { read: false });
    var scriptSrc = gulp.src(paths.script, { read: false });
    var styleSrc = gulp.src(paths.style, { read: false });

    gulp.src(webroot + 'app/index.html')
        .pipe(wiredep({
            optional: 'configuration',
            goes: 'here',
            ignorePath: '..'
        }))
        .pipe(inject(series(scriptSrc, moduleSrc, routeSrc, controllerSrc), { ignorePath: '/wwwroot' }))
        .pipe(inject(series(styleSrc), { ignorePath: '/wwwroot' }))
        .pipe(gulp.dest(webroot + 'app'));
});

More detail about gulpfile.js:

Series: used for order javascript file: Because when we include js file in a html page, it must be ordered.

Inject: used for include neccessary libraries into our html page.

Wiredep: used for include bower libraries into our html page.

var gulp = require("gulp"),
    series = require('stream-series'),
    inject = require('gulp-inject'),
    wiredep = require('wiredep').stream;

Now see the main script:

gulp.src(webroot + 'app/index.html')
        .pipe(wiredep({
            optional: 'configuration',
            goes: 'here',
            ignorePath: '..'
        }))
        .pipe(inject(series(scriptSrc, moduleSrc, routeSrc, controllerSrc), { ignorePath: '/wwwroot' }))
        .pipe(inject(series(styleSrc), { ignorePath: '/wwwroot' }))
        .pipe(gulp.dest(webroot + 'app'));

Line 1: Hey gulp, we will use index.html page for inject js and css, let look at the content of index.html and you will see.

Line 2: Inject bower libraries sorted by dependencies

<!-- bower:css -->
<!-- endbower -->

<!-- bower:js -->
<!-- endbower -->

Line 3: Inject

<!-- inject:css -->
<!-- endinject -->

<!-- inject:js -->
<!-- endinject -->

Create index.html: Right click on app -> Add -> New Item -> HTML Page -> index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>AspNetCoreSPA</title>

    <!-- bower:css -->
    <!-- endbower -->
    <!-- inject:css -->
    <!-- endinject -->
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <a href="#">AspNetCoreSPA</a>
            </div>
        </div>
    </div>
    <div class="container body-content">
        <div class="jumbotron">
            <h1>AspNetCoreSPA</h1>
            <p class="lead">Welcome to AspNetCoreSPA</p>
        </div>
        <div class="row">
            <h2>Student List</h2>
            <table class="table table-striped">
                <thead>
                    <tr>
                        <th>Firstname</th>
                        <th>Lastname</th>
                        <th>Email</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>John</td>
                        <td>Doe</td>
                        <td>john@example.com</td>
                    </tr>
                    <tr>
                        <td>Mary</td>
                        <td>Moe</td>
                        <td>mary@example.com</td>
                    </tr>
                    <tr>
                        <td>July</td>
                        <td>Dooley</td>
                        <td>july@example.com</td>
                    </tr>
                </tbody>
            </table>
        </div>

        <hr />
        <footer>
            <p>&copy; 2016 - Toan Manh Nguyen</p>
        </footer>
    </div>

    <!-- bower:js -->
    <!-- endbower -->
    <!-- inject:js -->
    <!-- endinject -->
</body>
</html>

Add site.css in wwwroot/styles/site.css

body {
    padding-top: 50px;
    padding-bottom: 20px;
}

/* Wrapping element */
/* Set some basic padding to keep content from hitting the edges */
.body-content {
    padding-left: 15px;
    padding-right: 15px;
}

/* Set widths on the form inputs since otherwise they're 100% wide */
input,
select,
textarea {
    max-width: 280px;
}

/* Styles for angular ui transition animations */
.angular-animation-container {
    position: relative;
}

.shuffle-animation.ng-enter,
.shuffle-animation.ng-leave {
    position: absolute;
}

.shuffle-animation.ng-enter {
    -moz-transition: ease-out all 0.3s 0.4s;
    -o-transition: ease-out all 0.3s 0.4s;
    -webkit-transition: ease-out all 0.3s 0.4s;
    -ms-transition: ease-out all 0.3s 0.4s;
    transition: ease-out all 0.3s 0.4s;
    left: 2em;
    opacity: 0;
}

    .shuffle-animation.ng-enter.ng-enter-active {
        left: 0;
        opacity: 1;
    }

.shuffle-animation.ng-leave {
    -moz-transition: 0.3s ease-out all;
    -o-transition: 0.3s ease-out all;
    -webkit-transition: 0.3s ease-out all;
    -ms-transition: 0.3s ease-out all;
    transition: 0.3s ease-out all;
    left: 0;
    opacity: 1;
}

    .shuffle-animation.ng-leave.ng-leave-active {
        left: 2em;
        -ms-opacity: 0;
        opacity: 0;
    }

Create HomeController to return index.html: Right click on Controllers folder -> Add -> New Item-> Class -> HomeController.cs

using Microsoft.AspNetCore.Mvc;

namespace AspNetCoreSPA.Web.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View("index");
        }
    }
}

Then go to View -> Other Windows -> Task Runner Explorer to see task of Gulpfile.js

Then Right click on lib and Run:

After run gulp task, the index.html will change a little:

In <head>

<!-- bower:css -->
<link rel="stylesheet" href="/libraries/bootstrap/dist/css/bootstrap.css" />
<!-- endbower -->
<!-- inject:css -->
<link rel="stylesheet" href="/styles/site.css">
<!-- endinject -->

In <body>

<!-- bower:js -->
<script src="/libraries/jquery/dist/jquery.js"></script> 
<script src="/libraries/bootstrap/dist/js/bootstrap.js"></script> 
<script src="/libraries/angular/angular.js"></script> 
<script src="/libraries/angular-ui-router/release/angular-ui-router.js"></script> 
<!-- endbower --> 
<!-- inject:js --> 
<script src="/scripts/site.common.js"></script> 
<script src="/scripts/site.ng.js"></script> 
<script src="/app/index.module.js"></script> 
<script src="/app/index.route.js"></script> 
<script src="/app/main/main.controller.js"></script> 
<!-- endinject -->

Cool!!! Gulp automatically inject necessary to index.html page with sorting.

Now, let's talk about Default Views Folder of AspNetCore:

  • Default Views folder of AspNetCore are: 
  • "/Views/{1}/{0}" + ViewExtension"
    "/Views/Shared/{0}" + ViewExtension"
  • But our views located at wwwroot/app, so how AspNetCore can detect our views? We have a bug here. Solution below:
    • Extend RazorViewEngine
    • Replace instance of IRazorViewEngine to MyRazorViewEngine, our new locations are:
    • "~/wwwroot/app/views/{1}/{0}.html"
      "~/wwwroot/app/{0}.html"
  • You can see detail in my code at github at link above

Now, we will separate html into main, the index.html just contain css, js, and body tag.

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>AspNetCoreSPA</title>

    <script type="text/javascript">
        var site = site || {};
    </script>

    <!-- bower:css -->
    <!-- endbower -->
    <!-- inject:css -->
    <!-- endinject -->
</head>
<body ng-app="app">
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <a href="#">AspNetCoreSPA</a>
            </div>
        </div>
    </div>
    <div class="container body-content">
        <div class="jumbotron">
            <h1>AspNetCoreSPA</h1>
            <p class="lead">Welcome to AspNetCoreSPA</p>
        </div>
        <div class="row">
            <div ui-view></div>
        </div>

        <hr />
        <footer>
            <p>&copy; 2016 - Toan Manh Nguyen</p>
        </footer>
    </div>

    <!-- bower:js -->
    <!-- endbower -->
    <!-- inject:js -->
    <!-- endinject -->
</body>
</html>

Let's create main.html in main folder and put code in:

main.html

<h2>Student List</h2>
<table class="table table-striped">
    <thead>
        <tr>
            <th>Firstname</th>
            <th>Lastname</th>
            <th>Email</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>John</td>
            <td>Doe</td>
            <td>john@example.com</td>
        </tr>
        <tr>
            <td>Mary</td>
            <td>Moe</td>
            <td>mary@example.com</td>
        </tr>
        <tr>
            <td>July</td>
            <td>Dooley</td>
            <td>july@example.com</td>
        </tr>
    </tbody>
</table>

We create index.module.js to config ui.router

(function () {
    'use strict';

    angular
        .module('app', [
            'ui.router'
        ]);

})();

We create index.route.js

(function () {
    'use strict';

    angular
        .module('app')
        .config(routerConfig);

    routerConfig.$inject = ['$stateProvider', '$urlRouterProvider'];

    function routerConfig($stateProvider, $urlRouterProvider) {
        $urlRouterProvider.otherwise('/');

        $stateProvider
          .state('home', {
              url: '/',
              templateUrl: 'app/main/main.html',
              controller: 'MainController',
              controllerAs: 'mainCtrl'
          });
    }
})();

Now let's see the whole project structure again:

Once again, you can download source-code here.

Let's hit F5 and see the result.

 

History

2016/05/26 - Create article.

2016/05/28 - Re-structure article and add more detail about Gulpfile.js

  • I changed some folder name in wwwroot like YeoMan folder structure. Please refer in my GitHub

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Toan Manh Nguyen
Architect FPT Software Hochiminh
Vietnam Vietnam
No Biography provided

Comments and Discussions

 
QuestionRazorViewEngine doesn't contains ViewLocationFormats property to override Pin
nimbus3d1-Nov-16 8:24
membernimbus3d1-Nov-16 8:24 
AnswerRe: RazorViewEngine doesn't contains ViewLocationFormats property to override Pin
Toan Manh Nguyen9-Mar-17 19:50
memberToan Manh Nguyen9-Mar-17 19:50 
QuestionThe gulpfile you have has a task inject:index Pin
Chris3138915-Sep-16 9:28
memberChris3138915-Sep-16 9:28 
The gulpfile you have has a task inject:index, should this be changed to lib?

gulp.task("inject:index", function () {

to
gulp.task("lib", function () {

AnswerRe: The gulpfile you have has a task inject:index Pin
Toan Manh Nguyen27-Sep-16 21:09
memberToan Manh Nguyen27-Sep-16 21:09 
QuestionWhy is 4.5.2 being used? Pin
Dewey22-Jun-16 9:46
memberDewey22-Jun-16 9:46 
AnswerRe: Why is 4.5.2 being used? Pin
Toan Manh Nguyen22-Jun-16 16:05
memberToan Manh Nguyen22-Jun-16 16:05 
QuestionPrerequisites Pin
Tillbert9929-May-16 20:42
memberTillbert9929-May-16 20:42 
AnswerRe: Prerequisites Pin
Toan Manh Nguyen29-May-16 20:55
memberToan Manh Nguyen29-May-16 20:55 
QuestionWhat version of Angular Pin
George McCormick27-May-16 4:58
memberGeorge McCormick27-May-16 4:58 
AnswerRe: What version of Angular Pin
Toan Manh Nguyen27-May-16 8:29
memberToan Manh Nguyen27-May-16 8:29 
NewsI'm going on writing full content and source code. Pin
Toan Manh Nguyen26-May-16 20:27
memberToan Manh Nguyen26-May-16 20:27 
GeneralRe: I'm going on writing full content and source code. Pin
David Schiffer29-May-16 1:04
professionalDavid Schiffer29-May-16 1:04 
GeneralRe: I'm going on writing full content and source code. Pin
Toan Manh Nguyen29-May-16 1:38
memberToan Manh Nguyen29-May-16 1:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01 | 2.8.190617.3 | Last Updated 21 Jun 2016
Article Copyright 2016 by Toan Manh Nguyen
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid