Scott Hanselman

Dealing with Application Base URLs and Razor link generation while hosting ASP.NET web apps behind Reverse Proxies

July 9, '19 Comments [4] Posted in ASP.NET | Azure | DasBlog | Open Source
Sponsored By

Updating my site to run on AzureI'm quietly moving my Website from a physical machine to a number of Cloud Services hosted in Azure. This is an attempt to not just modernize the system - no reason to change things just to change them - but to take advantage of a number of benefits that a straight web host sometimes doesn't have. I want to have multiple microsites (the main page, the podcast, the blog, etc) with regular backups, CI/CD pipeline (check in code, go straight to staging), production swaps, a global CDN for content, etc.

I'm also moving from an ASP.NET 4 (was ASP.NET 2 until recently) site to ASP.NET Core 2.x LTS and changing my URL structure. I am aiming to save money but I'm not doing this as a "spend basically nothing" project. Yes, I could convert my site to a static HTML generated blog using any number of great static site generators, or even a Headless CMS. Yes I could host it in Azure Storage fronted by a CMS, or even as a series of Azure Functions. But I have 17 years of content in DasBlog, I like DasBlog, and it's being actively updated to .NET Core and it's a fun app. I also have custom Razor sites in the form of my podcast site and they work great with a great workflow. I want to find a balance of cost effectiveness, features, ease of use, and reliability.  What I have now is a sinking feeling like my site is gonna die tomorrow and I'm not ready to deal with it. So, there you go.

Currently my sites live on a real machine with real folders and it's fronted by IIS on a Windows Server. There's an app (an IIS Application, to be clear) leaving at \ so that means hanselman.com/ hits / which is likely c:\inetpub\wwwroot full stop.

For historical reasons, when you hit hanselman.com/blog/ you're hitting the /blog IIS Application which could be at d:\whatever but may be at c:\inetpub\wwwroot\blog or even at c:\blog. Who knows. The Application and ASP.NET within it knows that the site is at hanselman.com/blog.

That's important, since I may write a URL like ~/about when writing code. If I'm in the hanselman.com/blog app, then ~/about means hanselman.com/blog/about. If I write /about, that means hanselman.com/about. So the ~ is a shorthand for "starting at this App's base URL." This is great and useful and makes Link generation super easy, but it only works if your app knows what it's server-side base URL is.

To be clear, we are talking about the reality of the generated URL that's sent to and from the browser, not about any physical reality on the disk or server or app.

I've moved my world to three Azure App Services called hanselminutes, hanselman, and hanselmanblog. They have names like http://hanselman.azurewebsites.net for example.

ASIDE: You'll note that hitting hanselman.azurewebsites.com will hit an app that looks stopped. I don't want that site to serve traffic from there, I want it to be served from http://hanselman.com, right? Specifically only from Azure Front Door which I'll talk about in another post soon. So I'll use the Access Restrictions and Software Based Networking in Azure to deny all traffic to that site, except traffic from Azure - in this case, from the Azure Front Door Reverse Proxy I'll be using.

That looks like this in this Access Restrictions part of the Azure Portal.

Only allowing traffic from Azure

Since the hanselman.com app will point to hanselman.azurewebsites.net (or one of its staging slots) there's no issue with URL generation. If I say / I mean /, the root of the site. If I generate a URL like "~/about" I'll get hanselman.com/about, right?

But with http://hanselmanblog.azurewebsites.net it's different.

I want hanselman.com/blog/ to point to hanselmanblog.azurewebsites.net.

That means that the Azure Front Door will be receiving traffic, then forward it on to the Azure Web App. That means:

  • hanselman.com/blog/foo -> hanselmanblog.azurewebsites.net/foo
  • hanselman.com/blog/bar -> hanselmanblog.azurewebsites.net/foo
  • hanselman.com/blog/foo/bar/baz -> hanselmanblog.azurewebsites.net/foo/bar/baz

There's a few things to consider when dealing with reverse proxies like this.

Is part of the /path being removed or is a path being added?

In the case of DasBlog, we have a configuration setting so that the app knows where it LOOKS like it is, from the Browser URL's perspective.

My blog is at /blog so I add that in some middleware in my Startup.cs. Certainly YOU don't need to have this in config - do whatever works for you as long as context.Request.PathBase is set as the app should see it. I set this very early in my pipeline.

That if statement is there because most folks don't install their blog at /blog, so it doesn't add the middleware.

//if you've configured it at /blog or /whatever, set that pathbase so ~ will generate correctly
Uri rootUri = new Uri(dasBlogSettings.SiteConfiguration.Root);
string path = rootUri.AbsolutePath;

//Deal with path base and proxies that change the request path
if (path != "/")
{
app.Use((context, next) =>
{
context.Request.PathBase = new PathString(path);
return next.Invoke();
});
}

Sometimes you want the OPPOSITE of this. That would mean that I wanted, perhaps hanselman.com to point to hanselman.azurewebsites.net/blog/. In that case I'd do this in my Startup.cs's ConfigureServices:

app.UsePathBase("/blog");

Be aware that If you're hosting ASP.NET Core apps behind Nginx or Apache or really anything, you'll also want ASP.NET Core to respect  X-Forwarded-For and other X-Forwarded standard headers. You'll also likely want the app to refuse to speak to anyone who isn't a certain list of proxies or configured URLs.

I configure these in Startup.cs's ConfigureServices from a semicolon delimited list in my config, but you can do this in a number of ways.

services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.All;
options.AllowedHosts = Configuration.GetValue<string>("AllowedHosts")?.Split(';').ToList<string>();
});

Since Azure Front Door adds these headers as it forwards traffic, from my app's point of view it "just works" once I've added that above and then this in Configure()

app.UseForwardedHeaders();

There seems to be some confusion on hosting behind a reverse proxy in a few GitHub Issues. I'd like to see my scenario ( /foo -> / ) be a single line of code, as we see that the other scenario ( / -> /foo ) is a single line.

Have you had any issues with URL generation when hosting your Apps behind a reverse proxy?


Sponsor: Develop Xamarin applications without difficulty with the latest JetBrains Rider: Xcode integration, JetBrains Xamarin SDK, and manage the required SDKs for Android development, all right from the IDE. Get it today

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Real World Cloud Migrations: Moving a 17 year old series of sites from bare metal to Azure

July 2, '19 Comments [6] Posted in ASP.NET | Azure | DasBlog
Sponsored By

Technical Debt has a way of sneaking up on you. While my podcast site and the other 16ish sites I run all live in Azure and have a nice CI/CD pipeline with Azure DevOps, my main "Hanselman.com" series of sites and mini-sites has lagged behind. I'm still happy with its responsive design, but the underlying tech has started to get more difficult to manage and build and I've decided it's time to make some updates.

Moving sites to Azure DevOps

I want to be able to make these updates and have a clean switch over so that you, the reader, don't notice a difference. There's a number of things to think about when doing any migration like this, realizing it'll take some weeks (or months if you're a bigger company that just me).

  • Continuous Deployment/Continuous Integration
    • I host my code on GitHub and Azure DevOps now lets you log in with GitHub and does a fine job of building AND deploying your code (while running tests AND allowing for manual quality gates) so I want to make sure my sites have a nice clean "check in and go live" process.
    • I'll also be using Azure App Services and Deployment Slots, so I'll have a dev/test/staging site and production, like a real professional. No more editing text files in production. Well, at least, I won't tell you when I'm editing text file in production.
  • Technology Update
    • Hanselman.com proper (not the blog) and the mini pages/sites underneath it run on ASP.NET 4.0 and WebForms. I was able to easily move the main site over to ASP.NET Razor Pages. Razor is just so elegant, as it's basically just HTML then you type @ and you're in C# (Razor). More on that below, but the upgrade was a day as the home page and minisites are largely readonly.
    • The Blog, hosted at /blog will be more challenging given I don't want to break two decades years of URLs, along with the fact that it's running DasBlog on a recently upgraded .NET 4.0. DasBlog was originally made in .NET 1, then upgraded to .NET 2, so this is 17 years of technical debt.
    • That said, the .NET Standard along with open source cross-platform .NET Core has allowed us - with the leadership of Mark Downie - to create DasBlog Core. DasBlog Core shares the core reliable (if crusty) engine of DasBlog along with an all new system of URL writing using ASP.NET Core middleware, as well as a complete re-do of the (well ahead of its time) DasBlog Theming Engine, now based on Razor Pages. It's brilliant. This is in active development.
  • Azure Front Door
    • Because I'm moving from a single machine running IIS to Azure, I'll want to split things apart to remove single points of failture. I'll use Azure Front Door to manage my URL structure and act as a front end cache as well as distribute traffic to multiple Azure App Services (Web Apps).
  • URL management
    • Are you changing your URLs and URL structure? Remember that URLs are UI and they matter. I've long wanted to remove the "aspx" extension from my URLs, as well as move the TitleCaseBlogPostThing to a more "modern" title-case-blog-post-thing style. I need to do this in a way that updates my google sitemap, breaks zero URLs, 301 redirects to the new style, and uses rel=canonical in a smart way.
  • Shared Assets/CDNs/Front Door
    • Since I run a family of sites, there's an opportunity to use a CDN as well and some clean CNAME DNS such that images.hanselman.com and images.hanselminutes.com can share assets. Since the Azure CDN is easy to setup and offers free SSL certs and pay-as-you go, I'll set both of those CNAMES up to point to the same Azure Storage where I'll keep images, show pics, CSS, and JS.

I'll be blogging the whole process. What do you want to hear/learn about?


Sponsor: Seq delivers the diagnostics, dashboarding, and alerting capabilities needed by modern development teams - all on your infrastructure. Download now.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Git is case-sensitive and your filesystem may not be - Weird folder merging on Windows

June 27, '19 Comments [7] Posted in DasBlog | Open Source
Sponsored By

I was working on DasBlog Core (an .NET Core cross-platform update of the ASP.NET WebForms-based blogging software that runs this blog) with Mark Downie, the new project manager, and Shayne Boyer. This is part of a larger cloud re-architecture of hanselman.com and the systems that run this whole site.

Shayne was working on getting a DasBlog Core CI/CD (Continuous Integration/Continuous Development) running in Azure DevOps' build system. We wanted individual build pipelines to confirm that DasBlog Core was in fact, cross-platform, so we needed to build, test, and run it on Windows, Linux, and Mac.

The build was working great on Windows and Mac...but failing on Linux. Why?

Well, like all things, it's complex.

  • Windows has a case-insensitive file system.
  • By default, Mac uses a case-insensitive file system.

Since Git 1.5ish there's been a setting

git config --global core.ignorecase true

but you should always be aware of what a setting does before you just set it.

If you're not careful, you or someone on your team can create a case sensitive file path in your git index while you're using a case insensitive operating system like Windows or Mac. If you do this, you'll be able to end up with two separate entries from git's perspective. However Windows will silently merge them and see just one.

Here's our themes folder structure as seen on GitHub.com.

Case insenstive folder names

But when we clone it on Mac or Windows, we see just one folder.

DasBlog as a single folder in VS Code

Turns out that six months ago one of us introduced another folder with the name dasblog while the original was DasBlog. When we checked them on Mac or Windows the files ended up in merged into one folder, but on Linux they were/are two, so the build fails.

You can fix this in a few ways. You can rename the file in a case-sensitive way and commit the change:

git mv --cached name.txt NAME.TXT

Please take care and back up anything you don't understand.

If you're renaming a directory, you'll do a two stage rename with a temp name.

git mv foo foo2
git mv foo2 FOO
git commit -m "changed case of dir"

Be safe out there!


Sponsor: Looking for a tool for performance profiling, unit test coverage, and continuous testing that works cross-platform on Windows, macOS, and Linux? Check out the latest JetBrains Rider!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Microsoft Web Platform, Web Application Gallery, Web Platform Installer (and DasBlog)

March 18, '09 Comments [18] Posted in ASP.NET | DasBlog | IIS | Mix | Open Source | Tools
Sponsored By

Slash WebIt's so fun to help "announce" things, especially things that are a long time coming and that a lot of people worked on to make things "just work." (You can follow them on Twitter @mswebplatform.)

This is one of those fantastic things. Today, you can install free applications direct to IIS using the Web Platform Installer 2.0 and the Web Application Gallery. The WebPI will also check for application dependencies, and if you like, automatically download and install them. It'll install SQL Express, PHP, whatever you need to get your apps running.

For example, you can now install DasBlog just by clicking this button:

Install DasBlog now with the Web Platform Installer

There's a bunch of cool apps available in the gallery immediately, both ASP.NET and PHP (remember that IIS kicks ass at hosting PHP):

This gallery sits on top of some technologies that have been slowly but surely sneaking out of the most-awesome IIS team lately.

How's it work?

Here's some technical details. You can install free apps a few ways, and they are all easy.

  • You can visit the Web App Gallery, find an app and click install. If you've got the Web Platform Installer it'll launch out of the browser. (Don't worry, it's not a browser plugin, it's more like iTunes. It'll "leap out of the browser" from a link.)
  • You can open IIS manager on your machine after installing WebPI, and click "Install Application from Gallery." This feature is awesome enough to require a screenshot to drink in:image
  • Or, just run the Web Platform Installer and browse around the catalog. The list of apps comes down as an Atom Feed.

Hey, isn't that dasBlog in the list there? Why, yes! ;)

Packaging Your App - Technical Details - DasBlog

You can upload YOUR free app to the gallery. Start by downloading the Application Developer's Kit. We packaged up DasBlog 2.3 like this. You install DasBlog

The package is a zip file. Inside at the root are two files, manifest.xml and parameters.xml. You start by "exporting" your app already working in IIS. Then, if your app has specific needs you can edit the files manually.

For example, here's our manifest.xml, which is an MSDeploy file:

<MSDeploy.iisApp>

<!-- Copies content from path into the destination specified in parameters.xml and marks as an app in IIS 5.1, 6.0 and 7.x -->
<iisapp path="dasblogce" />

<!-- Set NTFS ACLs on the destination specified in parameters.xml -->
<setAcl path="dasblogce/content" setAclAccess="Read,Write,Delete" />
<setAcl path="dasblogce/logs" setAclAccess="Read,Write,Delete" />
<setAcl path="dasblogce/siteconfig" setAclAccess="Read,Write,Delete" />

</MSDeploy.iisApp>

Remember that DasBlog doesn't use a database, but rather stores all data in XML files in the content folder. Our MSDeploy file sets ACLs on a few directories to allow "Read,Write,Delete." This file encapsulates a potentially tricky setup step for DasBlog.

The parameters.xml is a little more sophisticated and drives a dynamic UI form inside of the Web Platform Installer 2.0.

For example, the blog owner's email is a parameter, and it's entered in two files. Since DasBlog uses XML files, we can use XPath to describe where to poke the values in.

<!-- Prompts for admin email -->
<parameter name="Email Address" description="Enter the blog owner's email address." defaultValue="dasblog@example.com">
<parameterEntry type="XmlFile" scope="dasblogce\\siteconfig\\site.config" match="//MSDeploy:Contact/text()" />
<parameterEntry type="XmlFile" scope="dasblogce\\siteconfig\\siteSecurity.config" match="//EmailAddress/text()" />
</parameter>

Here's what the generated UI looks like:

image

You can read about this format in the Application Developer's Kit. You can parameterize your SQL files as well, it's a very flexible format.

Then we zipped up the deployment and uploaded it to CodePlex. The CodePlex site is smart enough to notice when the Platform Installer is requesting a release (it sniffs the User-Agent) and serves up the file rather than the web site, so I can use CodePlex to host the zip. (It would have sucked to have to host somewhere else.)

MSDeploy -> WebDeploy -> Web Platform Installer 2.0 -> Web App Gallery

Just to take a second, and make sure this is clear.

There's the command-line MSDeploy engine, there's WebDeploy that integrates with IIS and has a nice UI, and there's the Web Application Gallery where you can get a bunch of free applications. When you're in the App Gallery, the Web Platform Installer will do the installation.

Here's how I see it. Of course, you just have to click a button, but I always like to see what's underneath.

image 

What's cool about this is that even if you don't want to put your app up on the Web Application Gallery, you can still use MSDeploy/WebDeploy to deploy your apps. They are REALLY powerful tools that can help you deploy, migrate between IIS6 and IIS7, and synchronize content and apps between machines in a web farm. If you deal with IIS apps all day, host them, develop them, etc, take some time and spend it over at http://www.iis.net.

Now, if you want to have your app included in the gallery, here's the principles your app should follow:

Web Application Gallery Development Principles

1. Be Current: The application you provide a link to must be the latest, stable final release version available, hosted on a publically available Web URL

2. Be Free of Charge: The application for which you submit a link to the Microsoft Web App Gallery must be provided free of charge and fully functional without time restrictions. You are welcome to charge for professional support or consulting services and/or provide an enhanced, enterprise version of the application for purchase on your site.  We will be happy to provide a link to your commercial products and services from your page in the Microsoft Web Application Gallery.

3. Be Compatible: The application to which you provide a link must run on Windows Server 2003, Windows Server 2008, Windows XP & Windows Vista using best practices on running ASP.NET applications and PHP applications on IIS.

4. Be Deployable: The application to which you provide a link must integrate with Microsoft Web Deploy as described in the Microsoft Web Application Packaging Guide, and run with the Microsoft Web Platform Installer v2.

5. Be Supported: You must provide a publicly available Web site where end users can download your application, find documentation and/or get free on a best effort basis support through a forum.

