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>
<!---->
<!---->
<!---->
<!---->
</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>© 2016 - Toan Manh Nguyen</p>
</footer>
</div>
<!---->
<!---->
<!---->
<!---->
</body>
</html>
Add site.css in wwwroot/styles/site.css
body {
padding-top: 50px;
padding-bottom: 20px;
}
.body-content {
padding-left: 15px;
padding-right: 15px;
}
input,
select,
textarea {
max-width: 280px;
}
.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>
Cool!!! Gulp automatically inject necessary to index.html page with sorting.
Now, let's talk about Default Views Folder of AspNetCore:
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>
<!---->
<!---->
<!---->
<!---->
</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>© 2016 - Toan Manh Nguyen</p>
</footer>
</div>
<!---->
<!---->
<!---->
<!---->
</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