Getting Started With ASP.NET Core 1.0 From Scratch
It’s an exciting time to be a developer. The tools available to us are getting better and more platform-agnostic and Microsoft is embracing the Open Source community. In this post, I am going to explore getting started with a ASP.NET Core app on Mac OS. I’m using Visual Studio Code as my editor but you can use whatever editor you prefer. Let’s get started!
First off, let’s create a directory and run dotnet new
to create a new C# app. I’m calling my app HelloCore:
Deans-MacBook-Pro:Projects deandavidson$ mkdir HelloCore
Deans-MacBook-Pro:Projects deandavidson$ cd HelloCore/
Deans-MacBook-Pro:HelloCore deandavidson$ dotnet new
Created new C# project in /Users/deandavidson/Documents/Projects/HelloCore.
So, what just happened? Let’s take a look at project.json
:
{
"version": "1.0.0-*",
"buildOptions": {
"debugType": "portable",
"emitEntryPoint": true
},
"dependencies": {},
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
}
},
"imports": "dnxcore50"
}
}
}
As you can see, by default it imports dnxcore50
. Why? This allows us to reference pre-RC1 .NET Core Preview libraries. There is a lot of great info on Target Framework Monikers in this blog post. If you are unfamiliar with how .NET Core naming has changed, Hanselman does a really good job of explaining it here: ASP.NET 5 is dead - Introducing ASP.NET Core 1.0 and .NET Core 1.0 <- With a title like that, you basically don’t even need to READ the post.
In addition to project.json
, we also have an entry point in to our application in Program.cs
:
using System;
namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Next, let’s restore dependencies using dotnet restore
:
Deans-MacBook-Pro:HelloCore deandavidson$ dotnet restore
log : Restoring packages for /Users/deandavidson/Documents/Projects/HelloCore/project.json...
log : Lock file has not changed. Skipping lock file write. Path: /Users/deandavidson/Documents/Projects/HelloCore/project.lock.json
log : /Users/deandavidson/Documents/Projects/HelloCore/project.json
log : Restore completed in 939ms.
Deans-MacBook-Pro:HelloCore deandavidson$
And finally let’s run the app using dotnet run
:
It’s nice that our app runs but it doesn’t actually do anything. Let’s add the following dependencies to our project.json
file’s root-level dependencies property which was previously blank:
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.0",
"type": "platform"
},
"Microsoft.AspNetCore.Mvc": "1.0.0",
"Microsoft.AspNetCore.Diagnostics": "1.0.0",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
"Microsoft.AspNetCore.StaticFiles": "1.0.0",
"Microsoft.Extensions.Logging.Console": "1.0.0",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
"Microsoft.Extensions.Configuration.Json": "1.0.0"
}
Do a dotnet restore
to load in the new dependencies. For this app, I want to put my server-side code in a folder called Api
and my client-side code in a folder called Client
. So, first off: let’s create the Api
folder and add a Startup.cs
file with the following code:
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
namespace HelloCore.Api
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Set up our file server.
app.UseFileServer(new FileServerOptions()
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), @"Client")),
RequestPath = new PathString(""), // Our root path goes to the Client folder
EnableDirectoryBrowsing = false // Don't allow users to browse directories
});
app.UseStaticFiles();
app.UseMvc();
}
}
}
Our app isn’t using this startup file yet or doing anything really. So, let’s change that by modifying our Program.cs
file to configure and run our ASP.NET app:
using System.IO;
using Microsoft.AspNetCore.Hosting;
using HelloCore.Api;
namespace HelloCore
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel() // Since we are on Mac OS, we are using kestrel
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>() // Use our Startup class in /App/
.Build();
host.Run(); // Actually run the app
}
}
}
We can’t currently run the app because the Client
folder doesn’t exist so our file server doesn’t work. Let’s create the Client
folder and add Index.html
to it with the following content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hello Core</title>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7"
crossorigin="anonymous">
<!-- Bootstrap Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r"
crossorigin="anonymous">
<!-- Bootstrap JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS"
crossorigin="anonymous"></script>
<!-- jquery -->
<script src="https://code.jquery.com/jquery-3.1.0.min.js" integrity="sha256-cCueBR6CsyA4/9szpPfrX3s49M9vUU5BgtiJj06wt/s=" crossorigin="anonymous"></script>
<!-- Our main app javascript -->
<script src="/Scripts/App.js"></script>
</head>
<body>
<div class="container">
<h1>Hello Core!</h1>
<button id="btn-get-contacts" class="btn btn-primary">Get Contacts</button>
<div id="contacts"></div>
</div>
</body>
</html>
Note that this file references /Scripts/App.js
which doesn’t exist yet. Before we create our script, let’s create a route that it can call to get some contact data. Create a Controllers
folder under /Api/
and add ContactsController.cs
with the following self-deprecating code:
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
namespace HelloCore.Api.Controllers
{
[Route("api/[controller]")]
public class ContactsController : Controller
{
// Note: for the sake of demo brevity we are just creating an
// inner class but don't do this in the real world.
public class ContactModel
{
// Nobody gets middle names around these parts...
public string FirstName { get; set; }
public string LastName { get; set; }
}
// GET api/contacts
[HttpGet]
public List<ContactModel> Get()
{
// Our data access layer, seen here, is the height of elegance.
return new List<ContactModel>() {
new ContactModel() { FirstName = "Dean", LastName = "Davidson" },
new ContactModel() { FirstName = "Scott", LastName = "Gu" },
new ContactModel() { FirstName = "Other", LastName = "Scott" },
new ContactModel() { FirstName = "John", LastName = "Doe" }
};
}
}
}
Now that we have a api/contacts
route, we can add App.js
to /Client/Scripts/
(create the Scripts
folder):
$(document).ready(function() {
// When the user clicks the "get contacts" button, retrieve contacts from our web API and display them.
$('#btn-get-contacts').click(function() {
$.get('/api/contacts', function(data) {
// Note: don't really do this either. Use a templating engine or SPA framework
$('#contacts').html('<ul id="contacts-group" class="list-group"></ul>');
var contactsGroup = $('#contacts-group');
for (var i = 0; i < data.length; i++) {
contactsGroup.append('<li class="list-group-item"><span class="glyphicon glyphicon-user"></span> ' + data[i].firstName + ' ' + data[i].lastName + '</li>');
}
});
});
});
Now, if we do a dotnet run and navigate to http://localhost:5000/
, we should be able to press our button and get data back from our API:
Conclusion
This is a very basic example but it will familiarize you with the building blocks of an ASP.NET Core web app. You can download/fork the source for this post here. As usual, let me know if you have any questions and I will be happy to help.