ASP.NET Core, DevOps, DevTools, MVC

MSBuild… what? I just right-click on Publish

What is MSBuild? I had no idea. It has the word “build” in it, it must build something. Oh, I also remember that in the early stages of .NET Core there was a lot of discussion about project.json and the all DNX thing…. Then they decided to keep this MSBuild thingy. In the end we are all using Visual Studio and the only thing I needed to know is how to set up a Publish definition, right-click on publish and voila! My app was ready to ship. Untill…

While doing some DevOps work, I wanted to build the app only once but transform the config file multiple times based on the environment. So I started digging into this MSBuild. What is it? Here is Microsoft definition: MSBuild is the Microsoft Build Engine, a platform for building applications.
It really does a lot for our apps but all its work is hidden behind the “greatest GUI” of all time: Visual Studio.

I was going into this discovery with the idea that MSBuild would work like some of the other build process. Run a command with some flags and the build is done. Instead, the story is slightly different. It is a bit like the Angular CLI with schematics. You use schematics to define custom actions or re-define existing actions. In MSBuild you use a build file (xml format) to define the sequence of actions you need to do. That is what the .csproj are. Just build definitions. When you run MSBuild and point to a .sln file, it knows how to go through each .csproj and run the build processes as needed. Let’s see how it works and play with it.

Where is MSbuild in our machines?

The MSBuild command comes with Windows installation here: C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe based on the .NET Framework. But this version is kind of bare bone, it lucks a bunch of extensions you might need for application specific builds (like we.config transformation).

Visual Studio installation brings in a more complete version of it here: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe (actual location may vary based on your VS version and installation settings). This version includes a bunch of extensions saved in these folders:

First a bit of configuration:

Let’s start by making sure MSBuild work. Open PowerShell (or your command line of choice) and just run the MSBuild that comes with Windows with the -version flag:

As you can see all is good, MSBuild responded with its version. Now let’s see the version available with Visual Studio install:

Much newer version (obviously).

We will be using this version for the exercises below.

Hello World

This example comes from here here.

Go to your working directory of choice and create a new file named HelloWorld.build. I use VS Code but you can use your editor of choice. The content of this new file is:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0"  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="HelloWorld">
        <Message Text="Hello"></Message>
        <Message Text="World"></Message>
    </Target>
</Project>

MSBuild has 2 main concepts in executing instructions:

  • Target
  • Task

The first is a set of instructions/command to complete a larger unit of work. A task is the smallest unit of work, usually just one instruction. All instructions are wrapped in a Project tag. A target is called via a flag on the MSBuild invocation /t:TargetName (see below). In this case we just print on the console “Hello” and “World”. Let’s do it.

In PowerShell run this command:

& "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" HelloWorld.build /t:HelloWorld

And:

How about flow control and variables?

But of course they are possible. A new variable is just a custom xml tag and condition can be build in a PropertyGroup tag. Create another build definition file and call it example2.build with this content:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0"  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup Condition="'$(Name)' == ''">
        <OutMsg>Please let us know your name!</OutMsg>
    </PropertyGroup>
    <PropertyGroup Condition="'$(Name)' != ''">
        <OutMsg>Welcome $(Name)!</OutMsg>
    </PropertyGroup>
    <Target Name="Condition">
            <Message Text="$(OutMsg)"></Message>
    </Target>
</Project>

Then run it with:

& "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" example2.build /t:Condition /p:Name=""

And then assign your first name to Name:

& "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" example2.build /t:Condition /p:Name="Emanuele"

You know already the result:

How about that web.config transformation?

In this case, we will use a task (small unit of work) and it is a preexisting task that comes with the Visual Studio installation. So we must import such task. Here is the build definition:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0"  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Staging|AnyCPU'">
        <BuildConfig>Staging</BuildConfig>
    </PropertyGroup>
    <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
        <BuildConfig>Release</BuildConfig>
    </PropertyGroup>
    <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v15.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
    <Target Name="TransformWebConfig">
        <TransformXml Source="Configuration/Web.config"
                      Transform="Configuration/Web.$(BuildConfig).config"
                      Destination="Web.config"
                      StackTrace="true"/>
    </Target>
</Project>

I called this definition web_configs.build.

We then need the we.config files to be transformed. I created 3 files inside a folder called Configuration:

  • web.config
  • web.Staging.config
  • web.Release.config

Here is the xml in each file. Nothing new just regular web.config with environment transformations.

<!-- web.config -->
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <connectionStrings>
        <add name="entities" connectionString="Debug" />
    </connectionStrings>
</configuration>

<!-- web.Staging.config -->
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
    <connectionStrings>
        <add name="entities" connectionString="Staging" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
    </connectionStrings>
</configuration>

<!-- web.Release.config -->
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
    <connectionStrings>
        <add name="entities" connectionString="Release" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
    </connectionStrings>
</configuration>

Let’s now run this build definition with this command:

& "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" web_configs.build /t:TransformWebConfig /p:Platform=AnyCPU /p:Configuration=Staging

As expected a new transformed web.config file is created in the working directory:

With this expected content:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <connectionStrings>
        <add name="entities" connectionString="Staging" />
    </connectionStrings>
</configuration>

You can run the Release version of the above command, just change the Configuration property value.

Well, I started this little journey a bit worry about the complexity of MSBuild, but once I understood a couple of the basic concepts, it wasn’t that hard to use. Keep in mind that you can do a lot with MSBuild, from copying files around, to run node, npm or other command line scripts.

I would love to hear some interesting use of MSBuild you have done. Feel free to leave a comment below.

ASP.NET Core, MVC

From WCF to Secure ASP.NET Core

Here is the challenge: securing a system of WCF services with modern OAuth and OpenIDConnect.

The entire business logic of this solution is handled and served by WCFs.  Nothing wrong with it, but the security practices were a bit outdated and not up to standard.

Considering that years of features and logic were coded in those WCFs, the budget was limited and the need to upgrade to a secure and modern system became quickly very urgent, a rewrite of the entire solution was definitely not an option.  Therefore, I decided to place all the WCFs end-point behind a ASP.NET Core proxy that leverages the Identity Server 4 framework for authentication.

Another challenge I faced was that the entire solution was in VB and I am mainly a C# developer; additionally this is a VB shop that was a little reluctant in moving to C#.  But after long pondering and knowing that in the end VB and C# compile down to the same Microsoft Intermediate Lanaguage (MSIL) we decided to code this proxy in C# and eventually start porting some of these WCF functionality into a VB .NET Standard Class Library that plays well with C# projects anyway.

After doing some research I found several sources of information that I am sharing below. Definitely the most important and inspiring one was this video and blog of Shayne Boyer called ASP.NET Core: Getting clean with SOAPThese other sources might be helpful as well:

– Here is a way to securing existing WCFs and keep open the door for more modern WebApi systems. The problem is that is done with Identity Server 3 (older version): https://leastprivilege.com/2015/07/02/give-your-wcf-security-architecture-a-makeover-with-identityserver3/ Check also the link to Github samples.

– Here is an article on CORS for WCF:

https://blogs.msdn.microsoft.com/carlosfigueira/2012/05/14/implementing-cors-support-in-wcf/

The solution in question has a few different clients:  Javascript client (browser dashboard apps), mobile apps and server client.  IdentityServer 4 has a configuration for each of these case scenarios (or flows) and their Quickstart samples give you a very good idea on how to implement them.

The key here is the creation of the WCF client that from the ASP.NET Core WebApi calls the WCF services.  Shayne uses the “svcutil” (ServiceModel Metadata Utility Tool) to automatically generate the WCF client code.  But instead I decided to use this great Visual Studio extension that does just that but makes it easier to instantiate and use a WCF client. The extension is called  Microsoft WCF Web Service Reference Provider , once installed you just go to “Connected Services” in your solution explorer, input the endpoint url and the tool creates all the code you need.  Also, just recently this tool has been included in Visual Studio 2017 (version 15.5 and above), thus no need to install this extension anymore. One thing I must mention about this tool is that it work only with a SOAP endpoints, if the WCF has other type of endpoints (like REST) the tool will not be able to read the service metadata end will return an error. You can always add a SOAP endpoint to your WCF (hopefully you have access to it).

The extension creates a client that you can instantiate by passing the “EndpointConfiguration” type and use it to call each method exposed by the WCF (by default async). Here is how:

WCFclient client = new WCFclient(EndpointConfiguration.soap);
var result = await client.MyMethodAsync(args);

Now, one of the things that we need to make sure to do is to close and dispose of the WCF Client at the end of each request.  And here the ASP.NET Core Dependency Injection comes in handy.

The ASP.NET Core DI call the Dispose() method of the injected service if it implements the  IDisposable interface.  Our WCF client created with either the VS extension or the svcutil.exe utility does not implement IDisposable.  So we will wrap the client in a class and implement the interface ourselves. The WCF Client do expose the Close() and Abort() methods needed to dispose the service client (see below).

Be careful as the ASP.NET docs note the followings:

// container will create the instance(s) of these types and will dispose them
services.AddScoped<Service1>();
services.AddSingleton<Service2>();

// container did not create instance so it will NOT dispose it
services.AddSingleton<Service3>(new Service3());
services.AddSingleton(new Service3());

So if the DI creates the instance it will dispose of it (if it implements IDisposable) but if it doesn’t it will not dispose of it even if it implements IDisposable. So do not pass in an instantiated object as an argument, just let DI do the magic.

In wrapping the WCF client we want to maintain the possibility of changing the EndpointConfiguration as well as other configurations we might want to pass in in the future (like a different URL to point to if necessary).  Thus we inherit from the IWCFClient (the WCF interface implemented by the WCF and its client which is brought in by the VS extension above) and IDisposable and we pass in the configuration directly into “base” by way of the contructor:

public class WCFClientBySoap : IWCFClient, IDisposable
{
    public WCFClientBySoap() : base(EndpointConfiguration.soap)
    {
    }

    private bool disposedValue = false; 

    protected async virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                try
                {
                    if (State != System.ServiceModel.CommunicationState.Faulted)
                    {
                        await CloseAsync();
                    }
                }
                finally
                {
                    if (State != System.ServiceModel.CommunicationState.Closed)
                    {
                        Abort();
                    }
                }
            }

            disposedValue = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
    }
}

Here is a blog that talk about disposing of a WCF Client WCF Client Closal and Disposal

Now we just need to register this wrapper with the ASP.NET Core DI container.  Because we want to be able to change the wrapper in case of different configurations, we register the wrapper as a concrete class of the WCF interface.  In Startup.cs we add this line in the ConfigurationServices method:

services.AddScoped<IWCFClient, WCFClientBySoap>();

Finally we create a controller/action endpoint for each WCF endpoint leveraging the injected client:

[Produces("application/json")]
[Route("mycontroller")]
public class MyControllerController : Controller
{
    private IWCFClient _wcfclient;

    public MyControllerController(IWCFClient wcfclient)
    {
        _wcfclient = wcfclient;

    }

    [HttpGet("getMyData")]
    public async Task<IActionResult> GetMyData()
    {
        try
        {
            var result = await _wcfclient.getMyDataAsync();
            if (result == null)
            {
                return BadRequest("Couldn't get Data");
            }
            return new ObjectResult(result);
         }
         catch (Exception x)
         {
             return BadRequest(x.Message);
         }
     }
}

And this is all. It just works.

 

ASP.NET Core

ASP.NET Core 2 is here! Semi painless upgrade.

On August 14, 2017 the ASP.NET team announced the release of ASP.NET Core 2 (along with .NET Standard 2 and .NET Core 2).

As you might have noticed everybody jumped in and upgraded their projects or at least their pet/test projects to v2.0; naturally, I felt the same urge and I did so for my current project.  So this post is to chronicle my fairly painless upgrade with a little hiccup mainly due to the limited documentation.  Keep in mind that Core 2.0 is still in preview even though is a final preview and quite stable.

You can find all sorts of blogs and instruction on how to upgrade, like here or here  and here.  Following all this information I worked my way to 2.0 as follows.

Keep in mind that this is “my” journey and yours might be different. I updated a Web application on a Windows machine with Visual Studio 2017 Community edition.

1. Install .NET Core 2.0 SDK

You can find it here.  The process is very simple just follow the installer instructions.

2. Install Visual Studio 2017 preview version 15.3

You can find it here, actually it wasn’t as clear to me that this was still a preview version and I spent quite a bit of time trying to update my current version of VS 2017 to 15.3 without success (obviously!).  So, Core 2.0 is available with VS2017 in preview only.

Go ahead and install it, it can live side-by-side with your stable version of VS with no problems.

This new version of Visual Studio can automatically detect if a new SDK is available (you just install it in step 1) and will offer you the ability to target the new framework.  You can read more about it here.

3. Install the Microsoft.AspNetCore.All meta package

You can do so by way of Nuget with the dependency management tool offered by VS 2017:

image

Your project probably have several AspNet Core packages but after installing Microsoft.AspNetCore.All you can remove most of them as they are already included in the AspNet.All meta package.  You can find a list of the included packages here.

4. Target .Net Core 2 in all the projects in your solution

This is pretty easy, right click on the project and select properties, in the application dialog select ASP.NET Core 2 as target. This is for the web projects, if you have any other data or business project to support the web app, they should target .NET Standard 2.0:

Target Core 2.0.NET Standard 2.0

At this point all is set, rebuild the project and run it….. but there is always something not exactly right.  Here is what I found:

Content Error

After the steps taken above the first error was the following:

Duplicate ‘Content’ items were included. The .NET SDK includes ‘Content’ items from your project directory by default. You can either remove these items from your project file, or set the ‘EnableDefaultContentItems’ property to ‘false’ if you want to explicitly include them in your project file. For more information, see https://aka.ms/sdkimplicititems. The duplicate items were: ‘wwwroot\img\anonimus.png’

