Deploying multiple versions of Angular app to Azure App Service
Asked Answered
K

3

12

I have an Angular application which I can deploy to Azure App Service without any issues.

First I compile my application using the following command:

ng build --output-path=dist --aot -prod

Then I add the following web.config to the dist folder:

<configuration>
    <system.webServer>
         <rewrite>
            <rules>
              <rule name="AngularJS 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="/" />
              </rule>
            </rules>
          </rewrite>
          <caching enabled="true" enableKernelCache="true">
              <profiles>
                  <add extension=".js" policy="DisableCache" kernelCachePolicy="DisableCache" />
              </profiles>
          </caching>
    </system.webServer>
</configuration>

I then zip the contents of the dist folder up and drag it into my Azure application ( https://<site.scm.azurewebsites.net>/ZipDeploy ). At this point the app works fine.

My problem is that I would like to deploy different versions of the application for different locales.

I compile my different versions using the following commands:

ng build --output-path=dist/en --aot -prod --bh /en/ --i18n-format=xlf --locale=en

ng build --output-path=dist/fr --aot -prod --bh /fr/ --i18n-file=src/locale/messages.fr.xlf --i18n-format=xlf --locale=fr

This outputs the folders en and fr to /dist containing the different versions of the app. I add a web.config to each version of the app e.g inside /dist/en and dist/fr.

Next, I zip up the en & fr folders and deploy it using ZipDeploy as above.

I can see my application homepage working fine at:

https://<site>.azurewebsites.net/en
https://<site>.azurewebsites.net/fr

However - when I get redirected to my application at https://<site>.azurewebsites.net/fr/redirect.html#state.... (I'm signing in using Azure B2C), I get the following error:

You do not have permission to view this directory or page.

This feels like iis is trying to literally interpret my url as a directory instead of letting Angular handle the routing but I'm not sure.

Does anybody know how to do this correctly?

Kellda answered 11/4, 2018 at 13:39 Comment(2)
Based on your description, I assumed that the en and fr folders are under D:\home\site\wwwroot, you could leverage KUDU to check your deployed web contents. Based on your rewrite rule, since the en and fr is the folder, the url rewrite rule may not work, so I assumed that when accessing https://<site>.azurewebsites.net/en, you should see the You do not have permission to view this directory or page error.Carrico
Maybe, I may misunderstand your issue, or have you configured How to map an Azure App Service Web App virtual directory to Azure Storage Container? Moreover, you could Enable diagnostics logging to narrow down this issue. Additionally, does https://<site>.azurewebsites.net/fr/redirect.html could work as expected?Carrico
R
10

We had also the problem for our i18n multi-languages website created by angular-cli.

What you need:

1) ensure that you have only 1 web.config in the root folder (not under ./fr or ./en)

2) ensure that your fr/index.html has the right base tag

<base href="/fr/">

3) ensure that your en/index.html has the right base tag

<base href="/en/">

4) the content of your unique web.config needs to include the following code:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
      <rewrite>
        <rules>
            <rule name="SPA Routes FR" stopProcessing="true">
                <match url="fr/.*" />
                <conditions logicalGrouping="MatchAll">
                    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                    <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
                </conditions>
                <action type="Rewrite" appendQueryString="true" url="fr/index.html" />
            </rule>
            <rule name="SPA Routes EN" stopProcessing="true">
                <match url="en/.*" />
                <conditions logicalGrouping="MatchAll">
                    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                    <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
                </conditions>
                <action type="Rewrite" appendQueryString="true" url="en/index.html" />
            </rule>
        </rules>
    </rewrite>
  </system.webServer>
</configuration>

The "appendQueryString" is needed if you have some query parameters with your URL.

Refrigerator answered 18/4, 2018 at 11:32 Comment(5)
Is there a wildcard syntax if you have more than 2 applications? I was thinking something like <match url="(.*)/.*" /> and then rewrite would include the first part of the match.Tees
@Tees Yes there is see my answer below https://mcmap.net/q/930742/-deploying-multiple-versions-of-angular-app-to-azure-app-serviceBulgarian
How do you put the "unique" web.config in the root with the ng cli? We address it in the angular.json as assets but then it will also be placed in the fr or en folder and thus not in the root.Bastardy
What do you mean by 'root' folder (where the web.config is supposed to exist)? Is it the dist folder (i.e. parent folder of /fr, /en folders)Conventioneer
@SuparnaGharpure Yes, it is the dist folderRefrigerator
B
6

For anyone looking for a generic solution see below.

It does 2 things:

  • Handle default language via redirect (our default language is de)
  • Handle rewires for any language
    • it uses regex to capture the language part (2 character language string + slash) from the beginning of the path, for example de/ or en/
    • it then uses the capture group to rewrite the path

Note:

  • This will not limit which language codes a user can request via url
  • If you want to be specific which languages are supported you can replace the regex with the following to only support de + en + fr:
    ^((?:de|en|fr)\/).*
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <httpRedirect enabled="true">
      <add wildcard="/" destination="/de" />
    </httpRedirect>  
    <rewrite>
      <rules>
        <rule name="angular" stopProcessing="true">
          <match url="^(.{2}\/).*" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/{R:1}" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>
Bulgarian answered 11/8, 2020 at 12:17 Comment(0)
H
0

I think we have to have specific routes to each locale something like the below

<configuration>
<system.webServer>
    <rewrite>
        <rules>
            <rule name="Angular Routes fr" stopProcessing="true">
                <match url="fr/*" />
                <conditions logicalGrouping="MatchAll">
                    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                </conditions>
                <action type="Rewrite" url="/fr" />
            </rule>
            <rule name="Angular Routes en" stopProcessing="true">
                <match url="en/*" />
                <conditions logicalGrouping="MatchAll">
                    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                </conditions>
                <action type="Rewrite" url="/en" />
            </rule>
            <rule name="Angular Routes default" 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="/en" />
            </rule>
        </rules>
    </rewrite>
</system.webServer>
Haplography answered 18/4, 2018 at 9:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.