The simplest ASP.NET Core app that'll serve static content
NOTE: This post is targeting .NET Core / ASP.NET Core 2.0, not 1.x
I have a confession to make, I've not spent much time or energy looking into the ins and outs of .NET Core, ASP.NET Core, Entity Framework Core and all the new ways of doing things that come with it. I do have a good reason though, aside from laziness,.. I was kinda waiting for product maturity / v2 before I dug into it in any great detail. Unsurprisingly it turned out that it was worth waiting a little while as some changes have filtered through like project.json (which essentially replaced pacakges.config) being superseded by PackageReference's and the new super-slimline .csproj format. The latter really is a boon, especially to anyone who's seen the mess that was/is .csproj files for prior project types. There's also the evolution of .NETStandard which means that it kinda feels like all the pieces are coming together in the right place now.
Creating the project - via the Command Line
Note: If you don't have it installed already, you'll need the .NET Core 2.0 SDK installed to be able to follow along. I've already got it installed, likely thanks to Visual Studio 2017, so won't be stepping through the process of installing it here.
One of the big things with .NET Core is the command line tooling support, so I thought I'd show the process I went though to create this project using that tooling. Once that's done I'm going to swap to Visual Studio 2017 for the editing experience but there's no real reason why you couldn't carry on using Notepad(++), Visual Studio Code or your other editor of choice. The principle with .NET Core is that it's truly multi-platform so everything you can do, you can do without Visual Studio.
So, step one, in PowerShell although you could use the Windows Command Prompt if you want - it makes no difference:
Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. PS C:\Users\robertwray> d: PS D:\> md BlogPostProject Directory: D:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 31/10/2017 12:59 BlogPostProject PS D:\>
I've created a folder for the project to live in - I've done this on my "D" drive because that's where I do this stuff. The folder you create could be anywhere, though do stick to somewhere that's properly under your control, i.e. don't try this in C:\Windows\System32, it may well work, but it's probably not a good idea!
The next thing to do is to use the "dotnet new" command to create a truly empty web project. This is a web project that contains no WebForms (it doesn't exist in ASP.NET Core so that's a bad example!), no MVC, no WebAPI, no Routing, no *nothing*. This can be achieved by running dotnet new and specifying web as the type of project to create / the template to run through. You can get a full list of all the templates/projects by running dotnet new without any other arguments. So, running dotnet new web gives us the following output:
PS D:\> cd .\BlogPostProject\ PS D:\BlogPostProject> dotnet new web The template "ASP.NET Core Empty" was created successfully. This template contains technologies from parties other than Microsoft, see https://aka.ms/template-3pn for details. Processing post-creation actions... Running 'dotnet restore' on D:\BlogPostProject\BlogPostProject.csproj... Restoring packages for D:\BlogPostProject\BlogPostProject.csproj... Generating MSBuild file D:\BlogPostProject\obj\BlogPostProject.csproj.nuget.g.props. Generating MSBuild file D:\BlogPostProject\obj\BlogPostProject.csproj.nuget.g.targets. Restore completed in 1.26 sec for D:\BlogPostProject\BlogPostProject.csproj. Restore succeeded.
Notice how I changed into the directory I created first, and then dotnet new has used that as the name for the project? Don't make the mistake of forgetting to do this and running dotnet new in another folder, like the root of drive D:, if you kinda happen to have one!
Before making any changes, it doesn't hurt to build and run the project, just to make sure it's all playing nicely, so call dotnet run and you should see something along the lines of this:
PS D:\BlogPostProject> dotnet run Hosting environment: Production Content root path: D:\BlogPostProject Now listening on: http://localhost:5000 Application started. Press Ctrl+C to shut down.
If you then copy the address (http://localhost:5000) from the console and browse to it, two things should happen:
- The browser should display the text "Hello World!"
- The console will spit out a couple of information lines to let you know it's responded to a request
Assuming they do, you're good to carry on!
Serving some static content
What you might notice is, no matter the URL you specify, and no matter what files you place in the wwwroot folder (as this is where ASP.NET Core serves content from, which is a bit different to the ASP.NET on .NET Framework way of things), you'll always get "HelloWorld!" back. Go on, try browsing to http://localhost:5000/badger/badger/cat/cat, yup, "HelloWorld!".
Because of the way things work in the "Core" world, you don't get something for nothing anymore. In order for something to happen, you've got to tell the runtime that it needs to happen. At the moment, every request is responded to with "Hello World!" because of these lines in Startup.cs:
app.Run(async (context) => { await context.Response.WriteAsync("Hello World!"); });
In order to get static content served up, we need to do something other than just return static text for every request that comes in, like, return a file. I'm going to move to Visual Studio now, just because it makes the process a little bit easier, but as I said before: you could do all of this via the command line. If your PC has Visual Studio 2017 installed and is configured similarly to mine, running the following should open Visual Studio:
PS D:\BlogPostProject> .\BlogPostProject.csproj
Press F5 and you should see your browser open and proclaim "Hello World!", you can look in the Output pane of Visual Studio to see something that's similar to what you saw when running the web application using dotnet run:
There's some extra noise in there, but the two lines that start Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: are pretty similar to the output seen when running in the console. Now lets drop some static content in to serve up, prove that it doesn't work because of the code in Startup.cs handling all requests by returning "Hello World!" and fix that.
Step 1 - Add some content. This is nice and easy, right-click on the wwwroot folder in Solution Explorer, choose Add, New Item... and then choose the HTML Page" option, giving the file a name of your choice, say "mypage.html" before clicking the "Add" button to create the file. I'm going to put an h1 in my mypage.html that says "This is my page" giving me a file that looks like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<h1>This is my page</h1>
</body>
</html>
Step 2 - Prove that we'll still only get the "Hello World!" content returned. To do this, hit F5. Your browser will load and it'll probably go to the root of your app. Tack "mypage.html" on the end, the app is still returning "Hello World!". That leads us onto the next step - getting "mypage.html" to be returned.
Step 3 - Rendering static content. This is actually really rather easy. Open up Startup.cs and replace the whole app.Run block with app.UseStaticFiles();. This one-liner pulls in an extension method from the assembly Microsoft.AspNetCore.StaticFiles that configures everything you need so that static files are processed. What you'll now see is that instead of getting "Hello World!" for the root of the app, you get your browsers 404 behaviour of choice
Edit the address bar to tack on "mypage.html" to the end, and re-load, et voila:
There's plenty more documentation out there, along with guides and tutorials that can show some of the other middleware that you can plug in here to do stuff other than simply return static content, indeed, this is how everything gets "bolted together" now. If you were to run dotnet new mvc to create a new MVC project on ASP.NET Core, you'd see that the generated Startup.cs would contain code that looks like this:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
There are a lot of "default" extension methods available that add functionality in, Intellisense in Visual Studio is a good way to discover some of them: