Run Umbraco Rest with Docker and Angular

Introduction 

 
Nowadays, we all see and hear tons of PoC and software solutions based on front-end frameworks, .NET backend, and containerization; with the spreading of .net core and its multi-platform nature, we have now opened a whole world of solutions that we couldn’t see before and that will speed up our developments and open us the possibility of developing better and faster web apps. I really love developing on macOS with Visual Studio Code, exploiting an API/backend solution with .NET Core and a front end made with Angular, putting all in a Docker container...fast, eventually scalable, light and friendly with all the most recent DevOps technologies.
 
But one of the biggest faults in Microsoft world is CMS. One of the best solutions by now is Umbraco CMS, which unfortunately in its last version (8) has not been ported to .NET Core yet.
 
So what could we do? Do we have to forget about Angular and containers since we are jailed inside Microsoft OS?
 
Of course not! In this guide, thanks to Docker Windows containers, I’ll show you how to make a little web page sample with Angular front end and data taken from the API layer served by Umbraco CMS, all developed and deployed inside a single Docker container.
 
This is the reason why we use a package plug-in for Umbraco called Umbraco HeadRest, developed by Matt Brailsford (https://github.com/mattbrailsford/umbraco-headrest).
 
Note that Umbraco has now a cloud solution like this - called Heartcore - but the HeadRest solution is definitely better for developing purposes and deep website customization since you have control over all features of the API layer.
 
 
Run Umbraco Rest With Docker And Angular
 
 
Let’s start!
 
Software and products needed on your dev machine,
  • Docker for Win
  • Visual Studio 2019 (for the .NET code)
  • Visual Studio Code (suggested for the Angular code but you can choose your favorite dev environment)
First, we need a database, the better choice for Umbraco is MSSQL; Umbraco could work with other DB engines too.
 
I suggest you Azure SQL database service DB, which is mostly the best solution but you can choose your own one. For example, keep the DB on the same container of the web app or put it in another separate container.
 
After you set up the DB, you can create a new Web Application directly from Visual Studio 2019. Enable the Docker support in order to debug and deploy the application inside a container.
 
Then, install Umbraco CMS from Nuget Package Manager (version used in this article is 8.4.0, but is already available v8.5.3 with some major changes). We also need the API layer package installed that will serve the Angular frontend, the “magic” one, so search for Our.Umbraco.HeadRest.
 
Run Umbraco Rest With Docker And Angular 
 
Then, let’s start the website inside the container to create some backend stuff.
 
I started creating a home page sample with a proper title and a first-level page sample with, again, a proper title.
 
Now, let's move to configuration to let HeadRest do its work. If you read the GitHub project page info you can understand it's mandatory, I’ll explain what I have done to set it up properly, in order to make the process easier.
 
We have to implement the Umbraco 8 Component and Composing method: essentially, these are the new ways that Umbraco gives us to register and configure components at the application startup.
 
For further info, read:
  • https://our.umbraco.com/documentation/implementation/composing/
  • https://creativewebspecialist.co.uk/2018/06/15/umbraco-v8-bye-bye-applicationeventhandler-hello-umbraco-components/
  • https://www.perplex.nl/en/blog/2019/umbraco-v8-changes-for-developers/
Basically, IComponent is an interface for code that should be initialized and terminated by Umbraco automatically when the application starts, the dependency injection stepson of ApplicationEventHandler.
 
So we configure our HeadRest like this:
  1.     public class HeadRestConfigComponent : IComponent  
  2.     {  
  3.         private readonly HeadRest _headRest;  
  4.   
  5.         public HeadRestConfigComponent(HeadRest headRest)  
  6.             => _headRest = headRest;  
  7.    
  8.         public void Initialize()  
  9.         {  
  10.   
  11.             // Configuration goes here  
  12.   
  13.             _headRest  
  14.              .ConfigureEndpoint("/api/""/root//homePage[1]"new HeadRestOptions  
  15.             {     
  16.                 //CustomRouteMappings = new HeadRestRouteMap()  
  17.                 //    .For("^/(.*)?$").MapTo("/"),  
  18.   
  19.                 ViewModelMappings = new HeadRestViewModelMap()  
  20.                 .For(HomePage.ModelTypeAlias).MapTo<HomePageViewModel>()  
  21.                 .For(FirstLevelPage.ModelTypeAlias).MapTo<FirstLevelPageViewModel>()  
  22.             });  
  23.         }  
  24.   
  25.         public void Terminate() { }  
  26.   
  27.     }  
We’ll have an API layer configured on the path/API of our web app, starting from the homepage node of the Umbraco tree; we also configure our view model map, which should be the view model that will be on our document type (HomePage and FirstLevelPage document types) and will be outputted in the API layer data.
 
I usually put all the “compiled” items (like controllers and the component initialization in this case) in an external project, to have cleaner code and a better solution organization.
 
The HomePage and FirstLevelPage classes have been automatically generated by Umbraco with the ModelsBuilder live DLL method, and the DLL has been imported in the external project.
 
Run Umbraco Rest With Docker And Angular
 
In order to let Umbraco pick up the component, it must be added to the Composition like this:
  1.     [ComposeAfter(typeof(HeadRestComposer))]  
  2.     public class HeadRestConfigComposer : IUserComposer  
  3.     {  
  4.         public void Compose(Composition composition)  
  5.         {  
  6.             composition.Components()  
  7.                 .Append<HeadRestConfigComponent>();  
  8.   
  9.             composition.WithCollectionBuilder<MapDefinitionCollectionBuilder>()  
  10.                 .Add<HeadRestMapDefinition>();  
  11.         }  
  12.   
  13.     }  
We also need to tell HeadRest how to map our view model classes with the document ones, and we use a map definition class to do it:
  1.     public class HeadRestMapDefinition : IMapDefinition  
  2.     {  
  3.         public void DefineMaps(UmbracoMapper mapper)  
  4.         {  
  5.             mapper.Define<HomePage, HomePageViewModel>                         (HomePageViewModelMapping.Instance.Map);  
  6.   
  7.             mapper.Define<FirstLevelPage, FirstLevelPageViewModel>  (FirstLevelPageViewModelMapping.Instance.Map);  
  8.   
  9.         }  
  10.   
  11.     }  
With the field specification mapping:
  1.     public class HomePageViewModelMapping : BaseMapping<HomePageViewModelMapping,  HomePage, HomePageViewModel>  
  2.     {  
  3.         public override void Map(HomePage src, HomePageViewModel dst, MapperContext ctx)  
  4.         {  
  5.             // Base mappings  
  6.             //BasePageViewModelMapper.Instance.Map(src, dst, ctx);  
  7.   
  8.             // Custom maps  
  9.             dst.Title = src.Title;  
  10.         }  
  11.     }  
The BaseMapping class defines a better way if we have some common fields in our document types:
  1.     public abstract class BaseMapping<TSelf, TFrom, TTo>  
  2.         where TSelf : BaseMapping<TSelf, TFrom, TTo>, new()  
  3.         where TTo : new()  
  4.     {  
  5.         private static readonly Lazy<TSelf> lazy = new Lazy<TSelf>(() => new TSelf());  
  6.         public static TSelf Instance { get { return lazy.Value; } }  
  7.   
  8.         protected BaseMapping()  
  9.         { }  
  10.   
  11.         public TTo Map(TFrom src, MapperContext ctx)  
  12.         {  
  13.             var dst = new TTo();  
  14.             Map(src, dst, ctx);  
  15.             return dst;  
  16.         }  
  17.   
  18.         public abstract void Map(TFrom src, TTo dst, MapperContext ctx);  
  19.     }  
Now we can test our API layer. If you launch the application and go to http://{container address}/api/ you should get a JSON response with your homepage property name and value.
 
That’s it, so let’s move to Angular!
 
Create and keep your working folder at the same level as the folder containing the website (I have a “front-end” folder on a par with the Umbraco web app). Then, using Visual Studio Code, install the basic Angular components in that folder with NPM and NG commands.
 
Create your needed Angular models based on the Umbraco document view models, set up the API URL endpoint to “/api” and try to output the title data in the home page.
 
Obviously, this is a very basic example, since it's not the main topic of this article.
 
Now we have to do three things:
  1. Let Visual Studio automatically compile and copy the Angular part from its folder to the website root
  2. Tell Umbraco to exclude our main index.html Angular page from its handling flow
  3. Tell our webserver to automatically land the web app on that page.
To accomplish the first task, we use the project pre-built events; since they will be executed every time the build is launched, we can insert the Angular compile and copy the part into that place.
 
Here's what I did:
  1. echo "cd $(SolutionDir)" &&^  
  2. cd "$(SolutionDir)\frontend" &&^  
  3.   
  4. echo "Building Project" &&^  
  5. ng build &&^  
  6.   
  7. echo 'copy files' &&^  
  8. xcopy "$(SolutionDir)\frontend\dist\frontend" "$(ProjectDir)" /Y  
Echos are for debugging purposes only.
 
I had some trouble with the NG command inside the event command line, so if Visual Studio does not recognize it, I advise you to go and check if it had been added to environment path variables. Restart the computer if necessary.
 
As you can see from the commands, we are placing the path inside the Angular part folder (frontend), then we launch the build for the Angular part that will place the results inside the dist\frontend folder, and copy all that files in the website root.
 
So, here we have our website with our pre-compiled Angular files all together but Umbraco still doesn’t know that it should not handle requests to our index page.
 
In order to avoid this, we have to add our needed ignore paths to the web.config file in the appSettings region. 
  1. <add key="Umbraco.Core.ReservedUrls" value="~/index.html,~/" /> 
Well done! If you type http://{container address}/index.html you can now see your Angular front-end page calling your API served by Umbraco!
 
Last thing: tell the web server that our default page must be the index one, so add this part too in the webconfig in the system.webServer region:
  1.       <defaultDocument>  
  2.         <files>  
  3.           <clear />  
  4.           <add value="index.html" />  
  5.         </files>  
  6.       </defaultDocument>  
Now, we can reach the index page simply typing http://{container address}/.
  
Brilliant! :D
 
You can go on using a lot of options offered by Angular: insert the container in a DevOps lifecycle to deploy it and so on...
 
If you want to add an authentication to your web app, as suggested by Matt Brailsford in his Umbraco HeadRest git repo, you can go for his oAuth2 solution called Umbraco authU https://github.com/mattbrailsford/umbraco-authu
 
Our.Umbraco.AuthU on nuget.
 
I hope that Umbraco will be ported to .NET Core as soon as possible; the project is going on and looking for contributors so finally we’ll have all the big advantages that .NET Core is giving us, cause Umbraco is undoubtedly one of the best and most consolidated CMS solutions in the .NET world.
 
Leave feedback if you want!


Similar Articles