<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
	<channel>
		<title>Miroslav Popovic</title>
		<link>https://miroslavpopovic.com/</link>
		<description>Senior software architect and .NET developer</description>
		<copyright>2025</copyright>
		<pubDate>Mon, 12 May 2025 03:44:49 GMT</pubDate>
		<lastBuildDate>Mon, 12 May 2025 03:44:49 GMT</lastBuildDate>
		<item>
			<title>ASP.NET Core 3.0 Workshop</title>
			<link>https://miroslavpopovic.com/posts/2019/07/aspnetcore-30-workshop</link>
			<description>&lt;p&gt;&lt;img src="/images/2019-07/workshop-cover.png" class="img-fluid" alt="ASP.NET Core 3.0 Workshop"&gt;&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2019/07/aspnetcore-30-workshop</guid>
			<pubDate>Fri, 26 Jul 2019 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;&lt;img src="/images/2019-07/workshop-cover.png" class="img-fluid" alt="ASP.NET Core 3.0 Workshop" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE (September 29th):&lt;/strong&gt; The workshop sample and materials have been updated to the final version of .NET Core 3.0. All the missing pieces are implemented.&lt;/p&gt;
&lt;h2 id="workshop-content"&gt;Workshop content&lt;/h2&gt;
&lt;ol start="0"&gt;
&lt;li&gt;&lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/tree/master/docs/00-prerequisites.md"&gt;Session #0&lt;/a&gt; - Prepare your environment by installing and configuring prerequisites&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/tree/master/docs/01-introduction.md"&gt;Session #1&lt;/a&gt; - An introduction to .NET Core and ASP.NET Core in form of presentations&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/tree/master/docs/02-tools-and-templates.md"&gt;Session #2&lt;/a&gt; - Working with .NET Core CLI tools and Visual Studio 2019, analyzing project templates&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/tree/master/docs/03-choosing-a-domain.md"&gt;Session #3&lt;/a&gt; - Choosing a domain to develop and creating user stories for it&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/tree/master/docs/04-project-initialization.md"&gt;Session #4&lt;/a&gt; - REST API introduction, creating Web API project, introduction to Postman and configuring git&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/tree/master/docs/05-domain-models-and-database.md"&gt;Session #5&lt;/a&gt; - Creating domain models, EF Core context and migrations&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/tree/master/docs/06-controllers-and-actions.md"&gt;Session #6&lt;/a&gt; - Creating view models and controllers, validation, error handling, using Postman&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/tree/master/docs/07-securing-api.md"&gt;Session #7&lt;/a&gt; - Securing API with JWT token based auth&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/tree/master/docs/08-testing-and-documentation.md"&gt;Session #8&lt;/a&gt; - Creating unit and integration tests, adding Swagger support&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/tree/master/docs/09-versioning-limiting-monitoring.md"&gt;Session #9&lt;/a&gt; - Versioning APIs, usage limiting, monitoring and creating health checks&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/tree/master/docs/10-blazor-client.md"&gt;Session #10&lt;/a&gt; - Introduction to Blazor and creating client-side Blazor client&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/tree/master/docs/99-resources.md"&gt;Session #99&lt;/a&gt; - Resources and further reading&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;For quite some time, I was thinking about creating a workshop for ASP.NET Core. After some talks I had with members of &lt;a href="https://www.facebook.com/eestec.lcbanjaluka/"&gt;EESTEC&lt;/a&gt; student organization and the management of &lt;a href="https://seavus.com/"&gt;Seavus&lt;/a&gt;, the idea started to shape up. My plan was to build something that could be reused for various events - for community organized events, inside the company, for conferences, and of course to be usable for others too.&lt;/p&gt;
&lt;p&gt;After almost two months of sleeping less, preparing samples, writing instructions and having a first live workshop with students in duration of 10 days, there's finally something to share. The current version of workshop, with sample project and instructions, is available &lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop"&gt;on GitHub&lt;/a&gt;. I was heavily influenced by .NET Foundation's &lt;a href="https://presentations.dotnetfoundation.org/"&gt;presentations and workshops&lt;/a&gt; and I even took some of the materials from there - parts of presentation, presentation template and the cover image for the workshop. Why not, &lt;a href="https://www.meetup.com/BLbitUG/"&gt;my user group&lt;/a&gt; and myself are members of .NET Foundation organization. You &lt;a href="https://dotnetfoundation.org/get-involved"&gt;could be too&lt;/a&gt;! :)&lt;/p&gt;
&lt;p&gt;So, what is this workshop about? Other than ASP.NET Core 3.0 of course. Well, at the end, I have decided to go with Web API route. After the introduction to .NET Core 3.0 and ASP.NET Core and looking through the available templates, together with attendees, a domain is chosen and implemented as REST Web API. That API is then improved and finally consumed by frontend SPA application. To continue at the bleeding edge, we are not using JavaScript SPA frameworks like &lt;a href="https://aurelia.io/"&gt;Aurelia&lt;/a&gt; (the best one out there ;)), React, Vue or Angular, but a completely new thing called &lt;a href="https://dotnet.microsoft.com/apps/aspnet/web-apps/client"&gt;Blazor&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;The workshop itself is split into 10 parts (for now). Each part has it's &lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/tree/master/docs"&gt;own document with instructions&lt;/a&gt;. There is a &lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/blob/master/docs/00-prerequisites.md"&gt;prerequisites&lt;/a&gt; document that you can use to prepare your environment. If you want to present it to others, there's a &lt;a href="https://github.com/miroslavpopovic/aspnetcore-workshop/blob/master/docs/speaker-notes.md"&gt;speaker notes&lt;/a&gt; document to remind you what should you go through during each part.&lt;/p&gt;
&lt;p&gt;Note that both ASP.NET Core 3.0 and client-side Blazor are at the moment in preview phase. .NET Core 3.0 will launch in September during &lt;a href="https://www.dotnetconf.net/"&gt;.NET Conf&lt;/a&gt; and client-side Blazor sometime next year. The workshop is updated to Preview 7, which &lt;a href="https://devblogs.microsoft.com/dotnet/announcing-net-core-3-0-preview-7/"&gt;came out&lt;/a&gt; a few days ago and has a go-live license. There are some things in workshop that could not be achieved with the current version. I still consider the workshop a work in progress, until the final versions of .NET Core 3.0 and client-side Blazor are out and the workshop updated.&lt;/p&gt;
&lt;p&gt;You are welcome to use the workshop as you see fit. You can follow it by yourself, using the provided instructions. Or you could present the workshop to your friends, community or company. You could also create a derived version of workshop from it - in which case I would be grateful for an attribution :). I hope it will be useful.&lt;/p&gt;
&lt;p&gt;I'm also interested in presenting the workshop as well as &lt;a href="https://miroslavpopovic.com/speaking"&gt;giving regular talks&lt;/a&gt;, so if you have something in mind, like a community event, conference, etc., let me know.&lt;/p&gt;
&lt;h2 id="live-workshop"&gt;Live workshop&lt;/h2&gt;
&lt;p&gt;At the end, here are some pictures from the live workshop we had in Banja Luka. Thanks &lt;a href="https://www.facebook.com/eestec.lcbanjaluka/"&gt;EESTEC&lt;/a&gt;, &lt;a href="https://seavus.com/"&gt;Seavus&lt;/a&gt;, &lt;a href="https://www.meetup.com/BLbitUG/"&gt;BLbit&lt;/a&gt;, &lt;a href="http://bit-alliance.ba/"&gt;Bit Alliance&lt;/a&gt; and &lt;a href="https://etf.unibl.org/index.php/en/home"&gt;ETFBL&lt;/a&gt; for the help in organizing the event! And of course, thanks to all attendees, you were great!&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/2019-07/workshop-1.jpg" class="img-fluid" alt="ASP.NET Core 3.0 Workshop, Banja Luka" /&gt;
&lt;img src="/images/2019-07/workshop-2.jpg" class="img-fluid" alt="ASP.NET Core 3.0 Workshop, Banja Luka" /&gt;
&lt;img src="/images/2019-07/workshop-3.jpg" class="img-fluid" alt="ASP.NET Core 3.0 Workshop, Banja Luka" /&gt;
&lt;img src="/images/2019-07/workshop-4.jpg" class="img-fluid" alt="ASP.NET Core 3.0 Workshop, Banja Luka" /&gt;
&lt;img src="/images/2019-07/workshop-5.jpg" class="img-fluid" alt="ASP.NET Core 3.0 Workshop, Banja Luka" /&gt;&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>2018 in review</title>
			<link>https://miroslavpopovic.com/posts/2019/02/2018-in-review</link>
			<description>&lt;p&gt;&lt;img src="/images/2019-02/community-2018.png" class="img-fluid" alt="Community activities in 2018"&gt;&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2019/02/2018-in-review</guid>
			<pubDate>Fri, 08 Feb 2019 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;&lt;img src="/images/2019-02/community-2018.png" class="img-fluid" alt="Community activities in 2018" /&gt;&lt;/p&gt;
&lt;p&gt;This is (a bit late) review of the activities I had in 2018., mostly for archival purpose, since I tend to forget things.&lt;/p&gt;
&lt;h2 id="seavus"&gt;Seavus&lt;/h2&gt;
&lt;p&gt;I started to work for &lt;a href="https://seavus.com"&gt;Seavus&lt;/a&gt; in September 2017., so 2018. was my first full year there. Some time was necessary to adopt to the new way of working. Before Seavus, I was doing remote / freelance work. Switching to regular working hours, with no overtime and staying up late took a while. As always, there are pros and cons. The biggest pro in my opinion is with social aspect. Seavus managed to attract a huge number of great people (900+ of employees at the moment) and I consider that as its greatest strength. Most of the cons that I have are related to the difference between the work I had before. None of them are really big, and things are improving along the way.&lt;/p&gt;
&lt;p&gt;Seavus Banja Luka office started growing nicely. Btw we &lt;a href="https://career.seavus.com/"&gt;are hiring&lt;/a&gt; :). At the end of 2018. I was offered a promotion to Technical Lead with additional assignments - basically to be a face of Seavus in Bosnia and Herzegovina. We'll see how that goes :).&lt;/p&gt;
&lt;h2 id="conferences-and-other-community-events"&gt;Conferences and other community events&lt;/h2&gt;
&lt;p&gt;&lt;img src="/images/2019-02/miroslav-popovic-mscommunity-2018.jpg" class="img-fluid" alt="MSCommunity 2018" /&gt;&lt;/p&gt;
&lt;p&gt;Continuing from 2017., the last year was also pretty busy regarding community events. Here are the talks I had:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.facebook.com/events/393720467707508/"&gt;January 25th, BLbit meetup&lt;/a&gt; - Introduction to ASP.NET Core 2.0 talk&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.facebook.com/events/1629179527151548/"&gt;February 6th, Seavus CodeTalks, Novi Sad&lt;/a&gt; - Building production-ready APIs with ASP.NET Core 2.0&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.meetup.com/MSDEV-Serbia-User-Group/events/247513563/"&gt;February 7th, MSDEV Serbia meetup, Belgrade&lt;/a&gt; - Introduction to ASP.NET Core 2.0 and Building production-ready APIs with ASP.NET Core 2.0 talks&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.networkkonferencija.ba/"&gt;April 19th, NetWork conference, Neum&lt;/a&gt; - Docker and ASP.NET Core talk&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.windays.hr/raspored/arhiva/2018/building_production_ready_apis/4249"&gt;April 27th, WinDays conference, Porec&lt;/a&gt; - Building production-ready APIs with ASP.NET Core 2.1 talk&lt;/li&gt;
&lt;li&gt;&lt;a href="https://weblica.hr/"&gt;May 12th, Weblica conference, Cakovec&lt;/a&gt; - Building production-ready APIs with ASP.NET Core talk&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codetalks.seavus.com/seavus-codetalks-in-kragujevac-17052018"&gt;May 17th, Seavus CodeTalks, Kragujevac&lt;/a&gt; - Docker and ASP.NET Core 2.1 talk&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.kulendayz.com/"&gt;September 1st, KulenDayz conference, Osijek&lt;/a&gt; - Docker and ASP.NET Core talk&lt;/li&gt;
&lt;li&gt;&lt;a href="https://conference.mscommunity.ba/"&gt;September 14th, MSCommunity conference, Sarajevo&lt;/a&gt; - Visual Studio productivity talk&lt;/li&gt;
&lt;li&gt;&lt;a href="http://criscon.krizevci.hr/"&gt;September 22nd, CrisCon conference, Krizevci&lt;/a&gt; - Docker and ASP.NET Core talk&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.sinergija18.rs/"&gt;October 25th, Sinergija conference, Belgrade&lt;/a&gt; - Building production-ready APIs with ASP.NET Core 2.x talk&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.meetup.com/dotnet-amsterdam/events/255737023/"&gt;November 1st, DotNet Amsterdam meetup, Amsterdam&lt;/a&gt; - Building production-ready APIs with ASP.NET Core 2.2 talk&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.sqlsaturday.com/791/EventHome.aspx"&gt;November 3rd, SQLSaturday event, Banja Luka&lt;/a&gt; - Building production-ready APIs with ASP.NET Core 2.2 talk&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codetalks.seavus.com/seavus-codetalks-in-belgrade-13112018"&gt;November 13th, Seavus CodeTalks, Belgrade&lt;/a&gt; - Be productive with Visual Studio talk&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codetalks.seavus.com/seavus-codetalks-in-banja-luka-15112018"&gt;November 15th, Seavus CodeTalks, Banja Luka&lt;/a&gt; - Be productive with Visual Studio talk&lt;/li&gt;
&lt;li&gt;&lt;a href="https://advtechdays.com/"&gt;December 6th, Advanced Technology Days conference, Zagreb&lt;/a&gt; - Migration from ASP.NET MVC to ASP.NET Core 2.x talk&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The DotNet Amsterdam meetup was recorded. You can check it out &lt;a href="https://youtu.be/XLHeJgWovI4"&gt;on YouTube&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now that I see all of it in a list, it's a lot :). Not sure if I'll be able to keep that pace in 2019.&lt;/p&gt;
&lt;h2 id="blbit-user-group"&gt;BLbit user group&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.facebook.com/groups/blbit/"&gt;BLbit&lt;/a&gt; continues to be most active &lt;a href="https://www.facebook.com/groups/mscommunitybih/"&gt;MSCommunity&lt;/a&gt; user group in Bosnia and Herzegovina by the &lt;a href="https://www.facebook.com/groups/blbit/events/"&gt;number of meetups&lt;/a&gt;. One of highlights of the last year was a participation in global &lt;a href="https://www.dotnetconf.net/"&gt;.NET Conf&lt;/a&gt; event - with a conference watch party and an additional meetup. We had swags too! :)&lt;/p&gt;
&lt;p&gt;Last year we have also started to use &lt;a href="https://www.meetup.com/BLbitUG/"&gt;Meetup platform&lt;/a&gt; and became members of &lt;a href="https://www.meetup.com/pro/dotnet/"&gt;.NET Foundation&lt;/a&gt;. Recently, we have also created a &lt;a href="https://twitter.com/blbitug"&gt;Twitter account&lt;/a&gt; and we are still on &lt;a href="https://www.eventbrite.com/o/korisnicka-grupa-blbit-banja-luka-3287790552"&gt;Eventbrite too&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We also got a new sponsor - &lt;a href="https://seavus.com"&gt;Seavus&lt;/a&gt;, in addition to &lt;a href="https://www.facebook.com/groups/mscommunitybih/"&gt;MSCommunity&lt;/a&gt; and institutions providing us with the place to held our meetups - &lt;a href="http://icbl.ba/"&gt;ICBL&lt;/a&gt;, &lt;a href="https://www.blc.edu.ba/en/"&gt;BLC&lt;/a&gt;, &lt;a href="https://qlab.space/"&gt;Qlab&lt;/a&gt; and &lt;a href="http://www.smartoffice.ba/"&gt;Smart Office&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hopefully, we will able to keep going strong this year too.&lt;/p&gt;
&lt;h2 id="plans-for-2019"&gt;Plans for 2019&lt;/h2&gt;
&lt;p&gt;Granted, there will be more responsibilities in Seavus, with the promotion, but I don't plan to neglect the community engagement. Might tune down a bit with the number of community events, but will try to be more active with my blog (yeah, not the first time I planned that) and open-source contributions.&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>ASP.NET Core Identity and Aurelia</title>
			<link>https://miroslavpopovic.com/posts/2018/07/aspnet-core-identity-and-aurelia</link>
			<description>&lt;p&gt;&lt;img src="/images/2018-07/aspnetcore-aurelia.png" class="img-fluid" alt="ASP.NET Core Identity and Aurelia"&gt;&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2018/07/aspnet-core-identity-and-aurelia</guid>
			<pubDate>Sat, 14 Jul 2018 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;&lt;img src="/images/2018-07/aspnetcore-aurelia.png" class="img-fluid" alt="ASP.NET Core Identity and Aurelia" /&gt;&lt;/p&gt;
