Documentation in Progress
See on GitHub
This project was put together based off of the
Angular
projects from thegenerator-aspnetcore-spa
generator. I wanted to understand how all of the components worked in that project template, and how to strip it down to a more bare starting point. A big reason for this was that I didn't want to be tied to using Bootstrap
- .vscode
launch.json
tasks.json
- Controllers
DataController.cs
HomeController.cs
- Models
- Extensions
WeatherExtensions.cs
WeatherForecast.cs
- Extensions
- src
- app
- components
- app
app.component.css
app.component.html
app.component.ts
- home
home.component.html
home.component.ts
- app
app.module.ts
- components
boot-client.ts
boot-server.ts
- app
- Views
- Home
Index.cshtml
- Shared
_Layout.cshtml
Error.cshtml
_ViewImports.cshtml
_ViewStart.cshtml
- Home
- wwwroot
.gitignore
appsettings.json
CoreTemplate.csproj
global.json
package.json
Program.cs
Startup.cs
tsconfig.json
web.config
webpack.config.js
webpack.config.vendor.js
mkdir {template} && cd {template}
npm init -y
code .
Using npm
, install the dependencies listed in package.json
as follows
npm install --save {dependency}
The only sections that need to be kept are name
, version
, dependencies
, and scripts
.
Note the
scripts
section is covered later when talking about build tools
package.json
{
"name": "core-template",
"version": "1.0.0",
"dependencies": {
"@angular/common": "^2.4.9",
"@angular/compiler": "^2.4.9",
"@angular/core": "^2.4.9",
"@angular/forms": "^2.4.9",
"@angular/http": "^2.4.9",
"@angular/platform-browser": "^2.4.9",
"@angular/platform-browser-dynamic": "^2.4.9",
"@angular/platform-server": "^2.4.9",
"@angular/router": "^3.4.9",
"@types/node": "^7.0.8",
"angular2-platform-node": "^2.1.0-rc.1",
"angular2-template-loader": "^0.6.2",
"angular2-universal": "^2.1.0-rc.1",
"angular2-universal-patch": "^0.2.1",
"angular2-universal-polyfills": "^2.1.0-rc.1",
"aspnet-prerendering": "^2.0.3",
"aspnet-webpack": "^1.0.28",
"awesome-typescript-loader": "^3.1.2",
"css": "^2.2.1",
"css-loader": "^0.27.1",
"es6-shim": "^0.35.3",
"event-source-polyfill": "0.0.9",
"expose-loader": "^0.7.3",
"extract-text-webpack-plugin": "^2.1.0",
"file-loader": "^0.10.1",
"html-loader": "^0.4.5",
"isomorphic-fetch": "^2.2.1",
"jquery": "^3.1.1",
"json-loader": "^0.5.4",
"preboot": "^4.5.2",
"raw-loader": "^0.5.1",
"rxjs": "^5.2.0",
"style-loader": "^0.13.2",
"to-string-loader": "^1.1.5",
"typescript": "^2.2.1",
"url-loader": "^0.5.8",
"webpack": "^2.2.1",
"webpack-hot-middleware": "^2.17.1",
"webpack-merge": "^4.0.0",
"zone.js": "^0.8.0"
},
"scripts": {
"build": "npm install && npm run webpack",
"webpack": "webpack --config webpack.config.vendor.js && webpack"
}
}
Specifies files that Git
should specifically ignore. Pulled from the aspnetcore-spa
generator template
/Properties/launchSettings.json
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
bin/
Bin/
obj/
Obj/
# Visual Studio 2015 cache/options directory
.vs/
/wwwroot/dist/**
/ClientApp/dist/**
# Workaround for https://github.com/aspnet/JavaScriptServices/issues/235
!/wwwroot/dist/_placeholder.txt
!/ClientApp/dist/_placeholder.txt
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config
# Windows Store app package directory
AppPackages/
BundleArtifacts/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
orleans.codegen.cs
# Workaround for https://github.com/aspnet/JavaScriptServices/issues/235
/node_modules/**
!/node_modules/_placeholder.txt
/yarn.lock
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/
Specifies the version of the .Net Core SDK to use for this project
{
"sdk" { "version": "1.1.1" }
}
Provides various application configuration settings
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
The presence of tsconfig.json
indicates the root of a TypeScript projects. Specifies the root files and the compiler options required to compile the project
{
"compilerOptions": {
"moduleResolution": "node",
"target": "es5",
"sourceMap": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"skipDefaultLibCheck": true,
"lib": [ "es6", "dom" ],
"types": [ "node" ]
},
"exclude": [ "bin", "node_modules" ],
"atom": { "rewriteTsconfig": false }
}
Configures the ASP.NET Core Module
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
</system.webServer>
</configuration>
Specifies the MSBuild configuration for the project
<Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81</PackageTargetFallback>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.0" />
</ItemGroup>
<ItemGroup>
<!-- Files not to show in IDE -->
<None Remove="yarn.lock" />
<!-- Files not to publish (node that the 'dist' subfolders are re-added below) -->
<Content Remove="src\**" />
</ItemGroup>
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
<Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />
<Exec Command="node node_modules/webpack/bin/webpack.js --env.prod" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="wwwroot\dist\**; src\dist\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>
In the above configuration section, the following files were specifically relevant to .NET Core:
appsettings.json
global.json
web.config
CoreTemplate.csproj
The file definitions that follow represent the starting point for the ASP.NET Core aspect of the project template
Application entry point. Builds hosting configuration using WebHostBuilder
using System.IO;
using Microsoft.AspNetCore.Hosting;
namespace CoreTemplate
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}
The Startup class configures the request pipeline that handles all requests made to the application
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace CoreTemplate
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
HotModuleReplacement = true
});
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
}
}
}
Although
WeatherForecast
class,WeatherExtensions
class, andDataController
API controller files are defined in the project, they are not actually used. They are purely an example for how to separate business logic from API calls and model definitions
Defines an example C# class model to be used in conjunction with an API call
namespace CoreTemplate.Models
{
public class WeatherForecast
{
public string DateFormatted { get; set; }
public int TemperatureC { get; set; }
public string Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
}
A static extensions class for business logic related to WeatherForecast
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace CoreTemplate.Models.Extensions
{
public static class WeatherExtensions
{
private static string[] Summaries = new[]
{
"Freezing",
"Bracing",
"Chilly",
"Cool",
"Mild",
"Warm",
"Balmy",
"Hot",
"Sweltering",
"Scorching"
};
public static Task<IEnumerable<WeatherForecast>> GetWeatherForecasts(this Random rng)
{
return Task.Run(() =>
{
var model = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
DateFormatted = DateTime.Now.AddDays(index).ToString("d"),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
});
return model;
});
}
}
}
A Web API Controller. The use of the Route
attribute specifies that all of the action methods in this controller can be reached at /api/Data
. The HttpGet
above the GetWeatherForecasts
action method specifies that it is an HTTP GET
request and that it can be reached at /api/Data/GetWeatherForecasts
. Note that in this method, the collection is retrieved using the GetWeatherForecasts
extension method defined in the WeatherExtensions
class
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using CoreTemplate.Models;
using CoreTemplate.Models.Extensions;
using Microsoft.AspNetCore.Mvc;
namespace CoreTemplate.Controllers
{
[Route("api/[controller]")]
public class DataController : Controller
{
[HttpGet("[action]")]
public async Task<IEnumerable<WeatherForecast>> GetWeatherForecasts()
{
return await new Random().GetWeatherForecasts();
}
}
}
ASP.NET MVC Controller that provides an entry point to for the application
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace CoreTemplate.Controllers
{
public class HomeController : Controller
{
public Task<IActionResult> Index()
{
return Task.Run(() =>
{
return (IActionResult)View();
});
}
public Task<IActionResult> Error()
{
return Task.Run(() =>
{
return (IActionResult)View();
});
}
}
}
Directives shared by many views may be specified in a common _ViewImports.cshtml
file
@using CoreTemplate
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
@addTagHelper "*, Microsoft.AspNetCore.SpaServices"
By convention, the _ViewStart.cshtml
file is located in the Views
folder. The statements listed in _ViewStart.cshtml
are run before every full view (not layouts, and not partial views)
@{
Layout = "_Layout";
}