I have a single page React app hosted in Azure blob storage but am getting an The requested content does not exist. error when deep linking into a page:
I've enabled static website option in the storage account:
The files are all in place in the $web container:
This includes the following web.config with a rewrite rule that is supposed to let index.html handle all the routing:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="React Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/" appendQueryString="true" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Can anyone see where I've gone wrong?
The static website in Azure Storage GPv2 is different from Azure App Service. It only hosts these static web files which include HTML/CSS/JavaScript files, other files can be handled in browser like images, robots.txt
, etc. It has inability to process server-side scripts, due to there is not IIS. So your web.config
file is no sense for it to change the access routing and be belong to server-side script for IIS.
Actually, you can see the words in Azure portal.
Configuring the blob service for static website hosting enables you to host static content in your storage account. Webpages may include static content and client-side scripts. Server-side scripting is not supported in Azure Storage. Learn more
And refer to the Learn more
link of Static website hosting in Azure Storage
In contrast to static website hosting, dynamic sites that depend on server-side code are best hosted using Azure App Service.
I recommended using Azure App Service for your app if requires the URL-rewrite feature.
You can do the deep linking if you point the 404 page back at your index.html
. This is not a perfect solution - has side effects - but it will work for the majority of cases.
I have multi-lingual Angular application with deep linking (which should be accessed directly) and I did the following:
Folder structure in the $web
contrainer:
. ru-UA
. uk
. en-US
. index.html
- Enable 404 to the
index.html
in the static website setup in the Azure Portal
- On your root
index.html
add the next code:
<html>
<head>
<script>
if (window.location.pathname != null) {
const segments = window.location.pathname.split('/').filter(x => x.length > 0);
const languages = ['ru-UA', 'uk', 'en-US'];
if (languages.indexOf(segments[0]) >= 0)
{
sessionStorage.setItem('path', window.location.pathname);
window.location = '/' + segments[0] + '/';
} else {
window.location = '/uk/'; // It might be your default language
}
}
</script>
</head>
</html>
- Then, in your Angular add the next:
const spaRedirect = sessionStorage.getItem('path');
if (spaRedirect) {
const path = spaRedirect.split('/').filter(x => x.length > 0).slice(1).join('/'); // slice(1) to remove language segment
await this.router.navigate(['/' + path]);
sessionStorage.removeItem('path');
}
Now you can have deep linking.
How it works:
- User enters in the browser:
www.domain.com/uk/very/deep/link
Azure
- Static Website engine can't find such blob and redirects to 400,
which is root
index.html
-> www.domain.com/index.html
- Root
index.html
got full link via
pathname
property and split it to the segments.
Actually, you can
avoid splitting by segment, but for me it's just extra validation
step.
- Then it redirect to Angular localized-specific
index.html
in appropriate folder and Angular Engine start to serve website -> www.domain.com/uk/index.html
Please note, that you can have a delay here, cause you have to launch Angular with all dependencies and only after that redirect
- Your very basic component finds something in the
sessionStorage.getItem('path')
and make Angular-based redirect to the deep link. -> www.domain.com/uk/very/deep/link
None of these solutions worked for me, and I miserably started to accept that I wouldn't be able to host my Angular application (with Routing) just in Azure Storage.
But, actually, I found a solution.
You need to manually add a web.config file to your src folder (yes, even though we're not hosting this on an IIS server)
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Main Rule" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
...then add this file to the assets
section in your angular.json file:
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
. . .
"assets": [
"src/web.config"
],
Shockingly, this works now.
In my Angular app, I could click around to various sub-pages, eg
http://www.mikesapp.web.core.windows.net/Orders/List
and, crucially, I can also open a browser and go directly to this webpage.