Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
154485 stories
·
33 followers

Get ready with the latest beta releases

1 Share

The beta versions of iOS 26.6, iPadOS 26.6, macOS 26.6, tvOS 26.6, visionOS 26.6, and watchOS 26.6 are now available. Get your apps ready by confirming they work as expected on these releases. And make sure to build and test with Xcode 26.5 to take advantage of the advancements in the latest SDKs.

View downloads and release notes

Learn about testing a beta OS

Learn about sending feedback

Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete

Team Productivity using Loop with Karinne Bessette

1 Share

How can Microsoft Loop make your team more productive? Richard chats with Karinne Bessette about the role that Loop components can play in making meetings where the agenda is live, generating work items in Microsoft Planner, and keeping key information up to date. Karinne talks about how Loop components can be connected to any M365 document, including Outlook, Word, Excel, and OneNote, but only for members of the M365 tenant. Loop is a powerful tool for productivity within the organization!

Links

Recorded April 27, 2026





Download audio: https://cdn.simplecast.com/media/audio/transcoded/5379899c-61c5-43c3-aa3f-1128cffd9ef4/c2165e35-09c6-4ae8-b29e-2d26dad5aece/episodes/audio/group/f634d684-2178-48f5-bb1f-58c5e8835021/group-item/48af5bc1-7b86-48be-b3b6-9c5b8d5b333c/128_default_tc.mp3?aid=rss_feed&feed=cRTTfxcT
Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete

MBW 1026: Double-Wide Mode - How Apple May Lean Into AI Features for Its OS's

1 Share

A slow week in Apple news as we wait for WWDC to begin on Monday, June 8th! Apple is seeking a Supreme Court review of the Epic Games case. Apple unveils new accessibility features utilizing Apple Intelligence. And Apple TV used an iPhone 17 Pro to broadcast a MLS match!

  • Apple's Watch and health efforts need reboot to rival new wearables.
  • Apple seeks Supreme Court review of contempt finding and injunction scope in Epic Games case.
  • Apple unveils new accessibility features, and updates with Apple Intelligence.
  • iOS 27 AI voice control: What it tells us about the Siri revamp.
  • Real Madrid immersive documentary on Apple Vision Pro.
  • Apple TV to broadcast first major professional live sporting event shot entirely on iPhone 17 Pro.

Picks of the Week

  • Leo's Pick: Insta360 Mic Pro
  • Jason's Picks: Cotypist
  • Andy's Pick: BBEdit 16
  • Christina's Pick: Hacks TV Series

Hosts: Leo Laporte, Andy Ihnatko, Jason Snell, and Christina Warren

Download or subscribe to MacBreak Weekly at https://twit.tv/shows/macbreak-weekly.

Join Club TWiT for Ad-Free Podcasts!
Support what you love and get ad-free audio and video feeds, a members-only Discord, and exclusive content. Join today: https://twit.tv/clubtwit

Sponsor:





Download audio: https://pdst.fm/e/pscrb.fm/rss/p/mgln.ai/e/294/cdn.twit.tv/megaphone/mbw_1026/ARML8722785082.mp3
Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete

The Quiet Licensing Drift Nobody Notices Until it Matters

1 Share
As with previous blogs, working code and all relevant how-tos exist in this GitHub Repository! Clone, star, fork...whatever makes the most sense! You ever get halfway through a conversation about endpoint security or device posture and realize you're just kind of assuming everyone's on Windows 11 Enterprise? Not because you checked, not because anyone validated it recently, just because that's how it's always been said out loud. It feels settled, like something that got figured out a long...

Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete

Running ASP.NET Core Applications in an IIS Subfolder Application

1 Share

Post Banner

ASP.NET Core applications by default want to run in a root folder - and to be fair that's the 99% use case. But there are those occasional situations where you want to run a Web site in a sub folder rather than on the root of the Web site.

In this post I review what's required to run ASP.NET with a PathBase - which works with any Web server - and then specifically discuss how to set this up on IIS, which is a little more complicated than it should be.

Why would I need to run out of a SubFolder?

The specific scenario that I ran into was that recently I updated my old custom Blog engine, and decided to pull all my secondary blogs onto my own server from various blog publishing sites. I've always run my blog on my own hosted server, but a couple of product blogs ran on different hosting sites which I never used because they were terrible.

With my recent site blog update - I finally moved the old WebForms app to .NET Core - and the much simplified deployment set up that comes with it, I decided to just run everything in house for these relatively low volume sites and get better, more consistent theming and a much easier publishing pipeline using Markdown Monster (using a new custom protocol - more on that in another post).

In any case the scenario here is that I have a root product site:

and I now want to run the blog site out of a /blog/ sub-folder, rather than as a root site. So:

rather than a separate new root Web site like:

(which is what I started with then aborted)

For SEO it's often beneficial to run everything on the same site, but it's also cleaner and more consistent with other sub folders like /docs and /support. Running a root site also requires more setup - yet another certificate and custom bindings to manage and so on - so using a subfolder is effectively more lightweight from a config perspective.

But using a sub-folder site requires more care is given how Urls are created in the application as we'll see.

Creating an Application in a Subfolder

There are two parts to the process of running an application in a subfolder:

  • Configuring ASP.NET for running from a subfolder
  • Configuring the Web Server for a subfolder

Setting up ASP.NET For running with a Subfolder

Turns out setting up ASP.NET to run from a subfolder is pretty easy to do as there's a dedicated middleware to set up a custom PathBase as ASP.NET likes to call it.

// in program.cs
app.UsePathBase("/blog/");

This code goes into program.cs after the builder has created an app instance, using the provided services. You'll want to do this near the top of the app middleware declarations to ensure the folder is respected all the way through the middleware pipeline - you'll want this before authentication and static files and certainly before any routing middleware. I have it at the very top of the pipeline.

I tend to parameterize the PathBase parameter with a configuration value, because I'm duplicating the same application in multiple folders. So, in my Weblog application it looks like this:

// config from DI initialization or wlApp.Configuration static
if (!string.IsNullOrEmpty(config.VirtualPath) && config.VirtualPath != "/")
{
	app.UsePathBase($"/{config.VirtualPath}/");
}

I have 3 blog sites - one of which runs as root and two which run in /blog/ subfolders. By parameterizing I can customize whether they run out of a subfolder or not without recompilation.

So what does .AddPathBase() actually do?

It's used to resolve Urls internally, based on the path you specify. Anytime ASP.NET creates a path dynamically for routes, uses ~/ in Views or Pages, or via Url.Content() or other IUrlHelper use it automatically fixes up the path with the provided path base.

So instead of returning /images/someimage.png which you get for a root site, you get /blog/images/someimage.png for example.

If you use implicit routing or you stick to using ~/ paths in your Views/Pages, ASP.NET does most of the heavy lifting without you having to do anything else. This is nothing new - class ASP.NET used to do that as well, but it's pretty nice that this is handled.

Fixing up Root Paths with ~/ and an ApplicationBasePath

This does mean you need to be more vigilant about how you root your Urls in your application. So rather than <img src="/images/someimage.png" /> you should use <img src="~/images/someimage.png" /> to ensure the appropriate path is used.

You can quickly find and replace all instances of hard coded root paths by doing a Find in Files Search (Ctrl-Shift-F) in your IDE and doing a search and replace for ="/ and replacing with ="~/. This should capture most scenarios in any physical files.

In code you can access the IUrlHelper interface in Razor views or injected into controllers or methods.

In a Razor Page or View

var rootPath = Url.Content("~/images/someimage.png");

You can also inject IUrlHelperFactory (WTF Microsoft?) and then retrieve the IUrlHelper in a somewhat convoluted way that only an ivory tower architect could love:

public class MyService
{
    private readonly IUrlHelper _url;

	public MyService(
    			IUrlHelperFactory factory,
    			IActionContextAccessor actionContextAccessor)
	{
	    _url = factory.GetUrlHelper(
	        actionContextAccessor.ActionContext);
	}
	
	public string Resolve()
	{
	    return _url.Content("~/images/logo.png");
	}

}

If you don't want to deal with this and just have a couple of generic methods that work anywhere, the Westwind.AspNetCore package has a couple of generic helpers:

Running locally with a SubFolder

If you create your app this way and you run it locally using the Kestrel Web server with dotnet run or from your IDE you will see the site come up in a subfolder:

Subfolder In Browser

The url includes the /docs/ subfolder:

https://localhost:5001/docs/

If you stuck to using ~/ root paths everything is bound to just work the same as if you were running from the root folder.

Note that you'll want to change your dev launchSettings.json to reflect the new launchUrl:

"Westwind.Weblog.MarkdownMonster": {
  "commandName": "Project",
  "launchBrowser": true,
  "environmentVariables": {
    "ASPNETCORE_ENVIRONMENT": "Development"
  },
  "applicationUrl": "https://localhost:5001",
  
  // THIS!
  "launchUrl": "https://localhost:5001/blog/"
},

Configuring IIS for a Subfolder Application

While the UsePathBase() middleware handles the ASP.NET Core side seamlessly, IIS requires some extra work to create an Application under your root website.

Note that IIS requires that each ASP.NET application requires its own dedicated ASP.NET Application Pool. Only a single ASP.NET Application can run inside of any given Application Pool, so unlike classic .NET sites, you can't share a single application pool for multiple Core apps.

IIS has Web Sites and Applications, which are very similar. Web Sites have extra configuration related to Host mappings and bindings, but otherwise Web Sites and Applications behave very similarly.

Here's what a folder based Application looks like in the IIS Manager:

Dedicated Application Pool In IIS

Note that the folder mapping doesn't have to match a physical folder below the Web site - it can point to any location on disk. However, in most cases I put the actual site content into the relative folder for consistency in finding it later 😄.

As mentioned you need to have a dedicated Application Pool for this application - or at least an AppPool that doesn't have another ASP.NET application in it. That AppPool should be configured for No Managed Code.

IIS Application Pool

Make sure you use an identity (user) that matches the rights that you need for your application. My apps tend to have a few configuration related settings that get written out so I have to use an account that has some additional rights.

Once this is set up and you've published your app, IIS will handle the initial request routing and pass the correct path information to the ASP.NET Core module to give you the same behavior that you see on your local install.

Running In IIS

Recommendations

If you're like me and you're doing this switch to running a sub-folder at the last minute after running and testing as a Root Site - I'd highly recommend you take a breather 😄 and spend a bit of time testing locally with the subfolder.

I initially built and worked with the site using a Root Web Site and only towards the very end tested with the sub-folder. For the most part the site ran with simple fixes but there were a few things that were missed like some Javascript client code that didn't use the base path. Take the time to test locally with the sub folder before publishing! Some of the errors you might run into are subtle and easy to miss.

To summarize these are some of the things that you have to probably fix or at least check when going from Site to Folder based:

All .cshtml, .razor Pages

All view pages can automatically fix up pages that reference via "~/" paths. Maybe you were diligent and started doing this right way. I rarely ever do this right - I do some with ~/ and some not which is no better than not doing it all 😄.

Luckily fixing up View pages is pretty straight forward: You can do a simple search and replace:

  • Select *.cshtml, *.razor etc.
  • Search for ="/
  • Replace with ="~/

Search In Files

Code Fix ups

You can do something similar for your .NET code, but you have to be a lot more selective and you can't do a search and replace.

  • Select *.cs, *.cshtml (cshtml for code blocks that build Urls)
  • Search for "/

You want to search code both in your CS files and any code snippets in your Views/Pages.

Hopefully there shouldn't be a lot of those in your code because generally hard coding Urls is a bad idea. Typically this only happens if Urls need to be built up based on a host of input parameters.

var redirectUrl = "/somedeeplink/in/the/site";

changed to:

var redirectUrl = wlApp.Configuration.ApplicationBasePath + "somedeeplink/in/the/site";

Again, this should be pretty rare but worth checking for. This search is likely to produce a lot of false positives that don't require any changes.

Javascript Code

Javascript code is little more tricky. If you have script code that's making server calls, you may have to fix up paths in your scripts. Scripts aren't executable code and the ASP.NET parser doesn't touch them as the files are served as static resources. It's also common that you provide Urls as strings.