&lt;p&gt;This blog post is a response to a &lt;a href="https://stackoverflow.com/q/51279092/119230"&gt;StackOverflow question&lt;/a&gt;. Andreas wanted to know how to add ASP.NET Core Identity to an existing Aurelia project. The answer is of course: &lt;em&gt;it depends&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If it's really an existing application but without any authentication support or even without database access, then adding authentication and Identity might prove a bit difficult if you are not familiar enough with ASP.NET Core. At least it was like that before ASP.NET Core 2.1 which brought us Identity UI library and Identity Scaffolding features, among other things.&lt;/p&gt;
&lt;p&gt;Identity UI library is implemented as a &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/razor-pages/ui-class?view=aspnetcore-2.1&amp;amp;tabs=visual-studio"&gt;Razor Class Library&lt;/a&gt;, another new feature in 2.1 which enables Razor views and pages to be embedded into class libraries. This was a perfect fit for Identity, so when you create a new ASP.NET Core 2.1 project with Identity enabled, you won't get any Account or Manage views, nor their models. Everything is embedded into the new &lt;code&gt;Microsoft.AspNetCore.Identity.UI&lt;/code&gt; assembly/NuGet package.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-2.1&amp;amp;tabs=visual-studio"&gt;Identity Scaffolding&lt;/a&gt; is a new feature that can be used from within Visual Studio or from .NET Core CLI. It's basically a code generator that will generate overrides for any (or all) Identity Razor views,  partials or necessary scripts. &lt;a href="https://twitter.com/talkingdotnet"&gt;Talking Dotnet&lt;/a&gt; has &lt;a href="http://www.talkingdotnet.com/how-to-scaffold-identity-ui-in-asp-net-core-2-1/"&gt;a good overview&lt;/a&gt; of Identity UI Scaffold.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/2018-07/aspnet-core-identity-scaffold.png" class="img-fluid" alt="Identity Scaffold" /&gt;&lt;/p&gt;
&lt;p&gt;So, this could be the answer to the question? Well, not quite. It will work only on ASP.NET Core 2.1 and above. If the project was created using &lt;a href="https://aurelia.io/docs/build-systems/aurelia-cli"&gt;Aurelia CLI&lt;/a&gt;, it would be on ASP.NET Core 2.0. In that case, you first need &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/migration/20_21?view=aspnetcore-2.1"&gt;to upgrade&lt;/a&gt; to ASP.NET 2.1.&lt;/p&gt;
&lt;p&gt;All of this is for an existing project, but if you are creating a new one, things might be a bit easier.&lt;/p&gt;
&lt;h2 id="creating-a-new-asp.net-core-project-with-aurelia-and-identity-enabled"&gt;Creating a new ASP.NET Core project with Aurelia and Identity enabled&lt;/h2&gt;
&lt;p&gt;ASP.NET Core 2.1 also has a new SPA project templates that are more in line with community usage of client-side frameworks. I.e. the new template for React is using Create React App and the new template for Angular is using Angular CLI. However, there is no template for Aurelia. &lt;a href="https://github.com/aniel"&gt;Neils Morf&lt;/a&gt; has a solution for this in his &lt;a href="https://aniel.info/posts/2018-02-25-aurelia-aspnetcore-spa-template"&gt;blog post&lt;/a&gt;. He basically creates a new project using the Angular template and then modifies few things to run Aurelia instead. Note that you won't need to install the new SPA templates since they come with ASP.NET Core 2.1 (his blog post was written prior to 2.1 release). After that, Identity Scaffold could be used to add Identity to it.&lt;/p&gt;
&lt;p&gt;Another approach would be to create a new ASP.NET Core 2.1 project with Identity enabled and then add Aurelia to it. This is what we will do here.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/2018-07/aspnet-core-identity-new-project.png" class="img-fluid" alt="New ASP.NET Core 2.1 project" /&gt;&lt;/p&gt;
&lt;p&gt;Now create a new Aurelia app in another folder using Aurelia CLI. We don't want the CLI to overwrite files in our ASP.NET Core project. We'll copy and modify files we need manually. Make sure you select TypeScript (that's what OP was using) and ASP.NET Core in options.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; npm install aurelia-cli -g
&amp;gt; au new
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copy the following folders from the generated Aurelia app to the previously generated ASP.NET Core app:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;aurelia_project&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the following files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;index.ejs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;package-scripts.js&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tsconfig.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tslint.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;webpack.config.js&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;webpack.netcore.config.js&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After these files are copied, we can run &lt;code&gt;npm install&lt;/code&gt; to install all the required &lt;code&gt;npm&lt;/code&gt; packages.&lt;/p&gt;
&lt;p&gt;Before we start modifying files, we need to figure out what we are trying to achieve. There are (at least) three ways we can add authentication, depending on how far we want to go down the rabbit hole:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use the default implementation of Identity and protect access to Aurelia app.&lt;/li&gt;
&lt;li&gt;Reimplement the whole ASP.NET Core Identity UI in Aurelia.&lt;/li&gt;
&lt;li&gt;Have something in between, like have login only implemented in Aurelia and the rest (registration, forgot password, management, etc.) implemented in MVC.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We will go with option #1, as that is the easiest route. We'll leave the default Identity implementation intact (login, register, forget password) and serve Aurelia app from the main route (&lt;code&gt;/Home/Index&lt;/code&gt;). &lt;code&gt;[Authorize]&lt;/code&gt; attribute will be added to &lt;code&gt;HomeController&lt;/code&gt; in order to force the user to log in before accessing Aurelia app.&lt;/p&gt;
&lt;p&gt;Note that this option will be viable only if we host the Aurelia app within the same ASP.NET Core app. If we had a separate project for it and a separate URL for hosting the client-side Aurelia app, we would probably go with option #2 or #3.&lt;/p&gt;
&lt;p&gt;Option #2 is not easy to implement since Identity has a lot of different functionality. That could be a good topic for another blog post. For simple apps, option #1 works pretty well.&lt;/p&gt;
&lt;p&gt;Let's start modifying files. First, we'll replace &lt;code&gt;Views/Home/Index.cshtml&lt;/code&gt; content in ASP.NET Core app with a content from &lt;code&gt;Views/Shared/_Layout.cshtml&lt;/code&gt; of Aurelia app and then define the body and scripts. The end result will be this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;#64;{
    Layout = null;
    ViewData[&amp;quot;Title&amp;quot;] = &amp;quot;Home page&amp;quot;;
}
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot; /&amp;gt;
    &amp;lt;title&amp;gt;&amp;#64;ViewData[&amp;quot;Title&amp;quot;] - Aurelia&amp;lt;/title&amp;gt;

    &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;~/dist/vendor.css&amp;quot; asp-append-version=&amp;quot;true&amp;quot; /&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div aurelia-app=&amp;quot;main&amp;quot;&amp;gt;Loading...&amp;lt;/div&amp;gt;

    &amp;lt;environment names=&amp;quot;Development&amp;quot;&amp;gt;
        &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;~/dist/vendor.bundle.js&amp;quot; asp-append-version=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;~/dist/app.bundle.js&amp;quot; asp-append-version=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/environment&amp;gt;
    &amp;lt;environment names=&amp;quot;Production&amp;quot;&amp;gt;
        &amp;lt;script type=&amp;quot;text/javascript&amp;quot; asp-src-include=&amp;quot;~/dist/common.*.bundle.js&amp;quot; asp-append-version=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/environment&amp;gt;
    &amp;lt;environment names=&amp;quot;Staging, Production&amp;quot;&amp;gt;
        &amp;lt;script type=&amp;quot;text/javascript&amp;quot; asp-src-include=&amp;quot;~/dist/vendor.*.bundle.js&amp;quot; asp-append-version=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script type=&amp;quot;text/javascript&amp;quot; asp-src-include=&amp;quot;~/dist/app.*.bundle.js&amp;quot; asp-append-version=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/environment&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next thing - &lt;code&gt;Startup&lt;/code&gt; class. In there we need to add two things, to enable Webpack dev middleware, which is a part of &lt;a href="https://github.com/aspnet/JavaScriptServices"&gt;JavaScriptServices&lt;/a&gt; and to add SPA fallback route. Both of those changes are inside &lt;code&gt;Configure&lt;/code&gt; method, so this is how it will look like after:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();

        // This is added
        app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
        {
            HotModuleReplacement = true,
            ConfigFile = &amp;quot;webpack.netcore.config.js&amp;quot;,
            HotModuleReplacementClientOptions = new Dictionary&amp;lt;string, string&amp;gt; { {&amp;quot;reload&amp;quot;, &amp;quot;true&amp;quot;} }
        });
    }
    else
    {
        app.UseExceptionHandler(&amp;quot;/Home/Error&amp;quot;);
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();

    app.UseAuthentication();

    app.UseMvc(routes =&amp;gt;
    {
        routes.MapRoute(
            name: &amp;quot;default&amp;quot;,
            template: &amp;quot;{controller=Home}/{action=Index}/{id?}&amp;quot;);

        // This is added
        routes.MapSpaFallbackRoute(
            name: &amp;quot;spa-fallback&amp;quot;,
            defaults: new {controller = &amp;quot;Home&amp;quot;, action = &amp;quot;Index&amp;quot;});
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we try to build the project now, we'll get TypeScript (&lt;code&gt;tsc&lt;/code&gt;) errors, since it seems to be too eager to include all &lt;code&gt;.js&lt;/code&gt; files it finds into compilation. To prevent it, &lt;code&gt;wwwroot&lt;/code&gt; and &lt;code&gt;./*.js&lt;/code&gt; exclusions need to be added into &lt;code&gt;tsconfig.json&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;quot;exclude&amp;quot;: [
    &amp;quot;node_modules&amp;quot;,
    &amp;quot;aurelia_project&amp;quot;,
    &amp;quot;wwwroot&amp;quot;,
    &amp;quot;./*.js&amp;quot;
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great, we can run the application now and get the output from Aurelia, but no authentication has been involved. Let's add &lt;code&gt;[Authorize]&lt;/code&gt; attribute to &lt;code&gt;HomeController&lt;/code&gt;'s &lt;code&gt;Index&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class HomeController : Controller
{
    [Authorize]
    public IActionResult Index()
    {
        return View();
    }

    // other methods ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we run the application now and browse &lt;code&gt;https://localhost:[port]/&lt;/code&gt;, we will be automatically redirected to login page. After login (and probably the initial registration), we will be redirected back to the home page which hosts our Aurelia app.&lt;/p&gt;
&lt;p&gt;We can also try building Aurelia app from the command line with &lt;code&gt;au build&lt;/code&gt;. It should work as expected. In fact, there's one more feature we missed when copying from the Aurelia app generated with &lt;code&gt;au new&lt;/code&gt;. Project file also contained an additional target for publishing the Webpack Aurelia output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Target Name=&amp;quot;PublishRunWebpack&amp;quot; AfterTargets=&amp;quot;ComputeFilesToPublish&amp;quot;&amp;gt;
    &amp;lt;!-- As part of publishing, ensure the JS resources are freshly built in production mode --&amp;gt;
    &amp;lt;Exec Command=&amp;quot;npm install&amp;quot; /&amp;gt;
    &amp;lt;Exec Command=&amp;quot;au build --env prod&amp;quot; /&amp;gt;
    &amp;lt;!-- Include the newly-built files in the publish output --&amp;gt;
    &amp;lt;ItemGroup&amp;gt;
        &amp;lt;DistFiles Include=&amp;quot;wwwroot\dist\**&amp;quot; /&amp;gt;
        &amp;lt;ResolvedFileToPublish Include=&amp;quot;&amp;#64;(DistFiles-&amp;gt;'%(FullPath)')&amp;quot; Exclude=&amp;quot;&amp;#64;(ResolvedFileToPublish)&amp;quot;&amp;gt;
        &amp;lt;RelativePath&amp;gt;%(DistFiles.Identity)&amp;lt;/RelativePath&amp;gt;
        &amp;lt;CopyToPublishDirectory&amp;gt;PreserveNewest&amp;lt;/CopyToPublishDirectory&amp;gt;
        &amp;lt;/ResolvedFileToPublish&amp;gt;
    &amp;lt;/ItemGroup&amp;gt;
&amp;lt;/Target&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this change, &lt;code&gt;dotnet publish&lt;/code&gt; operation now also creates an output from Aurelia app.&lt;/p&gt;
&lt;p&gt;That's it, we have a client-side Aurelia app used in combination with server-side ASP.NET Core app with authentication and ASP.NET Core Identity enabled. By default, a cookie-based authentication is configured. If we need to make an API calls from Aurelia app to ASP.NET Core, we would also need to send cookies with each fetch request from the client - assuming that authorization is required for all API calls on the server side (and you do require it, don't you?). Usually, cookie-based authentication is not the best choice for API calls. Token-based authentication should be used instead.&lt;/p&gt;
&lt;h2 id="bonus-authorizing-api-calls-from-aurelia-app-with-jwt-tokens"&gt;Bonus: Authorizing API calls from Aurelia app with JWT tokens&lt;/h2&gt;
&lt;p&gt;The plan is to leave the current cookie-based authentication enabled for use in Identity (and potentially other MVC based pages) and to add JWT token based authentication too. &lt;a href="https://twitter.com/shawnwildermuth"&gt;Shawn Wildermuth&lt;/a&gt; has a good post about &lt;a href="https://wildermuth.com/2017/08/19/Two-AuthorizationSchemes-in-ASP-NET-Core-2"&gt;enabling dual mode authentication&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The following code should be added to &lt;code&gt;Startup.ConfigureServices&lt;/code&gt; method, just before the &lt;code&gt;services.AddMvc()...&lt;/code&gt; line:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services.AddAuthentication()
    .AddCookie(cfg =&amp;gt; cfg.SlidingExpiration = true)
    .AddJwtBearer(cfg =&amp;gt;
    {
        cfg.SaveToken = true;

        cfg.TokenValidationParameters = new TokenValidationParameters()
        {
            ValidIssuer = Configuration[&amp;quot;Tokens:Issuer&amp;quot;],
            ValidAudience = Configuration[&amp;quot;Tokens:Issuer&amp;quot;],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration[&amp;quot;Tokens:Key&amp;quot;]))
        };
    });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code depends on a presence of several configuration options, so we need to add those to &lt;code&gt;appsettings.json&lt;/code&gt; or another configuration source. &lt;strong&gt;Make sure these options are overridden and that strong key is used for production.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;quot;Tokens&amp;quot;: {
    &amp;quot;Key&amp;quot;: &amp;quot;2b0c0b6d-28ee-430b-b644-aaf99a195161&amp;quot;,
    &amp;quot;Issuer&amp;quot;: &amp;quot;https://miroslavpopovic.com&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have added JWT authentication, we need to have a mechanism of sharing the token and other data with the client side app. One easy approach might be to generate new JWT token each time user access the home page and send it to the browser together with the HTML that hosts Aurelia app. It avoids the extra call to the server to retrieve user data (including the token), but be aware that it might not be the best choice from the security standpoint - &lt;strong&gt;make sure you are on HTTPS!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This class will contain the data that will be passed on to the client:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ConfigViewModel
{
    public string UserName { get; set; }
    public string Token { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's add &lt;code&gt;GenerateToken&lt;/code&gt; method to &lt;code&gt;HomeController&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// A better approach would be to have this in a separate class
// Note that this is just a sample implementation!
private static string GenerateToken(string name, bool isAdmin, string issuer, string key)
{
    var claims = new List&amp;lt;Claim&amp;gt;
    {
        new Claim(JwtRegisteredClaimNames.Sub, name),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
    };
    if (isAdmin) claims.Add(new Claim(ClaimTypes.Role, &amp;quot;admin&amp;quot;));

    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
    var creds = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

    var token = new JwtSecurityToken(
        issuer,
        issuer,
        claims,
        expires: DateTime.Now.AddDays(1),
        signingCredentials: creds);

    return new JwtSecurityTokenHandler().WriteToken(token);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We'll inject &lt;code&gt;IConfiguration&lt;/code&gt; into &lt;code&gt;HttpController&lt;/code&gt; constructor and modify the &lt;code&gt;Index&lt;/code&gt; method to call token generator and give necessary data to the view:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Authorize]
public IActionResult Index()
{
    var token = GenerateToken(
        User.Identity.Name,
        User.IsInRole(&amp;quot;admin&amp;quot;),
        _configuration[&amp;quot;Tokens:Issuer&amp;quot;],
        _configuration[&amp;quot;Tokens:Key&amp;quot;]);

    return View(new ConfigViewModel {UserName = User.Identity.Name, Token = token});
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Index&lt;/code&gt; view also needs to be changed. &lt;code&gt;&amp;#64;model ConfigViewModel&lt;/code&gt; needs to be added to the top and &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; that acts as a container for Aurelia app needs to accept &lt;code&gt;data-*&lt;/code&gt; parameters:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div aurelia-app=&amp;quot;main&amp;quot; data-username=&amp;quot;&amp;#64;Model.UserName&amp;quot; data-token=&amp;quot;&amp;#64;Model.Token&amp;quot;&amp;gt;Loading...&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Aurelia app, we'll modify &lt;code&gt;main.ts&lt;/code&gt; to register the instance of &lt;code&gt;serverConfig&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;aurelia.container.registerInstance(
    'serverConfig',
    Object.assign({}, (aurelia.host as HTMLElement).dataset));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;app.ts&lt;/code&gt; or any other components can now inject &lt;code&gt;serverConfig&lt;/code&gt; and use it for binding, API calls, etc.:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { inject } from 'aurelia-framework';

&amp;#64;inject('serverConfig')
export class App {
    message = 'Hello World!';
    serverConfig;

    constructor(serverConfig) {
        this.serverConfig = serverConfig;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;app.html&lt;/code&gt; content:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;template&amp;gt;
    &amp;lt;h1&amp;gt;${message}&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;Welcome ${serverConfig.username}&amp;lt;/h2&amp;gt;
    &amp;lt;p&amp;gt;Your JWT token for API calls is: ${serverConfig.token}&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we'll add &lt;code&gt;DataController&lt;/code&gt; that will require JWT authorization for calls:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace AspNetCoreAurelia.Controllers
{
    [ApiController]
    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    public class DataController : Controller
    {
        [HttpGet(&amp;quot;/api/data&amp;quot;)]
        public IActionResult Index()
        {
            return Ok(new {data = new Random().Next(1, 100)});
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To call the API we'll use &lt;code&gt;aurelia-fetch-client&lt;/code&gt; plugin which needs to be installed first:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; npm install aurelia-fetch-client
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, &lt;code&gt;app.ts&lt;/code&gt; will be modified with a new &lt;code&gt;loadData()&lt;/code&gt; method that's using &lt;code&gt;aurelia-fetch-client&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { inject } from 'aurelia-framework';
import { HttpClient } from 'aurelia-fetch-client';

&amp;#64;inject('serverConfig', HttpClient)
export class App {
    data;
    message = 'Hello World!';
    http;
    serverConfig;

    constructor(serverConfig, http) {
        this.serverConfig = serverConfig;
        this.http = http;

        http.configure(config =&amp;gt; {
            config
                .useStandardConfiguration()
                .withBaseUrl('/api/')
                .withDefaults({
                    headers: {
                        'Accept': 'application/json',
                        'Authorization': 'Bearer ' + serverConfig.token
                    }
                });
        });
    }

    loadData() {
        this.http.fetch('data')
            .then(response =&amp;gt; response.json())
            .then(result =&amp;gt; {
                this.data = result.data;
            });
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;app.html&lt;/code&gt; will be modified too to have a button that triggers the call to &lt;code&gt;loadData()&lt;/code&gt; method and displays the result of the API call:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;template&amp;gt;
    &amp;lt;h1&amp;gt;${message}&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;Welcome ${serverConfig.username}&amp;lt;/h2&amp;gt;
    &amp;lt;p&amp;gt;Your JWT token for API calls is: ${serverConfig.token}&amp;lt;/p&amp;gt;

    &amp;lt;button class=&amp;quot;btn btn-default&amp;quot; click.trigger=&amp;quot;loadData()&amp;quot;&amp;gt;Load Data&amp;lt;/button&amp;gt;

    &amp;lt;div if.bind=&amp;quot;data&amp;quot;&amp;gt;
        Random data from server is: ${data}
    &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's the final result in all its glory:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/2018-07/aurelia-demo-app.png" class="img-fluid" alt="Aurelia demo app" /&gt;&lt;/p&gt;
&lt;p&gt;The finished sample code can be found &lt;a href="https://github.com/miroslavpopovic/aspnetcore-aurelia-sample"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;So, that's it. We talked about how to add ASP.NET Core Identity to the existing project with ASP.NET Core and Aurelia. After that, we have considered options for creating a new ASP.NET Core and Aurelia project that has Identity implemented from the start and choose to implement the easiest one.&lt;/p&gt;
&lt;p&gt;Unfortunately, we don't have a template that would cover this - to generate an ASP.NET Core app with Identity and Aurelia, but it's not that hard to do it now when we have Aurelia CLI at our disposal and also Identity scaffolding. It will be even easier when Aurelia CLI updates the ASP.NET Core output to 2.1. Hopefully, it will happen soon and you can always jump in &lt;a href="https://github.com/aurelia/cli"&gt;to help&lt;/a&gt;, it's open source after all :).&lt;/p&gt;
&lt;p&gt;Finally, I will leave you with a thought. Do consider whether you really want to have Aurelia (or any other client-side framework) in the same project as your backend. Microservices are a buzzword today, and with a good reason. Serving static client-side code and serving backend API from the same server may affect the performance. When you have them separately it's easier to scale them appropriately. The client-side code could be served from CDN optimized for static files and with aggressive caching involved. Backend code could then be scaled up easily to accommodate higher request count and not to be bogged down by static files. There are a PowerPoint presentation and sample code that you might find interesting on &lt;a href="https://github.com/miroslavpopovic/docker-aspnetcore-sample-1"&gt;my GitHub repository&lt;/a&gt; with a microservice-like organization. It has ASP.NET Core backend, Postgres database, HAProxy balancer and Aurelia frontend in separate Docker containers.&lt;/p&gt;
&lt;p&gt;Again, the code from this post can be found &lt;a href="https://github.com/miroslavpopovic/aspnetcore-aurelia-sample"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>Using Elasticsearch with ASP.NET Core and Docker</title>
			<link>https://miroslavpopovic.com/posts/2018/07/elasticsearch-with-aspnet-core-and-docker</link>
			<description>&lt;p&gt;Recently, I got an assignment for my employer's internal project to investigate &lt;a href="https://www.elastic.co/products/elasticsearch"&gt;Elasticsearch&lt;/a&gt; and its usage from within ASP.NET Core 2.1 app. I had no prior knowledge of Elasticsearch, but I had some experience with &lt;a href="http://lucene.apache.org/solr/"&gt;Solr&lt;/a&gt; and earlier with &lt;a href="https://github.com/nhibernate/NHibernate-Search"&gt;NHibernate Search&lt;/a&gt; which also used &lt;a href="https://lucene.apache.org/"&gt;Lucene&lt;/a&gt; under the cover. This post is a summary of my research.&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2018/07/elasticsearch-with-aspnet-core-and-docker</guid>
			<pubDate>Thu, 12 Jul 2018 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;Recently, I got an assignment for my employer's internal project to investigate &lt;a href="https://www.elastic.co/products/elasticsearch"&gt;Elasticsearch&lt;/a&gt; and its usage from within ASP.NET Core 2.1 app. I had no prior knowledge of Elasticsearch, but I had some experience with &lt;a href="http://lucene.apache.org/solr/"&gt;Solr&lt;/a&gt; and earlier with &lt;a href="https://github.com/nhibernate/NHibernate-Search"&gt;NHibernate Search&lt;/a&gt; which also used &lt;a href="https://lucene.apache.org/"&gt;Lucene&lt;/a&gt; under the cover. This post is a summary of my research.&lt;/p&gt;
&lt;p&gt;So, what exactly is Elasticsearch?&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/2018-07/elasticsearch.png" class="img-fluid" alt="Elasticsearch logo" /&gt;&lt;/p&gt;
&lt;p&gt;Elasticsearch is a distributed, RESTful search and analytics engine. It's based on Lucene and provides a JSON based REST API on top of it, among other things. It also has a rich ecosystem of tools and a number of official and unofficial client libraries. There are two official .NET clients, a low-level Elasticsearch.Net which is just an unopinionated wrapper around REST API and a high-level NEST library built on top of Elasticsearch.Net which provides strongly typed, fluent query DSL.&lt;/p&gt;
&lt;h2 id="elasticsearch-installation"&gt;Elasticsearch installation&lt;/h2&gt;
&lt;p&gt;Almost all of my development work is done on Windows and for it, we have several &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html#install-elasticsearch"&gt;installation options&lt;/a&gt;, including a simple &lt;code&gt;.zip&lt;/code&gt; file package, an &lt;code&gt;.msi&lt;/code&gt; installation package and of course docker images.&lt;/p&gt;
&lt;p&gt;If you are going with &lt;code&gt;.zip&lt;/code&gt; or &lt;code&gt;.msi&lt;/code&gt; package, you'll also need to install Java 8 SDK or later. Once unpacked or installed, you can run Elasticsearch from the command line or install it as a Windows service. Since I don't have Java SDK installed but have Docker, I'm going with &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.3/docker.html"&gt;Docker image&lt;/a&gt; option.&lt;/p&gt;
&lt;p&gt;Elastic, the company behind the Elasticsearch, is hosting their &lt;a href="https://www.docker.elastic.co/"&gt;own Docker registry&lt;/a&gt;. To pull the latest Elasticsearch image (version 6.3.0 at the moment of writing) from the registry, you need to execute this command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; docker pull docker.elastic.co/elasticsearch/elasticsearch:6.3.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://www.elastic.co/products/kibana"&gt;Kibana&lt;/a&gt; is also interesting for playing around with Elasticsearch. It's a web GUI that lets you visualize Elasticsearch data and navigate through the whole Elastic Stack. To pull its image, execute this command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; docker pull docker.elastic.co/kibana/kibana:6.3.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have both Elasticsearch and Kibana downloaded, we can run them under the same network. First, let's create a Docker network:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; docker network create esnetwork --driver=bridge
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the network set up, run both Elasticsearch and Kibana containers:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; docker run -p 9200:9200 -p 9300:9300 -e &amp;quot;discovery.type=single-node&amp;quot; --name elasticsearch -d --network esnetwork docker.elastic.co/elasticsearch/elasticsearch:6.3.0
&amp;gt; docker run -p 5601:5601 --name kibana -d --network esnetwork docker.elastic.co/kibana/kibana:6.3.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also need to map a volume to avoid the data being reset each time we restart the container, which further complicates the command. Instead of that, we can use docker-compose. Let's create &lt;code&gt;docker-compose.yml&lt;/code&gt; file with the following content:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: '2'
services:
    elasticsearch:
        image: docker.elastic.co/elasticsearch/elasticsearch:6.3.0
        ports:
            - &amp;quot;9200:9200&amp;quot;
            - &amp;quot;9300:9300&amp;quot;
        volumes:
            - esdata:/usr/share/elasticsearch/data
        networks:
            - esnetwork
    kibana:
        image: docker.elastic.co/kibana/kibana:6.3.0
        ports:
            - &amp;quot;5601:5601&amp;quot;
        networks:
            - esnetwork
        depends_on:
            - elasticsearch
volumes:
    esdata:
        driver: local
networks:
    esnetwork:
        driver: bridge
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, in short, we create two services &lt;code&gt;elasticsearch&lt;/code&gt; and &lt;code&gt;kibana&lt;/code&gt; and connect them via bridge network. &lt;code&gt;kibana&lt;/code&gt; depends on &lt;code&gt;elasticsearch&lt;/code&gt; in order to prevent it from being started first. If &lt;code&gt;elasticsearch&lt;/code&gt; container start fails, &lt;code&gt;kibana&lt;/code&gt; will fail too. We are also using the default ports for both.&lt;/p&gt;
&lt;p&gt;To run everything, we can just do:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; docker-compose up
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now both Elasticsearch and Kibana are started. Browse http://localhost:5601/ to be greeted with Kibana dashboard.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/2018-07/kibana.png" class="img-fluid" alt="Kibana dashboard" /&gt;&lt;/p&gt;
&lt;p&gt;That's everything we need for running Elasticsearch in dev environment. Installation on production environment shouldn't be too hard either as there are packages for various Linux distros. Also, there are Elasticsearch SaaS offerings from all major cloud vendors and &lt;a href="https://www.elastic.co/cloud"&gt;from Elastic itself&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="sample-project-creation"&gt;Sample project creation&lt;/h2&gt;
&lt;p&gt;To see how Elasticsearch works with ASP.NET Core, we'll create a new blog project, using Mads Kristensen's &lt;a href="https://github.com/madskristensen/MiniBlog.Core"&gt;MiniBlog.Core&lt;/a&gt; template. MiniBlog is a simple blogging engine built on ASP.NET Core 2.0.&lt;/p&gt;
&lt;p&gt;If you don't want to follow the step-by-step creation of the new project and adding functionality to it, there's &lt;a href="https://github.com/miroslavpopovic/miniblog-elasticsearch"&gt;a complete sample&lt;/a&gt; on GitHub.&lt;/p&gt;
&lt;p&gt;On the command line, install the template:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; dotnet new --install MadsKristensen.AspNetCore.Miniblog
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then create the project and add the NEST NuGet package:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; dotnet new miniblog --name MiniBlogElasticsearch
&amp;gt; dotnet add package NEST
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open the project with Visual Studio Code or with Visual Studio 2017:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; code .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you start the debugging now or do &lt;code&gt;dotnet run&lt;/code&gt; from the command line, you should get the following welcome post in your browser:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/2018-07/miniblog-welcome.png" class="img-fluid" alt="MiniBlog.Core Welcome" /&gt;&lt;/p&gt;
&lt;p&gt;You can log in (using &lt;em&gt;Sign in&lt;/em&gt; link at the bottom of the page) and create new posts - we will need several in order to test how search is working. Use &lt;code&gt;demo&lt;/code&gt; for both username and password.&lt;/p&gt;
&lt;h2 id="configuring-elasticsearch-client"&gt;Configuring Elasticsearch client&lt;/h2&gt;
&lt;p&gt;NEST NuGet package is already installed in the previous step. &lt;a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/nest.html"&gt;NEST&lt;/a&gt; is a high level .NET client for Elasticsearch which provides a way to call Elasticsearch REST APIs via strongly-typed, easy to use, query DSL. There's a good &lt;a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/nest-getting-started.html"&gt;Getting started&lt;/a&gt; document on Elastic search documentation which can bring you up to speed.&lt;/p&gt;
&lt;p&gt;For connecting to Elasticsearch API, we will use &lt;code&gt;ElasticClient&lt;/code&gt; which needs to be configured first. The documentation states that &lt;code&gt;ElasticClient&lt;/code&gt; is thread safe and hence it's possible to use a singleton instance of it per application (or per index). In order to be good ASP.NET Core citizens, let's initialize everything we need for Elasticsearch in &lt;code&gt;Startup&lt;/code&gt; class' &lt;code&gt;ConfigureServices&lt;/code&gt; method. Of course, we'll follow the best practices and add extension method for it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// In Startup class
public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddElasticsearch(Configuration);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here's the basic extension method implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static class ElasticsearchExtensions
{
    public static void AddElasticsearch(
        this IServiceCollection services, IConfiguration configuration)
    {
        var url = configuration[&amp;quot;elasticsearch:url&amp;quot;];
        var defaultIndex = configuration[&amp;quot;elasticsearch:index&amp;quot;];

        var settings = new ConnectionSettings(new Uri(url))
            .DefaultIndex(defaultIndex)
            .DefaultMappingFor&amp;lt;Post&amp;gt;(m =&amp;gt; m
                .Ignore(p =&amp;gt; p.IsPublished)
                .PropertyName(p =&amp;gt; p.ID, &amp;quot;id&amp;quot;)
            )
            .DefaultMappingFor&amp;lt;Comment&amp;gt;(m =&amp;gt; m
                .Ignore(c =&amp;gt; c.Email)
                .Ignore(c =&amp;gt; c.IsAdmin)
                .PropertyName(c =&amp;gt; c.ID, &amp;quot;id&amp;quot;)
            );

        var client = new ElasticClient(settings);

        services.AddSingleton&amp;lt;IElasticClient&amp;gt;(client);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The method expects that there is an &lt;code&gt;elasticsearch&lt;/code&gt; section in the configuration, so we need to add it to &lt;code&gt;appsettings.json&lt;/code&gt; (or other configuration source):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &amp;quot;elasticsearch&amp;quot;: {
        &amp;quot;index&amp;quot;: &amp;quot;miniblog&amp;quot;,
        &amp;quot;url&amp;quot;: &amp;quot;http://localhost:9200/&amp;quot;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;index&lt;/code&gt; should point to the default index in Elasticsearch and &lt;code&gt;url&lt;/code&gt; to the actual address of our Elasticsearch instance. Note that the organization within Elasticsearch is a bit different than within relational database server. When I first looked into it, I thought that &lt;em&gt;index&lt;/em&gt; is an equivalent of a single database and &lt;em&gt;type&lt;/em&gt; an equivalent of a database table. However, it's not really like that. Types are now obsolete from version 6.0.0 and will be completely removed in version 7.0.0. Indices should contain a single type only. Fortunately, we only have one type that needs to be indexed and searched - a blog &lt;code&gt;Post&lt;/code&gt;. &lt;code&gt;Comment&lt;/code&gt; type is nested and indexed under &lt;code&gt;Post&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote class="blockquote"&gt;
&lt;p&gt;From the &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/master/removal-of-types.html"&gt;docs&lt;/a&gt;:
Indices created in Elasticsearch 6.0.0 or later may only contain a single mapping type. Indices created in 5.x with multiple mapping types will continue to function as before in Elasticsearch 6.x. Mapping types will be completely removed in Elasticsearch 7.0.0.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;OK, back to &lt;code&gt;AddElasticsearch&lt;/code&gt; extension method. It also creates default mappings for our types. We are choosing not to index &lt;code&gt;Post.IsPublished&lt;/code&gt;, &lt;code&gt;Comment.Email&lt;/code&gt; (GDPR everyone?) and &lt;code&gt;Comment.IsAdmin&lt;/code&gt; properties. Plus, we want to have better names for ID properties in index - &lt;code&gt;id&lt;/code&gt; instead of &lt;code&gt;iD&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once &lt;code&gt;ConnectionSettings&lt;/code&gt; are defined, we create a new instance of &lt;code&gt;ElasticClient&lt;/code&gt; using those settings and register it as a singleton under .NET Core dependency injection container.&lt;/p&gt;
&lt;h2 id="indexing-blog-posts"&gt;Indexing blog posts&lt;/h2&gt;
&lt;p&gt;Great, we can now use &lt;code&gt;IElasticClient&lt;/code&gt; when we need to communicate with Elasticsearch instance. Let's use it to add posts to the index whenever they are saved.&lt;/p&gt;
&lt;p&gt;Open &lt;code&gt;Services/FileBlogService.cs&lt;/code&gt; in your editor and locate &lt;code&gt;SavePost(Post post)&lt;/code&gt; method. MiniBlog saves all the blog posts as separate XML files under &lt;code&gt;wwwroot/Posts&lt;/code&gt; folder and &lt;code&gt;SavePost&lt;/code&gt; method does the actual saving. We'll modify it by adding some code at the beginning and at the end of the method:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Inject IElasticClient to the constructor

public async Task SavePost(Post post)
{
    string filePath = GetFilePath(post);
    post.LastModified = DateTime.UtcNow;

    // Add this line:
    bool postExists = File.Exists(filePath);

    // ... rest of the method functionality

    // Add these lines at the end:
    if (postExists)
        await _elasticClient.UpdateAsync&amp;lt;Post&amp;gt;(post, u =&amp;gt; u.Doc(post));
    else
        await _elasticClient.IndexDocumentAsync(post);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to index the blog &lt;code&gt;Post&lt;/code&gt; we check if the post already exists. If it does, we assume it's already a part of the index, so we do &lt;code&gt;UpdateAsync&lt;/code&gt;. Elasticsearch will identify the post by id. If it doesn't already exist, it will call &lt;code&gt;IndexDocumentAsync&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Blog &lt;code&gt;Post&lt;/code&gt;s can also be deleted. We need to modify &lt;code&gt;DeletePost(Post post)&lt;/code&gt; method too:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public async Task DeletePost(Post post)
{
    string filePath = GetFilePath(post);

    if (File.Exists(filePath))
        File.Delete(filePath);

    await _elasticClient.DeleteAsync&amp;lt;Post&amp;gt;(post);

    if (_cache.Contains(post))
        _cache.Remove(post);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's possible that index sometimes gets out of sync with actual posts - like when we are adding posts without starting Elasticsearch first, or if we have created posts before implementing the indexing changes above. To cover these cases, we'll create a simple reindex operation (NOT for production use). We can have it under the new &lt;code&gt;SearchController&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class SearchController : Controller
{
    private readonly IBlogService _blogService;
    private readonly IElasticClient _elasticClient;
    private readonly IOptionsSnapshot&amp;lt;BlogSettings&amp;gt; _settings;

    public SearchController(IBlogService blogService, IElasticClient elasticClient, IOptionsSnapshot&amp;lt;BlogSettings&amp;gt; settings)
    {
        _blogService = blogService;
        _elasticClient = elasticClient;
        _settings = settings;
    }

    [Authorize]
    [Route(&amp;quot;/search/reindex&amp;quot;)]
    public async Task&amp;lt;IActionResult&amp;gt; ReIndex()
    {
        await _elasticClient.DeleteByQueryAsync&amp;lt;Post&amp;gt;(q =&amp;gt; q.MatchAll());

        var allPosts = (await _blogService.GetPosts(int.MaxValue)).ToArray();

        foreach (var post in allPosts)
        {
            await _elasticClient.IndexDocumentAsync(post);
        }

        return Ok($&amp;quot;{allPosts.Length} post(s) reindexed&amp;quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The implementation is pretty naïve (like I said, NOT for production use). It deletes all posts from the index using &lt;code&gt;match_all&lt;/code&gt; operator of Elasticsearch and then recreates the index by adding all posts to the index one by one. There's a better way to do this for production - &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-reindex.html"&gt;Reindex API&lt;/a&gt; but our implementation is good enough for a simple demo.&lt;/p&gt;
&lt;p&gt;When we navigate to &lt;code&gt;http://localhost:[our-port]/search/reindex&lt;/code&gt; in the browser, it will trigger reindexing and display the number of indexed posts.&lt;/p&gt;
&lt;p&gt;That's it! Our Elasticsearch index now has data we can use. Let's try to see it in Kibana. Navigate to http://localhost:5601/app/kibana#/dev_tools/console and enter the following in the console:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET miniblog/_search
{
    &amp;quot;query&amp;quot;: { &amp;quot;match_all&amp;quot;: {} }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will use the &lt;code&gt;match_all&lt;/code&gt; operator to return everything in our &lt;code&gt;miniblog&lt;/code&gt; index. Depending on the number of posts you have added you might have a different total number:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/2018-07/kibana-dev-tools.png" class="img-fluid" alt="Kibana Dev Tools Console" /&gt;&lt;/p&gt;
&lt;h2 id="implementing-search-operation"&gt;Implementing search operation&lt;/h2&gt;
&lt;p&gt;Excellent! We have data indexed but we're not using it for anything from the application itself. Let's add search operation next. Add the following to our &lt;code&gt;SearchController&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Route(&amp;quot;/search&amp;quot;)]
public async Task&amp;lt;IActionResult&amp;gt; Find(string query, int page = 1, int pageSize = 5)
{
    var response = await _elasticClient.SearchAsync&amp;lt;Post&amp;gt;(
        s =&amp;gt; s.Query(q =&amp;gt; q.QueryString(d =&amp;gt; d.Query(query)))
            .From((page - 1) * pageSize)
            .Size(pageSize));

    ViewData[&amp;quot;Title&amp;quot;] = _settings.Value.Name + &amp;quot; - Search Results&amp;quot;;
    ViewData[&amp;quot;Description&amp;quot;] = _settings.Value.Description;

    if (!response.IsValid)
    {
        // We could handle errors here by checking response.OriginalException or response.ServerError properties
        return View(&amp;quot;Results&amp;quot;, new Post[] { });
    }

    if (page &amp;gt; 1)
        ViewData[&amp;quot;prev&amp;quot;] = GetSearchUrl(query, page - 1, pageSize);
    if (response.IsValid &amp;amp;&amp;amp; response.Total &amp;gt; page * pageSize)
        ViewData[&amp;quot;next&amp;quot;] = GetSearchUrl(query, page + 1, pageSize);

    return View(&amp;quot;Results&amp;quot;, response.Documents);
}

private static string GetSearchUrl(string query, int page, int pageSize)
{
    return $&amp;quot;/search?query={Uri.EscapeDataString(query ?? &amp;quot;&amp;quot;)}&amp;amp;page={page}&amp;amp;pagesize={pageSize}/&amp;quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's pretty simple. &lt;code&gt;QueryString&lt;/code&gt; full text query is used for searching for the string passed in from the browser. &lt;code&gt;QueryString&lt;/code&gt; searches in all fields by default and that's what we need. A pagination is also implemented with &lt;code&gt;page&lt;/code&gt; and &lt;code&gt;pageSize&lt;/code&gt; parameters, used as arguments for &lt;code&gt;From&lt;/code&gt; and &lt;code&gt;Size&lt;/code&gt; methods of NEST fluent DSL.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Tip:&lt;/em&gt; Errors are not handled. By default, NEST doesn't throw exceptions, neither for search nor for indexing operations. We can change that when configuring the client. When exceptions aren't thrown, you can check the response object. &lt;code&gt;response.IsValid&lt;/code&gt; will be false in case of any error and &lt;code&gt;response.ServerErrors&lt;/code&gt; or &lt;code&gt;response.OriginalException&lt;/code&gt; would contain actual errors or exceptions. We could for instance check which error happens and send it to the view to display it to the user.&lt;/p&gt;
&lt;p&gt;The backend is implemented. We now need to add a search form to our pages. We'll add it below the header in &lt;code&gt;_Layout.cshtml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;header class=&amp;quot;siteheader&amp;quot;&amp;gt;
    &amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;
        &amp;lt;p itemprop=&amp;quot;name&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;~/&amp;quot;&amp;gt;&amp;#64;settings.Value.Name&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
        By &amp;lt;span itemprop=&amp;quot;author&amp;quot;&amp;gt;&amp;#64;settings.Value.Owner&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;!-- Insert search form here: --&amp;gt;
    &amp;lt;form class=&amp;quot;container&amp;quot; asp-action=&amp;quot;Find&amp;quot; asp-controller=&amp;quot;Search&amp;quot; method=&amp;quot;get&amp;quot;&amp;gt;
        &amp;lt;input type=&amp;quot;search&amp;quot; name=&amp;quot;query&amp;quot; placeholder=&amp;quot;Search...&amp;quot; value=&amp;quot;&amp;#64;ViewContext.HttpContext.Request.Query[&amp;quot;query&amp;quot;]&amp;quot; /&amp;gt;
    &amp;lt;/form&amp;gt;
&amp;lt;/header&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When user enters a value in search input and hits Enter, it will trigger the search operation and redirect the browser to search results. As a last thing, let's add &lt;code&gt;Views/Search/Results.cshtml&lt;/code&gt; Razor view:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;#64;model IEnumerable&amp;lt;Post&amp;gt;
&amp;lt;div class=&amp;quot;container search-results&amp;quot;&amp;gt;
    &amp;lt;h1&amp;gt;Search results&amp;lt;/h1&amp;gt;

    &amp;#64;if (!Model.Any())
    {
        &amp;lt;p&amp;gt;Nothing found for: &amp;lt;em&amp;gt;&amp;#64;ViewContext.HttpContext.Request.Query[&amp;quot;query&amp;quot;]&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
    }

    &amp;#64;foreach (var post in Model)
    {
        &amp;lt;article class=&amp;quot;post&amp;quot;&amp;gt;
            &amp;lt;header&amp;gt;
                &amp;lt;h2&amp;gt;&amp;lt;a asp-controller=&amp;quot;Blog&amp;quot; asp-action=&amp;quot;Post&amp;quot; asp-route-slug=&amp;quot;&amp;#64;post.Slug&amp;quot;&amp;gt;&amp;#64;post.Title&amp;lt;/a&amp;gt;&amp;lt;/h2&amp;gt;
                &amp;lt;time datetime=&amp;quot;&amp;#64;post.PubDate.ToString(&amp;quot;s&amp;quot;)&amp;quot; itemprop=&amp;quot;datePublished&amp;quot;&amp;gt;Published &amp;#64;post.PubDate.ToString(&amp;quot;MMM d, yyyy&amp;quot;)&amp;lt;/time&amp;gt;
                &amp;#64;if (User.Identity.IsAuthenticated)
                {
                    &amp;lt;a asp-controller=&amp;quot;Blog&amp;quot; asp-action=&amp;quot;edit&amp;quot; asp-route-id=&amp;quot;&amp;#64;post.ID&amp;quot; title=&amp;quot;Edit the post&amp;quot;&amp;gt;Edit post&amp;lt;/a&amp;gt;
                }
            &amp;lt;/header&amp;gt;
            &amp;lt;p&amp;gt;&amp;#64;post.Excerpt&amp;lt;/p&amp;gt;
        &amp;lt;/article&amp;gt;
    }

    &amp;lt;nav class=&amp;quot;pagination&amp;quot; aria-label=&amp;quot;Pagination&amp;quot;&amp;gt;
        &amp;#64;if (ViewData[&amp;quot;prev&amp;quot;] != null)
        {
            &amp;lt;a rel=&amp;quot;prev&amp;quot; href=&amp;quot;&amp;#64;ViewData[&amp;quot;prev&amp;quot;]&amp;quot; title=&amp;quot;Previous results&amp;quot;&amp;gt;&amp;amp;laquo; Previous&amp;lt;/a&amp;gt;
        }
        &amp;#64;if (ViewData[&amp;quot;next&amp;quot;] != null)
        {
            &amp;lt;a rel=&amp;quot;next&amp;quot; href=&amp;quot;&amp;#64;ViewData[&amp;quot;next&amp;quot;]&amp;quot; title=&amp;quot;Next results&amp;quot;&amp;gt;Next &amp;amp;raquo;&amp;lt;/a&amp;gt;
        }
        &amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;

        &amp;#64;section Head {
            &amp;#64;if (ViewData[&amp;quot;prev&amp;quot;] != null)
            {
                &amp;lt;link rel=&amp;quot;prev&amp;quot; href=&amp;quot;&amp;#64;ViewData[&amp;quot;prev&amp;quot;]&amp;quot; /&amp;gt;
            }
            &amp;#64;if (ViewData[&amp;quot;next&amp;quot;] != null)
            {
                &amp;lt;link rel=&amp;quot;next&amp;quot; href=&amp;quot;&amp;#64;ViewData[&amp;quot;next&amp;quot;]&amp;quot; /&amp;gt;
            }
        }
    &amp;lt;/nav&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Search page displays a list of &lt;code&gt;Post&lt;/code&gt;s found by Elasticsearch in response to the given query.&lt;/p&gt;
&lt;p&gt;If we search for something on our site, this is what we will get:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/2018-07/miniblog-search.png" class="img-fluid" alt="MiniBlog Search Results" /&gt;&lt;/p&gt;
&lt;p&gt;Pagination will work too if you have more than 5 results.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;This is just a tip of the iceberg. Elasticsearch has a vast number of features that you can use. The &lt;a href="https://github.com/miroslavpopovic/miniblog-elasticsearch"&gt;sample application&lt;/a&gt; on GitHub implements a few more features like &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.3/mapping-boost.html"&gt;boosting&lt;/a&gt;, &lt;a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/6.x/writing-analyzers.html"&gt;custom analyzers&lt;/a&gt;, tokenization, character filters, etc. You can find that in &lt;a href="https://github.com/miroslavpopovic/miniblog-elasticsearch/blob/master/src/ElasticsearchExtensions.cs"&gt;&lt;code&gt;ElasticsearchExtensions.cs&lt;/code&gt;&lt;/a&gt; file.&lt;/p&gt;
&lt;p&gt;A thing to keep in mind is - make sure you double check everything before going into production.&lt;/p&gt;
&lt;p&gt;First of all, you don't want Elasticsearch or Kibana's port exposed publicly! It should be accessible from your app backed only. It can (and should) also be protected with authentication. Check out &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-authenticate.html"&gt;X-Pack Authenticate API&lt;/a&gt; and &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-ssl.html"&gt;SSL Certificate API&lt;/a&gt; for that.&lt;/p&gt;
&lt;p&gt;Second, don't just throw everything into the index. Pick up only the data that makes sense. Adding a bunch of data just for the sake of having them indexed will hurt the performance, but more importantly, you'll get into trouble when you need to delete something. Remember GDPR? User data should be kept out of the index unless it's absolutely necessary. The other security-critical information too. Data breaches happen all the time. Don't be a part of the statistics.&lt;/p&gt;
&lt;p&gt;So, what's my final thought on Elasticsearch? It's an excellent and powerful tool and it fulfilled my expectations.&lt;/p&gt;
&lt;p&gt;However, what I didn't expect is the quality of the documentation. It's fantastic! There's an API reference, getting started documentation, best practices, an excellent documentation for the clients, etc. And not just the documentation for Elasticsearch, but for Docker and ASP.NET Core too. Long gone are the days where we had dry MSDN documentation with unoptimized and often wrong samples written by technical writers and not by developers.&lt;/p&gt;
&lt;p&gt;Getting started with something as complex as Elasticsearch these days is easy thanks to an excellent documentation, so kudos to people from Elastic and community for writing them! All the Elasticsearch docs are editable on &lt;a href="https://github.com/elastic/elasticsearch/edit/6.3/docs/reference/getting-started.asciidoc"&gt;GitHub&lt;/a&gt;. Kudos also go to &lt;a href="https://github.com/docker/docker.github.io/edit/master/index.md"&gt;Docker&lt;/a&gt; and &lt;a href="https://github.com/aspnet/Docs/blob/master/aspnetcore/index.md"&gt;ASP.NET Core&lt;/a&gt; documentation that's also available for editing on GitHub. This is truly a documentation renaissance.&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>Conference Bonanza</title>
			<link>https://miroslavpopovic.com/posts/2018/04/conference-bonanza</link>
			<description>&lt;p&gt;The following few weeks will be pretty furious for me, as I'm going to attend several conferences as a speaker.&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2018/04/conference-bonanza</guid>
			<pubDate>Mon, 16 Apr 2018 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;The following few weeks will be pretty furious for me, as I'm going to attend several conferences as a speaker.&lt;/p&gt;
&lt;h2 id="microsoft-network-8-neum"&gt;Microsoft NetWork 8, Neum&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://msnetwork.ba"&gt;&lt;img src="/images/2018-04/msnetwork-2018.png" class="img-fluid" alt="Microsoft NetWork 8" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is our local Microsoft conference now almost traditionally held in &lt;a href="https://en.wikipedia.org/wiki/Neum"&gt;Neum&lt;/a&gt;, a small town in Bosnia and Herzegovina on the Adriatic Sea.&lt;/p&gt;
&lt;p&gt;Back on NetWork after a 2 year break. My slot is on Thursday at 11 am with a talk &amp;quot;Docker and ASP.NET Core (2.1)&amp;quot;. The sample for this talk can be found &lt;a href="https://github.com/miroslavpopovic/docker-aspnetcore-sample-1"&gt;on GitHub&lt;/a&gt; where I'll also put a link to the slides after the presentation.&lt;/p&gt;
&lt;h2 id="microsoft-windays-18-porec"&gt;Microsoft WinDays 18, Poreč&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.windays.hr"&gt;&lt;img src="/images/2018-04/windays-2018.png" class="img-fluid" alt="Microsoft WinDays 18" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A week after, I'm off to Croatia and again to the Adriatic Sea. Microsoft WinDays conference is held in &lt;a href="https://en.wikipedia.org/wiki/Pore%C4%8D"&gt;Poreč&lt;/a&gt; this year, with WinDays Technology from 25th to 27th of April.&lt;/p&gt;
&lt;p&gt;This is my first WinDays conference which I'm really looking forward to. I got the last slot on Friday, just before the lunch at 1:30 pm. Only half an hour so I had to reorganize my demos a bit. The talk is &amp;quot;Building Production-Ready APIs with ASP.NET Core 2.1&amp;quot; which is my old talk but with latest ASP.NET Core 2.1 changes incorporated. The samples can also be found &lt;a href="https://github.com/miroslavpopovic/production-ready-apis-sample-2.1"&gt;on GitHub&lt;/a&gt;. Link to the slides will be available after the presentation.&lt;/p&gt;
&lt;h2 id="weblica-2018-cakovec"&gt;Weblica 2018, Čakovec&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://weblica.hr"&gt;&lt;img src="/images/2018-04/weblica-2018.png" class="img-fluid" alt="Weblica 2018" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Finally, on May 12th, I'm in Croatia again. This time in &lt;a href="https://en.wikipedia.org/wiki/%C4%8Cakovec"&gt;Čakovec&lt;/a&gt;, a small town in the north part of Croatia. Unlike the previous two conferences Weblica is community driven.&lt;/p&gt;
&lt;p&gt;I had a pleasure of presenting on this conference few years ago. They have great people there, fantastic nature and also great food. My slot is the first one after the keynote, at 9:30 am and the talk is the same as on WinDays but 15 minutes longer.&lt;/p&gt;
&lt;h2 id="honorable-mentions"&gt;Honorable mentions&lt;/h2&gt;
&lt;h3 id="tarabica-2018-belgrade"&gt;Tarabica 2018, Belgrade&lt;/h3&gt;
&lt;p&gt;Another community conference this time in Belgrade, Serbia. My plan was to nominate the talk there too this year, but unfortunately it's on the same day as Weblica where I was already accepted as a speaker. Nah, maybe next year. For those interested to attend, you can find more info on &lt;a href="http://www.tarabica.org"&gt;http://www.tarabica.org&lt;/a&gt;. The attendance is free.&lt;/p&gt;
&lt;h3 id="seavus-conference-skopje"&gt;Seavus conference, Skopje&lt;/h3&gt;
&lt;p&gt;Finally, my employer &lt;a href="https://seavus.com"&gt;Seavus&lt;/a&gt; is planning a one day conference in Skopje. But guess what, also on May 12th. There must be something about that day...&lt;/p&gt;
&lt;p&gt;Anyway, three conferences are more than enough for such a short period. I'm looking forward to meet a lot of other speakers / friends and of course enjoy a bit of time off from my daily routine.&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>Upcoming HTTP API related changes in ASP.NET Core 2.1</title>
			<link>https://miroslavpopovic.com/posts/2018/02/upcoming-http-api-related-changes-in-aspnet-core-2.1</link>
			<description>&lt;p&gt;Two weeks ago ASP.NET team &lt;a href="https://blogs.msdn.microsoft.com/webdev/2018/02/02/asp-net-core-2-1-roadmap/"&gt;unveiled&lt;/a&gt; its roadmap for ASP.NET Core 2.1 release. In addition to some highly expected features like new SignalR version, Identity changes and WebHooks, the things that I'm most excited of are Web API improvements. These changes are summed up in an initiative with a funny name - &lt;a href="https://github.com/aspnet/Mvc/projects/5"&gt;Project KodKod&lt;/a&gt; - and a goal "to make MVC into a opinionated, forward-thinking, batteries included framework for HTTP APIs".&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2018/02/upcoming-http-api-related-changes-in-aspnet-core-2.1</guid>
			<pubDate>Sun, 18 Feb 2018 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;Two weeks ago ASP.NET team &lt;a href="https://blogs.msdn.microsoft.com/webdev/2018/02/02/asp-net-core-2-1-roadmap/"&gt;unveiled&lt;/a&gt; its roadmap for ASP.NET Core 2.1 release. In addition to some highly expected features like new SignalR version, Identity changes and WebHooks, the things that I'm most excited of are Web API improvements. These changes are summed up in an initiative with a funny name - &lt;a href="https://github.com/aspnet/Mvc/projects/5"&gt;Project KodKod&lt;/a&gt; - and a goal &amp;quot;to make MVC into a opinionated, forward-thinking, batteries included framework for HTTP APIs&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/2018-02/aspnet-mvc-project-kodkod.png" class="img-fluid" alt="Project KodKod" /&gt;&lt;/p&gt;
&lt;p&gt;Ever since ASP.NET Web API was introduced, almost 6 years ago, it became THE way that I was working with ASP.NET. I used it mostly to build a backend for SPA applications and on a few API only projects. My experience of building APIs is summed up in a talk that I've been presenting on local conferences and user group meetings, called &amp;quot;Building production ready APIs with ASP.NET Core 2.0&amp;quot;. The samples and the conference PowerPoint presentations can be found &lt;a href="https://github.com/miroslavpopovic/production-ready-apis-sample"&gt;on GitHub&lt;/a&gt;. As ASP.NET Core 2.1 is coming with a number of new API-related features, it could be a good time to update my presentation. So, let's see what are some of those features...&lt;/p&gt;
&lt;h2 id="https-by-default"&gt;HTTPS by default&lt;/h2&gt;
&lt;p&gt;While not really an API specific feature, it's something that is more or less a must for public facing APIs. When you create a new ASP.NET Core 2.1 project, the template will enable HTTPS by default. A default local certificate will be created during .NET Core SDK installation. There will also be a new tool for managing certificates that will probably be installed as global tool. Global tools are a feature similar to Node global tools which will enable creation and distribution of tools ecosystem via NuGet. You can read more about it on &lt;a href="https://blogs.msdn.microsoft.com/dotnet/2018/02/02/net-core-2-1-roadmap/"&gt;.NET Core 2.1 roadmap&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In addition to HTTPS by default, the template will also enable HSTS - &lt;a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security"&gt;HTTP Strict Transport Security&lt;/a&gt; protocol - which will prevent browsers to try HTTP address. HTTPS will be forced.&lt;/p&gt;
&lt;h2 id="better-swagger-openapi-specification-support"&gt;Better Swagger / OpenAPI specification support&lt;/h2&gt;
&lt;p&gt;For a long time, Swagger was de facto a standard for describing RESTful APIs but there were other tools too. That changed last year when SmartBear Software, a company behind Swagger, &lt;a href="https://swagger.io/blog/announcing-openapi-3-0/"&gt;donated&lt;/a&gt; Swagger 2.0 specification to the OpenAPI initiative. Today, it's known as &lt;a href="https://www.openapis.org/"&gt;OpenAPI specification 3.0&lt;/a&gt; and it's backed up by all the big players in API space.&lt;/p&gt;
&lt;p&gt;ASP.NET Core 2.1 will come with a new type - &lt;code&gt;ActionResult&amp;lt;T&amp;gt;&lt;/code&gt; which will enable discoverability of the action result type, without the need for additional attributes that is required now to get the correct metadata.&lt;/p&gt;
&lt;p&gt;Also, a new tool will be added for build-time OpenAPI generation - at the moment we have only runtime generation. More features are planned for future versions.&lt;/p&gt;
&lt;h2 id="apicontroller-attribute"&gt;&lt;code&gt;[ApiController]&lt;/code&gt; attribute&lt;/h2&gt;
&lt;p&gt;This is my favorite new feature that will bring back some conventions we had in old ASP.NET Web API. Basically, you will decorate controller class with &lt;code&gt;[ApiController]&lt;/code&gt; and MVC will be able to infer some defaults and do additional processing on your action methods. This is what we will have for start:&lt;/p&gt;
&lt;h3 id="automatic-model-state-validation"&gt;Automatic model state validation&lt;/h3&gt;
&lt;p&gt;No need to write &lt;code&gt;if (!ModelState.IsValid) return BadRequest(ModelState);&lt;/code&gt;. Validation will be done automatically before calling your action method.&lt;/p&gt;
&lt;p&gt;One of the &lt;em&gt;best practices&lt;/em&gt; I've been preaching is &lt;a href="https://github.com/miroslavpopovic/production-ready-apis-sample/blob/44870228b8c9952ca33ee791d80aacfa4256b762/BoardGamesApi/Models/ApiResult.cs"&gt;wrapping a response in an object&lt;/a&gt; that has properties like &lt;code&gt;Error&lt;/code&gt; and &lt;code&gt;IsSuccessful&lt;/code&gt; in addition to the actual data. ASP.NET Core 2.1 will take a different approach by introducing a validation error response in the shape of &lt;a href="https://tools.ietf.org/html/rfc7807"&gt;RFC 7807&lt;/a&gt; standardized response, but you will be able to override it.&lt;/p&gt;
&lt;h3 id="inferring-defaults-on-action-method-parameters"&gt;Inferring defaults on action method parameters&lt;/h3&gt;
&lt;p&gt;One thing that we lost in transition from the old ASP.NET Web API were automatic recognition of the source for action method parameters. This is coming back to ASP.NET Core 2.1 controllers decorated with &lt;code&gt;[ApiController]&lt;/code&gt;. &lt;code&gt;[FromBody]&lt;/code&gt; will be default for complex types. &lt;code&gt;[FromRoute]&lt;/code&gt; will be used when possible and otherwise &lt;code&gt;[FromQuery]&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="other"&gt;Other&lt;/h3&gt;
&lt;p&gt;There are few more things that will be enabled with the new &lt;code&gt;[ApiController]&lt;/code&gt; attribute. You can find more in depth review of this attribute in &lt;a href="https://www.strathweb.com/2018/02/exploring-the-apicontrollerattribute-and-its-features-for-asp-net-core-mvc-2-1/"&gt;a post by Filip W&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="sample"&gt;Sample&lt;/h2&gt;
&lt;p&gt;To summarize, let's take a look at one of the action methods that I have in my presentation sample code, and how it would look like in ASP.NET Core 2.1.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Sample: ASP.NET Core 2.0
[Authorize]
[Route(&amp;quot;api/games&amp;quot;)]
public class GamesController : Controller
{
    private readonly IGamesRepository _gamesRepository;

    public GamesController(IGamesRepository gamesRepository)
    {
        _gamesRepository = gamesRepository;
    }
    
    /// &amp;lt;summary&amp;gt;
    /// Create a new game from the supplied data.
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;param name=&amp;quot;model&amp;quot;&amp;gt;Data to create the game from.&amp;lt;/param&amp;gt;
    /// &amp;lt;response code=&amp;quot;200&amp;quot;&amp;gt;Returns the created game.&amp;lt;/response&amp;gt;
    [Authorize(Roles = &amp;quot;admin&amp;quot;)]
    [HttpPost]
    [ProducesResponseType(typeof(ApiResult&amp;lt;Game&amp;gt;), 200)]
    public IActionResult Post([FromBody] GameInput model)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        var game = new Game();
        model.MapToGame(game);

        _gamesRepository.Create(game);

        var url = $&amp;quot;{Request.Scheme}://{Request.Host}/api/games/{game.Id}&amp;quot;;

        return Created(url, game.WrapData());
    }       
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// Sample: ASP.NET Core 2.1
[ApiController]
[Authorize]
[Route(&amp;quot;api/games&amp;quot;)]
public class GamesController : Controller
{
    private readonly IGamesRepository _gamesRepository;

    public GamesController(IGamesRepository gamesRepository)
    {
        _gamesRepository = gamesRepository;
    }
    
    /// &amp;lt;summary&amp;gt;
    /// Create a new game from the supplied data.
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;param name=&amp;quot;model&amp;quot;&amp;gt;Data to create the game from.&amp;lt;/param&amp;gt;
    [Authorize(Roles = &amp;quot;admin&amp;quot;)]
    [HttpPost]
    public ActionResult&amp;lt;Game&amp;gt; Post(GameInput model)
    {
        // No model state validation code here, hooray!

        var game = new Game();
        model.MapToGame(game);

        _gamesRepository.Create(game);

        var url = $&amp;quot;{Request.Scheme}://{Request.Host}/api/games/{game.Id}&amp;quot;;

        return Created(url, game);
    }       
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Since I haven't posted on my blog in a while, I felt obliged to share some of my thoughts on the current state of .NET (Core).&lt;/p&gt;
&lt;p&gt;As a long-time ASP.NET developer, it's really nice to see how rejuvenated ASP.NET Core is rapidly improving with each version. At one moment, before .NET Core became a thing, it looked like .NET ecosystem became stale. Even I (an early proponent of .NET) have took &lt;a href="http://javascriptkicks.com/articles/147418/learning-node-js-and-react-while-building-a-product"&gt;a foray into Node&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;.NET Core, .NET Standard and ASP.NET Core have brought back the hope for .NET developers in general. Open-source approach, multi-platform, beautiful C# language and a great performance could make ASP.NET Core a framework of choice and a force to recon in the server-side world.&lt;/p&gt;
&lt;p&gt;We could even see a full-stack C# (similar to full-stack JavaScript with Node). Blazor, an experiment to create a client-side .NET web framework that's running in a browser via Web Assembly is now on ASP.NET team's &lt;a href="https://github.com/aspnet/Blazor"&gt;GitHub repository&lt;/a&gt;. To learn more about it, watch &lt;a href="https://www.youtube.com/watch?v=Ta_qXpXQqGQ"&gt;ASP.NET Community Standup&lt;/a&gt; from two weeks ago.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you didn't know, ASP.NET Community Standups have a new home on YouTube, under the &lt;a href="https://www.youtube.com/channel/UCiaZbznpWV1o-KLxj8zqR6A/videos"&gt;.NET Foundation channel&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Finally, after some struggle, the future of .NET looks bright!&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>From Jekyll to Wyam</title>
			<link>https://miroslavpopovic.com/posts/2017/07/from-jekyll-to-wyam</link>
			<description>&lt;p&gt;After doing mostly JavaScript development in the last few years I decided that it's time to head back to .NET world and see what's new. Actually, I never really left. There was a bunch of older .NET projects that I have maintained in the meantime and few new ones, but all of them were on full .NET Framework. My plan is to dive into .NET Core and ASP.NET Core. While I've been closely following all the new development around .NET Core, I'm yet to do a concrete project with it. Hopefully, this is about to change.&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2017/07/from-jekyll-to-wyam</guid>
			<pubDate>Wed, 19 Jul 2017 00:00:00 GMT</pubDate>
			<content:encoded>&lt;h2 id="back-to.net"&gt;Back to .NET&lt;/h2&gt;
&lt;p&gt;After doing mostly JavaScript development in the last few years I decided that it's time to head back to .NET world and see what's new. Actually, I never really left. There was a bunch of older .NET projects that I have maintained in the meantime and few new ones, but all of them were on full .NET Framework. My plan is to dive into .NET Core and ASP.NET Core. While I've been closely following all the new development around .NET Core, I'm yet to do a concrete project with it. Hopefully, this is about to change.&lt;/p&gt;
&lt;p&gt;For start, I'm switching this blog to &lt;a href="https://wyam.io/"&gt;Wyam&lt;/a&gt;, a .NET Core based static site generator. &lt;a href="https://hexo.io/"&gt;Hexo&lt;/a&gt; was one of the alternatives that I was looking into, but since it's Node.js based, I decided it's more appropriate to go with .NET Core based platform.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/2017-07-wyam.png" class="img-fluid" alt="Wyam" /&gt;&lt;/p&gt;
&lt;p&gt;It seems that each time when I try to return to blogging I switch the blog platform, so let's continue with that practice :)&lt;/p&gt;
&lt;h2 id="phase-1-installing-and-initializing-wyam"&gt;Phase 1 - Installing and initializing Wyam&lt;/h2&gt;
&lt;p&gt;Installation was easy as described in &lt;a href="https://wyam.io/docs/usage/obtaining"&gt;Wyam documentation&lt;/a&gt;. I have downloaded the latest  Windows Installer and run it. After that it's necessary to go to &lt;code&gt;%LocalAppData%\Wyam&lt;/code&gt; and run &lt;code&gt;Wyam.Windows add-path&lt;/code&gt; to add the Wyam installation folder to Windows PATH variable.&lt;/p&gt;
&lt;p&gt;Next step, reading the documentation. Seriously, I do prefer to read the docs first to get myself more familiar with something I'm interested in and Wyam has a pretty good documentation.&lt;/p&gt;
&lt;p&gt;My blog is hosted on GitHub Pages. Old version was using Jekyll. To start with the migration, I have created a new git branch. Initializing Wyam was as simple as &lt;code&gt;wyam new -r Blog&lt;/code&gt; to create a new configuration using Blog recipe.&lt;/p&gt;
&lt;p&gt;Wyam has created &lt;code&gt;config.wyam&lt;/code&gt; file that can be modified. This is my first configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#recipe Blog

Settings[Keys.Host] = &amp;quot;blog.miroslavpopovic.com&amp;quot;;
Settings[BlogKeys.IncludeDateInPostPath] = true;
Settings[BlogKeys.RssPath] = &amp;quot;feed.xml&amp;quot;;
Settings[BlogKeys.Title] = &amp;quot;Miroslav Popovic&amp;quot;;
Settings[BlogKeys.Description] = &amp;quot;Full-stack .NET and JavaScript software architect&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Few things that are changed from the default. I prefer to have date in URL, so &lt;code&gt;BlogKeys.IncludeDateInPostPath&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt;. By default, Wyam generates two feed files, one for RSS - &lt;code&gt;feed.rss&lt;/code&gt; and one for Atom - &lt;code&gt;feed.atom&lt;/code&gt; format. My old setup had one RSS file - &lt;code&gt;feed.xml&lt;/code&gt;. To prevent subscribers from making changes in their feed readers, &lt;code&gt;BlogKeys.RssPath&lt;/code&gt; was set to &lt;code&gt;feed-xml&lt;/code&gt;. Other settings should be self-expanatory.&lt;/p&gt;
&lt;p&gt;With this it was possible to build the blog &lt;code&gt;wyam build&lt;/code&gt; or build and watch for changes &lt;code&gt;wyam build --watch&lt;/code&gt;. For previewing the result in browser &lt;code&gt;wyam preview&lt;/code&gt; runs the integrated server on port 5080 by default.&lt;/p&gt;
&lt;h2 id="phase-2-moving-assets"&gt;Phase 2 - Moving assets&lt;/h2&gt;
&lt;p&gt;Jekyll used assets relative to root folder, like static pages (&lt;code&gt;about.md&lt;/code&gt;), &lt;code&gt;/images&lt;/code&gt;, favicons, etc. Wyam uses &lt;code&gt;/input&lt;/code&gt; as a root, so all the files need to be moved there.&lt;/p&gt;
&lt;h2 id="phase-3-migrating-posts"&gt;Phase 3 - Migrating posts&lt;/h2&gt;
&lt;p&gt;Wyam Blog recipe also supports markdown by default. Migrating posts to Wyam was as simple as moving them from Jekyll's &lt;code&gt;_posts&lt;/code&gt; folder to Wyam's &lt;code&gt;input/posts&lt;/code&gt; folder and changing few things in &lt;a href="https://wyam.io/docs/concepts/metadata#front-matter"&gt;FrontMatter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;FrontMatter is a bit of metadata at the top of the post which you can use to define title, description, tags, dates, or any other custom metadata. There are some differences between Jekyll and Wyam FrontMatter syntax. I.e. tags in Jekyll are defined separated with space, like &lt;code&gt;tags: tag1 tag2 tag3&lt;/code&gt; and that will create a single tag in Wyam. For Wyam, you need to either define them with array syntax &lt;code&gt;tags: [tag1, tag2, tag3]&lt;/code&gt; or split them to multiple lines:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tags:
  - tag1
  - tag2
  - tag3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another difference is with redirects. Jekyll redirects are defined with &lt;code&gt;redirect_from: &amp;quot;/some-url/&amp;quot;&lt;/code&gt; and Wyam supports this syntax: &lt;code&gt;RedirectFrom: /some-url/&lt;/code&gt;. The fact that you can define multiple redirects using the same syntax as for tags made me very happy. This is especially usefull if you are doing several migrations, like I did (from FunnelWeb to Jekyll to Wyam):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;RedirectFrom:
  - resurrection
  - 2012/06/10/resurrection
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since I don't have that many posts, I did all of these changes manually, but you could easily to search/replace in files.&lt;/p&gt;
&lt;h2 id="phase-4-tuning-up-the-theme"&gt;Phase 4 - Tuning up the theme&lt;/h2&gt;
&lt;p&gt;The default Clean Blog theme looks very good, so that's what I have used. Overriding the parts of layout is as simple as copying and modifying the partial views from Clean Blog theme source, like &lt;code&gt;_Scripts.cshtml&lt;/code&gt; (for defining Google Analytics script), &lt;code&gt;_Head.cshtml&lt;/code&gt; (for favicons and other meta tags), etc. Modifying CSS was also simple. All that is required is a new file for style overrides - &lt;code&gt;assets/css/override.css&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;My blog is using Disqus for comments. Adding Disqus is described on &lt;a href="https://wyam.io/recipes/blog/themes/cleanblog"&gt;Wyam docs&lt;/a&gt;. I was a bit concerned with preserving existing comments, but there are solutions for that too. One is described by Erik Onarheim in his &lt;a href="https://erikonarheim.com/posts/using-wyam-blog#customizing-wyam-with-disqus"&gt;migrating to Wyam&lt;/a&gt; post and another one is to use &lt;a href="https://help.disqus.com/customer/portal/articles/912757-url-mapper"&gt;Disqus URL mapper&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="phase-5-deployment"&gt;Phase 5 - Deployment&lt;/h2&gt;
&lt;p&gt;Finally, the time has come to deploy. There's a pretty good guide about setting up the continuous deployment on &lt;a href="https://wyam.io/docs/deployment/appveyor"&gt;Wyam docs&lt;/a&gt; so I won't repeat it here.&lt;/p&gt;
&lt;p&gt;One problem I had is with git branches. My previous setup with Jekyll had &lt;code&gt;master&lt;/code&gt; and &lt;code&gt;gh-pages&lt;/code&gt; branches. Unfortunately, having the code in &lt;code&gt;master&lt;/code&gt; branch and output in &lt;code&gt;gh-pages&lt;/code&gt; does not work for user and organization pages on GitHub and my blog code is on user page. At least that's what the documentation told me. It only allows serving from &lt;code&gt;master&lt;/code&gt; so I reorganized my git repository a bit. &amp;quot;Source&amp;quot; code is in &lt;code&gt;source&lt;/code&gt; branch and AppVeyor build copies the output to &lt;code&gt;master&lt;/code&gt; branch.&lt;/p&gt;
&lt;h2 id="finally"&gt;Finally&lt;/h2&gt;
&lt;p&gt;This blog is now generated using Wyam. At least until the next migration :)&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>Speaking Engagements</title>
			<link>https://miroslavpopovic.com/posts/2015/04/speaking-engagements</link>
			<description>&lt;p&gt;On Wednesday, 29. April, I'm holding a talk &lt;a href="https://msnetwork.ba/conference/sessions#se2313"&gt;"Tales from the client side"&lt;/a&gt; on 5th Microsoft Bosnia and Herzegovina &lt;a href="https://msnetwork.ba/"&gt;NetWork conference&lt;/a&gt;. The samples and presentation are available at &lt;a href="https://github.com/miroslavpopovic/msnetwork5-sample"&gt;https://github.com/miroslavpopovic/msnetwork5-sample&lt;/a&gt;.&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2015/04/speaking-engagements</guid>
			<pubDate>Mon, 27 Apr 2015 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;On Wednesday, 29. April, I'm holding a talk &lt;a href="https://msnetwork.ba/conference/sessions#se2313"&gt;&amp;quot;Tales from the client side&amp;quot;&lt;/a&gt; on 5th Microsoft Bosnia and Herzegovina &lt;a href="https://msnetwork.ba/"&gt;NetWork conference&lt;/a&gt;. The samples and presentation are available at &lt;a href="https://github.com/miroslavpopovic/msnetwork5-sample"&gt;https://github.com/miroslavpopovic/msnetwork5-sample&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At Saturday, 9. May I'm heading to Čakovec Croatia for &lt;a href="http://weblica.hr/"&gt;Weblica&lt;/a&gt; conference, where I'm holding a talk &amp;quot;Building modern web in Microsoft world&amp;quot;.&lt;/p&gt;
&lt;p&gt;Looking forward to them!&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/conferences-2015-ms-network-weblica.png" class="img-fluid" alt="Conferences" /&gt;&lt;/p&gt;
&lt;p&gt;Update: Weblica presentation is available at &lt;a href="https://github.com/miroslavpopovic/web-ms-2015"&gt;https://github.com/miroslavpopovic/web-ms-2015&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>Switching to Jekyll</title>
			<link>https://miroslavpopovic.com/posts/2015/03/switching-to-jekyll</link>
			<description>&lt;p&gt;I decided to switch my blog from &lt;a href="http://www.funnelweblog.com/"&gt;FunnelWeb&lt;/a&gt; to &lt;a href="http://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; and host it on &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt;. There were some problems along the way so I'm documenting it here in case it might help someone. If you want a more detailed introduction to Jekyll, read Brian Rinaldi's &lt;a href="http://developer.telerik.com/featured/getting-started-with-jekyll/"&gt;Getting Started with Jekyll&lt;/a&gt; post.&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2015/03/switching-to-jekyll</guid>
			<pubDate>Mon, 23 Mar 2015 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;I decided to switch my blog from &lt;a href="http://www.funnelweblog.com/"&gt;FunnelWeb&lt;/a&gt; to &lt;a href="http://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; and host it on &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt;. There were some problems along the way so I'm documenting it here in case it might help someone. If you want a more detailed introduction to Jekyll, read Brian Rinaldi's &lt;a href="http://developer.telerik.com/featured/getting-started-with-jekyll/"&gt;Getting Started with Jekyll&lt;/a&gt; post.&lt;/p&gt;
&lt;h2 id="installing-jekyll-on-windows"&gt;Installing Jekyll on Windows&lt;/h2&gt;
&lt;p&gt;Since I'm not a designer, I decided to choose one of &lt;a href="https://github.com/jekyll/jekyll/wiki/Themes"&gt;many Jekyll themes&lt;/a&gt; and modify if necessary. Changing the theme directly on GitHub would be clumsy, since a lot of files are involved. Also doing little changes and testing the outcome would require a lot of git commits. Instead, I cloned my repository locally and installed Jekyll and its requirements on my Windows machine.&lt;/p&gt;
&lt;p&gt;There's a &lt;a href="http://jekyll-windows.juthilo.com/"&gt;very good tutorial&lt;/a&gt; for installing Jekyll on Windows. Unfortunately it's a little bit older. I followed the guide and installed the latest version of x64 build of Ruby, Ruby DevKit, Jekyll, x64 build of Python and Pygments. The latest Ruby version at the time of writing was 2.2.1.&lt;/p&gt;
&lt;p&gt;It looked like everything went smoothly, but when trying to run &lt;code&gt;Jekyll serve&lt;/code&gt; on my blog repository, it failed. The problem was with one of the Ruby gems called &lt;a href="http://www.nokogiri.org/"&gt;nokogiri&lt;/a&gt;. Apparently, it &lt;a href="http://stackoverflow.com/questions/28999906/require-cannot-load-such-file-nokogiri-nokogiri-loaderror-when-running"&gt;doesn't work&lt;/a&gt; with Ruby 2.2.1 on Windows, so I had to fall back to older Ruby version - 2.1.5.&lt;/p&gt;
&lt;p&gt;Of course, something had to go wrong with that too. After installing Ruby 2.1.5 x64 and Ruby DevKit, gem installation with the following message:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bat"&gt;gem install jekyll
ERROR:  Could not find a valid gem 'jekyll' (&amp;gt;= 0), here is why:
       	Unable to download data from https://rubygems.org/ - SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (https://api.rubygems.org/latest_specs.4.8.gz)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="http://stackoverflow.com/a/27641786/119230"&gt;This SO answer&lt;/a&gt; helped and I was able to install Jekyll after that.&lt;/p&gt;
&lt;p&gt;Since I'm referencing few Ruby gems in &lt;code&gt;_config.yml&lt;/code&gt; I had to install those too:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bat"&gt;gem install jekyll-sitemap
gem install jekyll-redirect-from
gem install jemoji
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, running the following command started the build process and enabled file change watcher. Success!&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bat"&gt;jekyll serve --watch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I tested several themes locally, before settling up with Pixyll theme.&lt;/p&gt;
&lt;h2 id="creating-user-repository"&gt;Creating user repository&lt;/h2&gt;
&lt;p&gt;There's an easy getting started guide on &lt;a href="http://www.smashingmagazine.com/2014/08/01/build-blog-jekyll-github-pages/"&gt;Smashing Magazine&lt;/a&gt;. I ended up with &lt;a href="https://github.com/miroslavpopovic/miroslavpopovic.github.io"&gt;user repository&lt;/a&gt; forked from John Otander's &lt;a href="https://github.com/johnotander/pixyll"&gt;Pixyll&lt;/a&gt; theme, instead of &lt;a href="https://github.com/barryclark/jekyll-now"&gt;Jekyll Now&lt;/a&gt; that's described in the guide above.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;_config.yml&lt;/code&gt; file &lt;code&gt;permalink&lt;/code&gt; is set to &lt;code&gt;pretty&lt;/code&gt;. That's what I want it to be, since I prefer to &lt;a href="http://jekyllrb.com/docs/permalinks/#built-in-permalink-styles"&gt;have date in URL and no extension&lt;/a&gt;. My blog posts didn't have date in URL before, so I had also added &lt;code&gt;jekyll-redirect-from&lt;/code&gt; gem to &lt;code&gt;gems&lt;/code&gt; section. With this gem installed, you can tell Jekyll to create redirects from defined URL to the current page or post. This is done with Front Matter instruction on top of the post. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
layout: post
title: Resurrection
redirect_from: &amp;quot;/resurrection/&amp;quot;
---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the &lt;code&gt;redirect_from&lt;/code&gt; value needs to have trailing slash, otherwise GitHub Pages will serve the generated document as file download instead of a regular page, causing browsers to download it. Hopefully, this will not cause SEO problems since my previous URLs were without the trailing slash.&lt;/p&gt;
&lt;p&gt;GitHub itself can serve as an editor for your posts. Since there were only nine posts on my blog, it was not necessary to create some kind of migration tool. I just added them one by one through GitHub repository (they were already in Markdown format).&lt;/p&gt;
&lt;p&gt;One thing that needed to be changed was the format of links. Markdown supports &lt;a href="http://daringfireball.net/projects/markdown/syntax#link"&gt;two types of links&lt;/a&gt; inline and reference. I had reference links in my posts. They were processed correctly on post pages, but post excerpts on home page did not process them - they were displayed in markdown syntax. After switching to inline links it worked correctly.&lt;/p&gt;
&lt;h2 id="domain-redirection"&gt;Domain redirection&lt;/h2&gt;
&lt;p&gt;My old blog was hosted on &lt;a href="https://billing.aspnix.com/aff.php?aff=079"&gt;ASPnix&lt;/a&gt; under a custom subdomain. Following the &lt;a href="https://help.github.com/articles/setting-up-a-custom-domain-with-github-pages/"&gt;GitHub guide&lt;/a&gt;, I had to delete the subdomain first and create a custom &lt;code&gt;cname&lt;/code&gt; record for &lt;code&gt;blog&lt;/code&gt; that points to &lt;code&gt;miroslavpopovic.github.io&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Also, &lt;a href="https://feedburner.google.com/"&gt;FeedBurner&lt;/a&gt; had to be redirected to use &lt;code&gt;/feed.xml&lt;/code&gt; file instead of &lt;code&gt;/feed&lt;/code&gt;. Users that were subscribed to my RSS feed will get one time refresh of all items, since post ids are different.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://disqus.com/"&gt;Disqus&lt;/a&gt; was used for my blog comments and since my post URLs are changed, I also needed to create the map between old and new URLs. There's a migrate tool in Admin area of Disqus for that. To get to Admin area, log in to Disqus, and select Admin from your profile drop-down menu on top right. Select your blog if not already selected and go to Discussion / Tools / Upload a URL map. You'll find the instructions on how to create CSV map file and upload it.&lt;/p&gt;
&lt;h2 id="final-words"&gt;Final words&lt;/h2&gt;
&lt;p&gt;That's it. Migration was relatively straightforward with few problems here and there. My blog is now a Jekyll generated static site hosted on GitHub Pages.&lt;/p&gt;
&lt;p&gt;GitHub repository for my blog is &lt;a href="https://github.com/miroslavpopovic/miroslavpopovic.github.io"&gt;available here&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>ECMAScript 2015 (ES6) Presentation</title>
			<link>https://miroslavpopovic.com/posts/2015/03/ecmascript-2015-es6-presentation</link>
			<description>&lt;p&gt;Ten days ago, I held a talk "ECMAScript 2015 (ES6)" on our local user group meeting. Slides on English and Serbian language, with examples, can be found here: &lt;a href="http://miroslavpopovic.github.io/ecmascript6/"&gt;http://miroslavpopovic.github.io/ecmascript6/&lt;/a&gt;.&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2015/03/ecmascript-2015-es6-presentation</guid>
			<pubDate>Sun, 08 Mar 2015 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;Ten days ago, I held a talk &amp;quot;ECMAScript 2015 (ES6)&amp;quot; on our local user group meeting. Slides on English and Serbian language, with examples, can be found here: &lt;a href="http://miroslavpopovic.github.io/ecmascript6/"&gt;http://miroslavpopovic.github.io/ecmascript6/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks to all who attended.&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>MS Community 2014 Presentation</title>
			<link>https://miroslavpopovic.com/posts/2014/09/ms-community-2014-presentation</link>
			<description>&lt;p&gt;I held a talk "Building modern web in Microsoft world" on this year's &lt;a href="http://konferencija.mscommunity.ba/"&gt;Microsoft Community conference&lt;/a&gt;. Thanks for everyone who attended. The conference was great as always. &lt;a href="https://twitter.com/ddamirMVP"&gt;Damir&lt;/a&gt; and &lt;a href="https://twitter.com/sahinovic"&gt;Enis&lt;/a&gt; did a great job organizing it.&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2014/09/ms-community-2014-presentation</guid>
			<pubDate>Fri, 19 Sep 2014 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;I held a talk &amp;quot;Building modern web in Microsoft world&amp;quot; on this year's &lt;a href="http://konferencija.mscommunity.ba/"&gt;Microsoft Community conference&lt;/a&gt;. Thanks for everyone who attended. The conference was great as always. &lt;a href="https://twitter.com/ddamirMVP"&gt;Damir&lt;/a&gt; and &lt;a href="https://twitter.com/sahinovic"&gt;Enis&lt;/a&gt; did a great job organizing it.&lt;/p&gt;
&lt;p&gt;You can find the presentation and a sample on my &lt;a href="https://bitbucket.org/miroslavpopovic/web-ms-demo/"&gt;BitBucket account&lt;/a&gt;. Thanks!&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>Time to learn AngularJS</title>
			<link>https://miroslavpopovic.com/posts/2014/04/time-to-learn-angularjs</link>
			<description>&lt;p&gt;A week ago, Rob Eisenberg &lt;a href="http://eisenbergeffect.bluespire.com/angular-and-durandal-converge/"&gt;announced&lt;/a&gt; that the future version of &lt;a href="http://durandaljs.com/"&gt;Duranda&lt;/a&gt;l will be called &lt;a href="http://angularjs.org/"&gt;AngularJS&lt;/a&gt; 2.0. What seemed to be a late April 1. joke proved to be true. Rob joined AngularJS core team as a full-time external contractor and they are already on the way, building the web's most advanced framework.&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2014/04/time-to-learn-angularjs</guid>
			<pubDate>Tue, 22 Apr 2014 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;A week ago, Rob Eisenberg &lt;a href="http://eisenbergeffect.bluespire.com/angular-and-durandal-converge/"&gt;announced&lt;/a&gt; that the future version of &lt;a href="http://durandaljs.com/"&gt;Duranda&lt;/a&gt;l will be called &lt;a href="http://angularjs.org/"&gt;AngularJS&lt;/a&gt; 2.0. What seemed to be a late April 1. joke proved to be true. Rob joined AngularJS core team as a full-time external contractor and they are already on the way, building the web's most advanced framework.&lt;/p&gt;
&lt;p&gt;This was a mind blowing news, at least for me. A little over a year ago, I chose Durandal to be my front-end framework of choice. Usually, I lean toward the most used framework / library, and Angular was (and still is) on the fire. Having widespread adoption and a huge community, Angular looked like a clear winner.&lt;/p&gt;
&lt;p&gt;However, there was Durandal, a small framework built by one of the most inspiring developers. It used &lt;a href="http://knockoutjs.com/"&gt;Knockout&lt;/a&gt; library for data binding and just built an MV* framework around it. Since I was a long time Knockout user, Durandal was so natural and easy to start with.&lt;/p&gt;
&lt;p&gt;As always, Rob (and other contributors) did an excellent job. I've always admired him, ever since I tried old &lt;a href="https://caliburn.codeplex.com/"&gt;Caliburn&lt;/a&gt; MVVM framework for WPF. He bought me completely with an inspiring talk on &lt;a href="http://channel9.msdn.com/Events/MIX/MIX10/EX15"&gt;building your own MVVM framework&lt;/a&gt; which later become &lt;a href="http://www.caliburnproject.org/"&gt;Caliburn.Micro&lt;/a&gt;, a framework without which I cannot think of doing any XAML based app.&lt;/p&gt;
&lt;p&gt;The amount of code you &lt;strong&gt;don't&lt;/strong&gt; need to write when using Caliburn.Micro in your XAML apps is simply astonishing. Convention over configuration at it's best. Bindings everywhere. No need for all those ceremonial code, like IDelegateCommand etc. that other MVVM frameworks have. Just write your plain and simple .NET code. Heck, it even had &lt;a href="https://caliburnmicro.codeplex.com/wikipage?title=IResult%20and%20Coroutines&amp;amp;referringTitle=Documentation"&gt;coroutines&lt;/a&gt; since day 1, which are basically async/await pattern, way before Microsoft brought it to .NET framework. Amazing.&lt;/p&gt;
&lt;p&gt;The same concepts are implemented in Durandal. Again, convention over configuration rules. There is a little to none infrastructure code you need to write in your view models / controllers. Just write simple JavaScript modules and let Durandal handle the rest.&lt;/p&gt;
&lt;p&gt;I used Durandal with success on a &lt;a href="http://silverreader.com/"&gt;private project&lt;/a&gt; and also on several client projects at work, so I can consider myself as an intermediate user. Now I'll try to deep dive into current version of Angular, to gain a perspective on what to expect in Angular 2.0. It might be better to wait for 2.0 and start learning then, since there will be &lt;a href="http://blog.angularjs.org/2014/03/angular-20.html"&gt;considerable changes&lt;/a&gt;, but I'm a sucker for learning. Besides, I would like to compare the philosophies behind both frameworks before that.&lt;/p&gt;
&lt;p&gt;Now if I could only think of some small project to play around with...&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>An advanced guide to HTML and CSS</title>
			<link>https://miroslavpopovic.com/posts/2014/01/an-advanced-guide-to-html-and-css</link>
			<description>&lt;p&gt;Lately, I'm catching up with my reading list - have a bunch of links bookmarked over the last year or so. Found a little gem today that I would like to share...&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2014/01/an-advanced-guide-to-html-and-css</guid>
			<pubDate>Sun, 19 Jan 2014 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;Lately, I'm catching up with my reading list - have a bunch of links bookmarked over the last year or so. Found a little gem today that I would like to share...&lt;/p&gt;
&lt;p&gt;&lt;a href="http://shayhowe.com/"&gt;Shay Howe&lt;/a&gt; put together a fantastic guide on HTML and CSS called &lt;a href="http://learn.shayhowe.com/advanced-html-css/"&gt;An Advanced Guide to HTML &amp;amp; CSS&lt;/a&gt;. Even seasoned front-end developers can learn a thing or two from it. Highly recommended!&lt;/p&gt;
&lt;p&gt;Oh yeah, and there's also &lt;a href="http://learn.shayhowe.com/html-css/"&gt;A Beginner's Guide to HTML &amp;amp; CSS&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>Rise of the SilverReader</title>
			<link>https://miroslavpopovic.com/posts/2013/06/rise-of-the-silverreader</link>
			<description>&lt;p&gt;Yes, you read that right. Rise of the &lt;a href="http://silverreader.com/"&gt;SilverReader&lt;/a&gt;, not &lt;a href="http://www.imdb.com/title/tt0486576/"&gt;Rise of the Silver Surfer&lt;/a&gt;.&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2013/06/rise-of-the-silverreader</guid>
			<pubDate>Fri, 28 Jun 2013 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;Yes, you read that right. Rise of the &lt;a href="http://silverreader.com/"&gt;SilverReader&lt;/a&gt;, not &lt;a href="http://www.imdb.com/title/tt0486576/"&gt;Rise of the Silver Surfer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Right after &lt;a href="http://googleblog.blogspot.com/2013/03/a-second-spring-of-cleaning.html"&gt;the news&lt;/a&gt; that shocked the world (at least our world) my friend &lt;a href="http://blog.developers.ba/"&gt;Radenko Zec&lt;/a&gt; suggested that we build an alternative to Google Reader. So, few months ago, together with another friend of ours &lt;a href="http://igormarkovic.com/"&gt;Igor Markovic&lt;/a&gt;, we started working on it and apparently, we &lt;a href="http://techcrunch.com/2013/06/24/there-is-no-google-reader-replacement-only-alternatives/"&gt;weren’t alone&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And now, just few days before our beloved Google Reader shuts down, we have published our &lt;a href="http://silverreader.com"&gt;first live beta version&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;You can read the &lt;a href="http://silverreader.wordpress.com/2013/06/28/going-live/"&gt;official announcement&lt;/a&gt; on &lt;a href="http://silverreader.wordpress.com/"&gt;SilverReader blog&lt;/a&gt;. Also, Radenko has made a &lt;a href="http://blog.developers.ba/post/2013/06/28/SilverReader-ultra-fast-Google-Reader-alternative.aspx"&gt;blog post&lt;/a&gt; explaining a technical side of the story.&lt;/p&gt;
&lt;p&gt;Try the &lt;a href="http://silverreader.com"&gt;SilverReader&lt;/a&gt; now. If you need any help, or want to submit an idea or issue, visit &lt;a href="https://silverreader.uservoice.com/knowledgebase/"&gt;SilverReader Knowledge Base&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>MS NetWork 2013 Presentation and Samples</title>
			<link>https://miroslavpopovic.com/posts/2013/04/ms-network-2013-presentation-and-samples</link>
			<description>&lt;p&gt;&lt;a href="http://msnetwork.ba"&gt;Microsoft NetWork 2013&lt;/a&gt; conference was great. It's the biggest IT conference in Bosnia and Herzegovina, held in &lt;a href="http://en.wikipedia.org/wiki/Tesli%C4%87"&gt;Teslić&lt;/a&gt; this year. I had a chance to meet some pretty cool people. Thanks to organizers for making an excellent event.&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2013/04/ms-network-2013-presentation-and-samples</guid>
			<pubDate>Fri, 05 Apr 2013 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;&lt;a href="http://msnetwork.ba"&gt;Microsoft NetWork 2013&lt;/a&gt; conference was great. It's the biggest IT conference in Bosnia and Herzegovina, held in &lt;a href="http://en.wikipedia.org/wiki/Tesli%C4%87"&gt;Teslić&lt;/a&gt; this year. I had a chance to meet some pretty cool people. Thanks to organizers for making an excellent event.&lt;/p&gt;
&lt;p&gt;My friend &lt;a href="http://blog.developers.ba/"&gt;Radenko Zec&lt;/a&gt; and I held a talk there - &amp;quot;Easy Transition to HTML5 using MVVM&amp;quot;. Thanks to everyone who attended. We didn't expect the full room. Hopefully, the talk wasn't too boring. For anyone interested, the presentation and sample code can be found on GitHub: &lt;a href="https://github.com/miroslavpopovic/easy-transition-sample"&gt;https://github.com/miroslavpopovic/easy-transition-sample&lt;/a&gt;. The presentation is in local language, but the samples are in English.&lt;/p&gt;
&lt;p&gt;We had an interesting question about organizing JavaScript view models when using Knockout. We suggested &lt;a href="http://durandaljs.com/"&gt;Durandal&lt;/a&gt; as an excellent new framework for that. For alternatives, take a look at the great &lt;a href="http://net.tutsplus.com/"&gt;TutsPlus&lt;/a&gt; article, posted few days ago: &lt;a href="http://net.tutsplus.com/tutorials/javascript-ajax/building-large-maintainable-and-testable-knockout-js-applications/"&gt;Building Large, Maintainable, and Testable Knockout.js Applications&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>jQuery UI Autocomplete - Filter words starting with term</title>
			<link>https://miroslavpopovic.com/posts/2012/06/jqueryui-autocomplete-filter-words-starting-with-term</link>
			<description>&lt;p&gt;I had an interested problem with &lt;a href="http://jqueryui.com/demos/autocomplete/"&gt;jQuery UI Autocomplete&lt;/a&gt; yesterday. The client required that the search / filtering should be done from the beginning of the word only. By default, jQuery UI Autocomplete filters through the list matching the term in the middle of the word.&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2012/06/jqueryui-autocomplete-filter-words-starting-with-term</guid>
			<pubDate>Sat, 23 Jun 2012 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;I had an interested problem with &lt;a href="http://jqueryui.com/demos/autocomplete/"&gt;jQuery UI Autocomplete&lt;/a&gt; yesterday. The client required that the search / filtering should be done from the beginning of the word only. By default, jQuery UI Autocomplete filters through the list matching the term in the middle of the word.&lt;/p&gt;
&lt;p&gt;There &lt;a href="http://stackoverflow.com/questions/2382497/jquery-autocomplete-plug-in-search-configuration"&gt;are&lt;/a&gt; &lt;a href="http://forum.jquery.com/topic/select-only-items-that-start-with-jquery-ui-autocomplete"&gt;solutions&lt;/a&gt; with using function as an Autocomplete source. It would be OK if we weren't using &lt;a href="http://stackoverflow.com/questions/7537002/autocomplete-combobox-with-knockout-js-template-jquery"&gt;jqAuto&lt;/a&gt; &lt;a href="http://knockoutjs.com/"&gt;Knockout&lt;/a&gt; binding by &lt;a href="http://www.knockmeout.net/"&gt;RP Niemeyer&lt;/a&gt; which already takes over the Autocomplete source option.&lt;/p&gt;
&lt;p&gt;The solution was pretty easy though. After searching thru jQuery UI's source, I found the &lt;code&gt;filter&lt;/code&gt; method used just for that. All that needs to be done is replacing that function with this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-javascript"&gt;// Overrides the default autocomplete filter function to
// search only from the beginning of the string
$.ui.autocomplete.filter = function (array, term) {
  var matcher = new RegExp(&amp;quot;^&amp;quot; + $.ui.autocomplete.escapeRegex(term), &amp;quot;i&amp;quot;);
  return $.grep(array, function (value) {
    return matcher.test(value.label || value.value || value);
  });
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is actually the copy of the default jQuery UI's &lt;code&gt;filter&lt;/code&gt; method with a single change. Regex is prefixed with &amp;quot;^&amp;quot; sign which matches the beginning of the string.&lt;/p&gt;
&lt;p&gt;Please note that this is a global change. It will affect all autocomplete boxes on your page.&lt;/p&gt;
&lt;p&gt;Here's a fiddle for that, if you want to play around (just try commenting / uncommenting the &lt;code&gt;filter&lt;/code&gt; method):&lt;/p&gt;
&lt;iframe style="width: 100%; height: 300px" src="http://jsfiddle.net/miroslav/yLdn3/embedded/" allowfullscreen="allowfullscreen" frameborder="0"&gt;&lt;/iframe&gt;
</content:encoded>
		</item>
		<item>
			<title>Using FluentMigrator with MSBuild</title>
			<link>https://miroslavpopovic.com/posts/2012/06/using-fluentmigrator-with-msbuild</link>
			<description>&lt;p&gt;I have been playing with &lt;a href="https://github.com/schambers/fluentmigrator"&gt;FluentMigrator&lt;/a&gt; lately, trying to implement the features I had with my old, SQL script based tool.&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2012/06/using-fluentmigrator-with-msbuild</guid>
			<pubDate>Tue, 12 Jun 2012 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;I have been playing with &lt;a href="https://github.com/schambers/fluentmigrator"&gt;FluentMigrator&lt;/a&gt; lately, trying to implement the features I had with my old, SQL script based tool.&lt;/p&gt;
&lt;p&gt;My plan was to write a blog post about integrating &lt;a href="https://github.com/schambers/fluentmigrator"&gt;FluentMigrator&lt;/a&gt; with &lt;a href="http://msdn.microsoft.com/en-us/library/0k6kkbsd.aspx"&gt;MSBuild&lt;/a&gt;, but the size kind of went out of hand. Instead of splitting it to multiple blog posts, I decided to write a &lt;a href="http://www.codeproject.com/"&gt;CodeProject&lt;/a&gt; article instead.&lt;/p&gt;
&lt;p&gt;Here's the link if you are interested: &lt;a href="http://www.codeproject.com/Articles/402430/Using-FluentMigrator-with-MSBuild"&gt;http://www.codeproject.com/Articles/402430/Using-FluentMigrator-with-MSBuild&lt;/a&gt;&lt;/p&gt;
</content:encoded>
		</item>
		<item>
			<title>Resurrection</title>
			<link>https://miroslavpopovic.com/posts/2012/06/resurrection</link>
			<description>&lt;p&gt;A lot of things have happen last two-three years for me. I did some experiments (business ones, not with drugs :) ), that proved unsuccessful. During this period I neglected some important and other not so important things. Now I'm trying to get back and reset.&lt;/p&gt;</description>
			<guid>https://miroslavpopovic.com/posts/2012/06/resurrection</guid>
			<pubDate>Sun, 10 Jun 2012 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;A lot of things have happen last two-three years for me. I did some experiments (business ones, not with drugs :) ), that proved unsuccessful. During this period I neglected some important and other not so important things. Now I'm trying to get back and reset.&lt;/p&gt;
&lt;p&gt;One of neglected things is blogging. So, in my reset mode, I have decided to abandon &lt;a href="http://codemind.blogspot.com/"&gt;Blogger&lt;/a&gt; and start fresh on a new platform - &lt;a href="http://www.funnelweblog.com/"&gt;FunnelWeb&lt;/a&gt;. Currently, a default FunnelWeb theme is used. I plan to tweak it up a little when I get time and/or inspiration.&lt;/p&gt;
&lt;p&gt;Old blog posts aren't imported. If you are interested in some of my older ramblings, go to &lt;a href="http://codemind.blogspot.com/"&gt;codemind.blogspot.com&lt;/a&gt;. This new blog also has a new feed, so please update your feed readers: &lt;a href="http://feeds.feedburner.com/miroslavpopovic/blog"&gt;http://feeds.feedburner.com/miroslavpopovic/blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Enough of gibberish. What's going on now?&lt;/p&gt;
&lt;p&gt;Lately, I'm doing a lot of web development, improving my JavaScript skills and trying to follow a large community behind HTML5. While doing that, I have collected a bunch of links to various web development resources. I got tired of having all those as browser shortcuts, so in an effort to organize it a bit, &lt;a href="http://miroslavpopovic.com/resources/web/"&gt;Web Resources&lt;/a&gt; page was made. Hopefully, you'll find something useful there. I'm planing to maintain that list in a future. You can also fork it on &lt;a href="https://github.com/miroslavpopovic/resources"&gt;GitHub&lt;/a&gt;, if you want to base your own list on it.&lt;/p&gt;
&lt;p&gt;Also, expect a link to my new CodeProject article.&lt;/p&gt;
&lt;p&gt;See you soon (hopefully).&lt;/p&gt;
</content:encoded>
		</item>
	</channel>
</rss>