So I checked the .csproj file and it was listing several files and folders part of the content directory (wwwroot):

I thought that this was odd.  The project was created with VS 2017 which automatically detects files in your project folder, so there is no need to actually list them in the .csproj file.  Anyway, I deleted those entry, and the error above was gone.  Great!  The first one is done.

Identity Error (Cookies)

The next series of errors were all due to the cookies settings I had under Identity.  Here is what the error looked like:

IdentityCookies Error
Cookies does not exist under IdentityOptions

I spent quite a bit of time to figure this out.  Everything pointed to this article in the docs, but as the title says, it let you configure cookies in the case where you do not have Identity set up, instead I did have Identity set up.  So I went into the docs to see how to set up Identity with cookies, but that is old code that doesn’t work anymore with Core 2.0.  In fact the previous article about cookies without Identity is explicitly saying that cookies have been removed from Identity.

Finally this Stackoverflow answer pointed me to this Github announcement  from the ASP.NET team which in turns pointed me in the right direction here.  And finally I found the solution.  You can read the details in the post linked above, but now cookies has its own configuration extension to be placed in the ConfigureService method of the Startup class:

services.ConfigureApplicationCookie(conf =>
{
  conf.LoginPath = "/Auth/Login";
  conf.Events = new CookieAuthenticationEvents()
  {
    OnRedirectToLogin = async ctx =>
    {
      if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == 200)
      {
        ctx.Response.StatusCode = 401;
      }
      else
      {
        ctx.Response.Redirect(ctx.RedirectUri);
      }
      await Task.Yield();
    }
  };
});

For the rest of the Identity configuration (pasword, etc.) I moved it under Configue<IdentityService>:

services.Configure<IdentityOptions>(config =>
{
  config.User.RequireUniqueEmail = true;
  config.Password.RequiredLength = 8;
});

Now all works perfectly as before.

One last piece of the puzzle is UseIdentity()  in the Configure() method which is deprecated, as it was just calling UseCookie 4 times (as noted on the ASP.NET team post). Instead use UseAuthentication()  here is my Startup class (I removed unrelated code):

public class Startup
    {
        //Removed for brevity

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddIdentity<CCMUser, IdentityRole>()
            .AddEntityFrameworkStores<CCMContext>()
            .AddDefaultTokenProviders();

            services.AddMvc()
            .AddJsonOptions(opt =>
            {
                opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            });

            services.ConfigureApplicationCookie(conf =>
            {
                conf.LoginPath = "/Auth/Login";
                conf.Events = new CookieAuthenticationEvents()
                {
                    OnRedirectToLogin = async ctx =>
                    {
                        if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == 200)
                        {
                            ctx.Response.StatusCode = 401;
                        }
                        else
                        {
                            ctx.Response.Redirect(ctx.RedirectUri);
                        }
                        await Task.Yield();
                    }
                };
            });

            services.Configure<IdentityOptions>(config =>
            {
                config.User.RequireUniqueEmail = true;
                config.Password.RequiredLength = 8;
            });
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, CCMSeedData seeder, IHttpContextAccessor httpContext)
        {
            //Removed for brevity

            app.UseStaticFiles();

            app.UseAuthentication();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");

                routes.MapSpaFallbackRoute(
                    name: "spa-fallback",
                    defaults: new { controller = "Home", action = "Index" });
            });
        }
    }
}

And this is it. I hope this helps someone else like me that had a bit of hard times fixing the cookies setting issue with Identity.

MVC

Return URL with fragment in ASP.NET Core MVC

I am working on a web project based on Asp.Net MVC and Aurelia and I decided to structure it with part MVC and part Spa (Aurelia). The project also implements a basic authentication system (Asp.Net Core Identity) where if a non-authenticated user is trying to access a secure page it is redirected to the login page, which is standard procedure in these cases.

As you can see the return URL sent in with the query string includes a fragment, used by the Aurelia routing (or any spa framework routing you are using):

http://localhost:14500/Auth/Login?ReturnUrl=%2Fcamp%2F7#sessions

The problem is that the fragment  portion of the URL is never sent to the server and it is therefore ignored. So here is what happens:

The fragment of the URL is not being added to the action url of the form, so when you post the login form the server redirects to a URL without fragment and you are not able to get to the correct page handled by the spa router.

So, I included a little Javascript functions that picks up the return URL from the browser’s location.href and updates the form action with the correct return URL:

$(document).ready(function () {
        var form = $("form")[0];
        var hash = document.location.hash;
        if (hash) {
            if (form) {
                if (form.action) {
                    form.action = document.location.href;
                }
            }
        }
    });

Here is how the form action looks like after this code runs:

Now, when the user logs in and the server redirect to the originally requested URL, Aurelia router picks it up and renders the correct page.