Build Quick .Net Core API and Site on Mac with Command Line

I really like my Macbook Pro. I'm also a developer who uses .Net a lot. This creates a problem for me since the .Net ecosystem is primarily a Windows one.

Luckily, with the addition of .Net Core this is changing. .Net Core is open source, cross-platform, and supports many of the things us .Net developers are used to. I was wanting to test the cross-platform part so I set out to build a simple Asp.Net Core application using mostly command-line on OSX.

The idea

I threw together a quick idea. The idea is not really the point of the application, but you have to build something. I'm doing a lot of continuous integration with AppVeyor. Publishing to Azure Websites requires a WebDeploy section that is built from an Azure publishprofile file. We have lots of projects so configuring the WebDeploy is always annoying.

The app will take a publishprofile from Azure (pasted in) and convert it to a WebDeploy section of appveyor.yml. If you don't understand what the task is, don't worry. What it's doing is less important to how we are doing it. Let's just build a core app.

Getting started

As an exercise, I'm doing the majority of this via command-line. We'll start by opening a terminal and creating the project directory.

mkdir publishprofile-webdeploy && cd publishprofile-webdeploy
mkdir src && cd src

Let's create a solution. This isn't required, but I like to keep consistency with project structure and naming.

dotnet new sln --name Cyberkruz.PublishProfile

Now it's time for the web project. We'll create it and add it to the solution.

dotnet new mvc --name Cyberkruz.PublishProfile.Web
dotnet sln Cyberkruz.PublishProfile.sln add Cyberkruz.PublishProfile.Web/Cyberkruz.PublishProfile.Web.csproj

A good project can turn great with a few tests. I'll use XUnit as the unit testing framework. Create the test project, add it to the solution, and add a reference to the web project.

dotnet new xunit --name Cyberkruz.PublishProfile.Web.Tests
dotnet sln Cyberkruz.PublishProfile.sln add Cyberkruz.PublishProfile.Web.Tests/Cyberkruz.PublishProfile.Web.Tests.csproj
dotnet add Cyberkruz.PublishProfile.Web.Tests/Cyberkruz.PublishProfile.Web.Test.csproj reference Cyberkruz.PublishProfile.Web/Cyberkruz.PublishProfile.Web.csproj

Great! At this point, we have a .Net Core solution with an Asp.Net MVC web project, a unit test project, and the references added. We can now restore required packages for the solution.

dotnet restore src/

We can also run the tests.

cd src/Cyberkruz.PublishProfile.Web.Tests/
dotnet test

Or run the application.

cd ../Cyberkruz.PublishProfile.Web
dotnet run

If you make some css changes and notice that the website doesn't update the css when you dotnet run then you will need to add another dependency to the project

dotnet add package BuildBuilderMinifier
dotnet restore
dotnet build

You should be able to navigate to http://localhost:5000 and see the application. Hurray!

Code the server

At this point, there's now a working solution that builds and runs all via command line. I'll explain a little about what I did next, but the full source code is listed at the bottom of the article.

The structure for this is pretty simple. Create a class that can do the conversion, expose the class via an API endpoint, and then use the frontend interface to hit the API endpoint. I created a Models folder in the web project and added a WebDeploy class with some methods on it.

using System;
using System.Text;
using System.Xml.Linq;
using System.Linq;

namespace Cyberkruz.PublishProfile.Web.Models
{
    public class WebDeploy
    {
        public string Server { get; set; }

        public string Website { get; set; }

        public string Username { get; set; }

        public string Password { get; set; }

        public string Yml
        {
            get { return this.ToYml(); }
        }

        public static WebDeploy FromPublishProfile(string profile)
        {
            return null;
        }

        private static string ParseServer(XElement e)
        {
            return null;
        }

        public string ToYml()
        {
            return null;
        }
    }
}

Then I created the same models folder in the test project with a WebDeployTests class in order to test my conversions.

using System;
using Xunit;
using Cyberkruz.PublishProfile.Web.Models;
using System.Text;

namespace Cyberkruz.PublishProfile.Web.Models.Tests
{
    public class WebDeployTests
    {
        [Fact]
        public void FromPublishProfile_NullXml_ThrowsException()
        {
            // ... left out for brevity
        }

        [Fact]
        public void FromPublishProfile_ValidXml_ReturnsObject()
        {
            // ... left out for brevity
        }

        [Fact]
        public void FromPublishProfile_ToYml_ReturnsYml()
        {
            // ... left out for brevity
        }
    }
}

With that done I implemented the WebDeploy class methods so that I had working conversions.

Web API

With Core, both MVC view controllers and API controllers inherit from the Controller class. With that said I added another controller called WebDeployController and called my conversion methods.

using System;
using Cyberkruz.PublishProfile.Web.Models;
using Microsoft.AspNetCore.Mvc;

namespace Cyberkruz.PublishProfile.Web.Controllers
{
    [Route("api/webdeploy")]
    public class WebDeployController : Controller
    {
        [HttpPost]
        public IActionResult Index(string model)
        {
            WebDeploy result = null;

            try
            {
                result = WebDeploy.FromPublishProfile(model);
            }
            catch(Exception)
            {
                return BadRequest();
            }

            return Ok(result);
        }
    }
}

It is very rudimentary, but it is just for demo purposes. The only other thing I did was remove some unnecessary routes and views created by the template.

Code the client

The default web project comes with bootstrap and jquery, so I didn't spend the time to bring in Angular, CSS and JS packaging, etc. You're provided with a site.js file so I just threw in some jQuery to hit the API method. Something like below.

$.ajax({
  url: '/api/webdeploy',
  type: 'POST',
  dataType: 'json',
  data: $form.serialize(),
  success: success,
  error: error,
})

From there I just changed the site.css file up a little so it didn't look exactly like bootstrap, cleaned up some images, and it's good to go. Getting a .Net site up and going on Mac isn't a lot of trouble it just has a bit of a learning curve. Hopefully, this can help someone get started.