6. Be Hostable: The application to which you provide a link must run well in a shared hosted environment as well as when the user has administrative rights for the computer.

7. Be Inclusive: If your link is included in the Gallery, you should include a link on your application community’s Web site to your Application entry on the Microsoft Web Application Gallery.

8. Be Safe: The application to which you provide a link must not harm customers or be malicious, dishonest, destructive, invasive, or act in any manner restricted by the Web Gallery Application Submission agreement.

Get the Microsoft Web PlatformCongrats to the IIS and /Web teams for this release, and thanks for all the guys on dasBlog for helping. Big thanks to Bill Staples for letting dasBlog 2.3 in on the launch.

Enjoy!

Related Links

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Best Code Syntax Highlighter for Snippets in your Blog

December 2, '08 Comments [36] Posted in ASP.NET | DasBlog | Javascript | Open Source
Sponsored By

I get a few emails a day of folks asking what Syntax Highlighter I use in my blog for my code samples. Specifically, the newer code samples, as some of the old ones sucked as I was experimenting, trying to find the best one to settle on.

The tool I use is actually called SyntaxHighlighter and it's from Alex Gorbatchev. The trick is that the syntax highlighter is all javascript on the client side.

I was having all sorts of troubles with other code highlighters. First, there were ones that put css classes and stuff all through your code, trying to decorate each keyword. This just bloated my feed and site and made the code look weird in some Feed Readers. Then I tried using images for code, like ScottGu does, but that is just wrong. You can't copy paste the code, you can't search it, it's disrespectful for the blind, etc. Meh.

How I post code to my blog

I use Windows Live Writer to post all my blog posts, and it has a great plugin model. I've actually written a WLW plugin for the CueCat...it's really easy. I use a plugin from DasBlog contributor Anthony Bouch called PreCode that directly targets/supports SyntaxHighlighter from within WLW.

Screenshot of my plugins in Windows Live WriterThat means I see this from inside Live Writer. I slick Insert PreCode Snippet, and paste in my code.

If you're reading this blog post from inside an aggregator or feed reader, the next two code snippets look identical to you. However, if you visit my blog, you'll see that one is different.

// Hello3.cs
using System;

public class Hello3
{
public static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
Console.WriteLine("You entered the following {0} command line arguments:",
args.Length );
for (int i=0; i < args.Length; i++)
{
Console.WriteLine("{0}", args[i]);
}
}
}
// Hello3.cs
using System;

public class Hello3
{
public static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
Console.WriteLine("You entered the following {0} command line arguments:",
args.Length );
for (int i=0; i < args.Length; i++)
{
Console.WriteLine("{0}", args[i]);
}
}
}

One looks like this, as HTML:

// Hello3.cs
using System;

public class Hello3
{

public static void Main(string[] args)
{

Console.WriteLine("Hello, World!");
Console.WriteLine("You entered
the following {0} command line arguments:",
args.Length );

for (int i=0; i < args.Length; i++)
{

Console.WriteLine("{0}", args[i]);
}
}
}

See the 'class="c#" name="code"' part? Alex's Javascript SyntaxHighlighter is looking for those and parsing them on the client side. I choose to add
breaks, but that's an option in PreCode. Other options for SyntaxHighlighter include line numbering, gutters, copy/paste support, a toolbar and more.

P.S. If you don't use Windows Live Writer (and seriously, stop and ask yourself, WHY NOT?) and use instead a web interface, you can integrate SyntaxHighlighter into your web-based rich text editor. For example, Darren made a SyntaxHighlighter Plugin for the popular FCKeditor. Perhaps we'll put that in DasBlog.

Installing SyntaxHighlighter to Your Blog

You install the SyntaxHighlighter by adding it to your blog's template. It doesn't care what blog engine you run, as it doesn't need anything on the server:









Just add the shCore library and just the languages you require. If you want your blog to feel snappy and you have some control over your server, don't forget to set the files/directories to cache on the client by making them expire far in the future. You don't want your user's browsers to keep asking for these scripts each page view.

Even better, you can create your own plugins for SyntaxHighlighter if you use a language Alex hasn't supported officially. This guy threw together a Scala SyntaxHighlighter file by editing the Java one and adding a regex.

There are a few bugs but I think folks forget that Alex is doing this all alone, so I have to give him mad props for the effort. It can be lonely and unforgiving when you do something awesome and either no one cares, or folks only care to complain.

UPDATE: There's some great un-bundled brushes collected here.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb
Page 1 of 27 in the DasBlog category Next Page

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.