It's a lesson I've learned a long time ago - almost every application that calls to the server requires a configurable value to for a base path to call the server. There are lots of ways to do this and in SPA applications I always have a config object to do this.

However, for traditional server based Web apps that have only a few isolated JavaScript callbacks to the server, there is no common entry point for scripts - they are just loaded randomly.

In this application I do this the brute force way by providing a base path in a global script variable that gets embedded into the _Layout.cshtml page. Since _Layout.cshtml touches every page this is as close as I get to a global client side entry point. You can also put this in a script file but you have to make sure it gets loaded early enough that all other scripts can see it.

The idea is that I create a global variable - or rather a global object with an embedded variable - that is accessible from any script. The script is defined at the top of the page in the header so it's visible to any code that follows.

I use another helper class from Westwind.AspNetCore helper for this:

@
{
	// at the top of _Layout.cshtml

	// creates an object with props for the value(s) below
	var scriptVars = new ScriptVariables("window.page");  
	scriptVars.Add("basePath", wlApp.Configuration.ApplicationBasePath);
}
<!DOCTYPE HTML>
<html>
<head runat="server">
    <title>@(ViewBag.Title ??  wlApp.Configuration.ApplicationName)</title>
    <script>
        // expands into an object with props
        @scriptVars.ToHtmlString();
    </script>

ScriptVariables is a helper class that makes it easy to create a Json safe object of values you want to embed into the page from your .NET code. You basically add variables which are then serialized - Javascript and Html safe into the document when you use @scriptVars.ToHtmlString().

In this case it produces an object with a single variable, but typically I end up with a handful of 'global' page level properties that need to be passed through:

window.page = {
	basePath: "https://markdownmonster.west-wind.com/blog/"
};

You can decide whether you want to use just /blog/ or the fully qualified path as I'm doing here.

I'm using:

scriptVars.Add("basePath", wlApp.Configuration.ApplicationBasePath);

which is configured value that I store in the app to have quick and easy access to the full site url. In the case of this _Layout.cshtml page you could also use:

scriptVars.Add("basePath", Url.Content("~/");

which produces the site relative /blog/ base path.

Now any scripts on the page - both in the page itself or in any script files can look at page.basePath and use that to fix up any Urls as necessary:

deletePost = ()=> {
	if (confirm('Are you sure you want to delete this post?')) {
		// THIS
		var url = page.basePath + "posts/@post.Id";
		
		ajaxJson(url, null,
			(res) => { 
			    // AND THIS
			    location.href = page.basePath + "posts";
			},
			(err) => {
			    alert("Error deleting post: " + err.responseText);
			},
			{ HttpVerb: "DELETE" });                                                    
	}
}

While this takes a little bit to set up, I always kick myself for not doing this right from the start. Having a base path for JS calls is just good practice, to ensure that you can move the Urls around as needed because you never know... as in this case.

Summary

Running Web sites out of a virtual folder is not all that common, but for many low impact sites I'm actually finding myself using them more often than I would have thought. When you need to do it, it's good to know that it's possible and not as complicated as I thought it might be.

The most time consuming part of the process isn't getting the site to run, but rather it's making sure that the site's links can run in the subfolder environment and resolve properly. Luckily fixing up links can be done relatively easily with ASP.NET due to its excellent internal support for resolving Urls. A few Search and Replace cycles go a long way to making sure apps can run in a subfolder application environment.

this post created and published with the Markdown Monster Editor
Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete

Version 0.1.6

1 Share

What's Changed

  • [MS] Add OCR layer service for embedded images and PDF scans by @lesyk in #1541
  • Fix O(n) memory growth in PDF conversion by calling page.close() afte… by @lesyk in #1612
  • Updated warning about binding to non-local interfaces. by @afourney in #1653
  • fix: handle deeply nested HTML that triggers RecursionError by @jigangz in #1644
  • Clarify security posture in READMEs by @afourney in #1807
  • feat: Add Azure Content Understanding converter by @chienyuanchang in #1865
  • Bump version to 0.1.6 by @afourney in #1914

New Contributors

Full Changelog: v0.1.5...v0.1.6

Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories