Base solution for your next web application
Open Closed

Deployment to Azure App Service #4302


User avatar
0
JapNolt created

I have downloaded the Angular/aspnetcore template (not merged) and I would like to deploy both to the same Azure App Service. How can I make that work? I'm assuming there are things I need to configure in the web.config and also maybe in the Azure App Service to make it work??


18 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @JapNolt,

    For *.Host project, you need to publish it just like any other ASP.NET Core application. Then, configure the values in appsettings.json file. Configuring below items is enough for running the application:

    "ConnectionStrings": {
            "Default": "Server=localhost; Database=AbpZeroTemplateDb; Trusted_Connection=True;"
    },
        
     "App": {
        "ServerRootAddress": "AddressOfHostWebSiteOnAzure",
        "ClientRootAddress": "AddressOfHostAngularSiteOnAzure"
    },
    

    For angular app, see this document. <a class="postlink" href="https://aspnetzero.com/Documents/Development-Guide-Angular#deployment">https://aspnetzero.com/Documents/Develo ... deployment</a>

    After publishing it, configure the values of remoteServiceBaseUrl (url of host website on azure), appBaseUrl (url of angular website on azure) in appconfig.json under "src > assets" folder.

  • User Avatar
    0
    JapNolt created

    So if I have both the Angular app and the backend api in the same Azure App Service, it doesn't seem to work. I'm assuming that the web.config isn't correct or something with the routes isn't right. I'm assuming I should be doing something contained here but not sure how to do it in Azure App Service. <a class="postlink" href="https://aspnetzero.com/Documents/Development-Guide-Angular#iis-deploy">https://aspnetzero.com/Documents/Develo ... iis-deploy</a>

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    I thought you want to host them seperately.

    Before publishing your *.Host application, follow these two steps

    Step 1: Add middleware for angular 2.x rotues

    Since we host our Angular 2.x application in a ASP.NET Core website, we will have a routing problem. After publishing our application, if we refresh page when we navigate to a page in our application (for example: /admin/users), we will have a empty page. Because when we request an url like <a class="postlink" href="http://yourwebsite.com/admin/users">http://yourwebsite.com/admin/users</a>, ASP.NET Core will not be able to find a matching Controller. In order to overcome this problem, add below code to Startup.cs file just before "app.UseStaticFiles();" line.

    app.Use(async (context, next) =>
    {
        await next();
    
        if (context.Response.StatusCode == 404
            && !Path.HasExtension(context.Request.Path.Value))
        {
            context.Request.Path = "/index.html";
            await next();
        }
    });
    

    Step 2: Remove HomeContoller

    We also need to remove HomeController from our project, so the app's default route will be our angular client app. We can still access to swagger ui under /swagger/ui/index.html.

    After Publish

    Our client and server applications are designed to work separately. Because of that, they have different configurations. In order to make them work together, we need to make some configurations after publish as well. We need to use same address configured for our host application in appsettings.json for our angular2 application. First configure your appsettings.json file for values "ServerRootAddress", "ClientRootAddress", "CorsOrigins". ServerRootAddress and ClientRootAddress must be same since we are hosting client and server together. If you are using subdomain per tenancy, then you need to include allowed addresses into CorsOrigins, otherwise CorsOrigins will be same as ServerRootAddress and ClientRootAddress. Then, copy the value of "ServerRootAddress" in appsettings.json and use this value for "remoteServiceBaseUrl" and "appBaseUrl" in "appconfig.json" under "wwwroot\assets" folder.

    Now you can view your website under "http://yourwebsite.com/". If you want to view swagger ui, then you can use <a class="postlink" href="http://yourwebsite.com/swagger/ui/index.html">http://yourwebsite.com/swagger/ui/index.html</a>.

    Notice that, appsettings.json and appconfig.json are two different files.

    Then just copy the output of angular publish (which will be under dist folder after publishing angular app) to wwwroot of your published *.Host app.

    Actually, merged solution automatically does what you want but if you are working on seperate solutions, you need to follow above steps manually.

    Let us know if you have any other problems.

  • User Avatar
    0
    JapNolt created

    Thanks for the detailed reply.

    If I do deploy to separate Azure App Services, I'm assuming I would need to have a different hostname for the front end (<a class="postlink" href="http://myservice.com">http://myservice.com</a>) and the api (<a class="postlink" href="http://myserviceapi.com">http://myserviceapi.com</a>) or is that not the case?

    If doing subdomain per tenancy regardless of what deployment model is used (merged or separate), is it necessary to set the CorsOrigin for each subdomain or is it possible to set a catchall? It seems like a lot of extra work to tweak the CorsOrigin every time that a new tenant is added, especially if we have automatic tenant signup enabled.

    BTW, what is your recommendation, to run merged or in separate app services?

  • User Avatar
    0
    godrunner created

    Greetings @JapNolt

    Here is The. Really. Simple Solution.

    When I first got started with ASPNET Zero, I spent quite a bit of time evaluating which versions to use and the related pro’s and cons of each. I settled on Angular deployed to two Azure App services. (not merged) Although this will cost a bit more, it keeps my options wide open for some things I need to acomplish. One big drawback of App Services is you can not use specific ports. One big plus though is the “slots” and many other features. It is a nice solution if you want to have a complete staging environment as well as production. It can save a lot of time, the workflow is less error-prone and for me, that more than offsets a bit of added expense. So, here is the simple solution. (NOTE: I used 4.5.1 for my testing but I am pretty sure this will all still work with 5.x. I am waiting for 5.1 to upgrade my project to let some of the bugs/dust settle a bit.) :-)

    Step 1 - Create App Services on Azure All you have to do is set up AppServiceA for your Angular project let’s say AppServiceH for your web.host project.

    Step 2 - Tweak web.host appsettings.*.json files only ONCE In your web.host project, add an appsettings.Production.json right along side of appsettings.json if it is not there already. (if I recall correctly a default App Service will default to production) Now you can leave your appsettings.json set to your production environment and configure your appsettings.Production.json to look something like mine below. Use the same principle for Staging. These correspond to your slots on Azure.

    In your appsettings.Production.json file, change the connection string to point to your Azure DB and make the following changes below that. This is all you need to have in that file.

    "App": {
        "ServerRootAddress": "http://{TENANCY_NAME}.AppServiceH.com",
        "ClientRootAddress": "http://{TENANCY_NAME}.AppServiceA.com",
        "CorsOrigins": "http://AppServiceA.com"
    
      }
    }
    

    Step 3 - Tweak appconfig.production in the angular project

    {
      "remoteServiceBaseUrl": "http://{TENANCY_NAME}.appserviceH.com",
      "appBaseUrl": "http://{TENANCY_NAME}.appserviceA.com"
    }
    

    Step 4 - Build and Publish

    Publish your web.host project to AppServiceH and I just FTP the Angular app up to AppServiceA (including web.config) and viola! Everything works perfectly with multitenancy!

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @godrunner,

    Thank you for your detailed explanation :).

  • User Avatar
    0
    JapNolt created

    @godrunner +1 Thanks.

  • User Avatar
    0
    dparizek created

    Several deployment questions: I understand how to deploy the non-merged version... but what about the merged version?

    So if we are deploying the merged version of Angular/.NET Core then how does deployment to Azure differ?

    Do we still need two urls and two instances of Azure App Services??

    Do we still need to update with NSSwag from the command line when we add new services? Still need to npm build -prod the angular portion? Or does that get done by the build scripts automatically in the merged version?

    It is confusing how the workflow is different comparing the merged and not-merged solutions.

  • User Avatar
    0
    godrunner created

    @dparizek, although I have never tried the merged version, my guess is there isn't that much difference except primarily the location of the files.

    The big downside of Azure App Services is that you can't use ports, however, for us at this point, after a lot of testing with some other options, it really works nice to have them separated and ya gotta love those slots.

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @dparizek,

    Do we still need two urls and two instances of Azure App Services??

    No, You can host them together in a single website. Merged project is created for that. Our demo (demo.aspnetzero.com) works like that.

    Do we still need to update with NSSwag from the command line when we add new services?

    Yes, during the development, you need to do this.

    Still need to npm build -prod the angular portion? Or does that get done by the build scripts automatically in the merged version?

    Merged project's publish script automatically runs "npm build -prod"

  • User Avatar
    0
    thor created

    Hi,

    I have been trying to set up one App Service for Angular and one for ASP.NET Core. It now appears to be working and both services are being built and deployed via VSTS (took a while to sort out how to get the Angular site built and deployed).

    However, I have two annoying issues remaining:

    1. Fonts are not being loaded properly. See below from the Chrome Dev tool:
    21:50:35.144 fontawesome-webfont.af7ae505a9eed503f8b8.woff2 Failed to load resource: the server responded with a status of 404 (Not Found)
    
    21:50:35.144 poppins-v5-devanagari_latin-ext_latin-300.01860d964547bc9d93cf.woff2 Failed to load resource: the server responded with a status of 404 (Not Found)
    
    21:50:35.601 poppins-v5-devanagari_latin-ext_latin-regular.76e766753becb2b7da20.woff2 Failed to load resource: the server responded with a status of 404 (Not Found)
    
    21:50:35.611 poppins-v5-devanagari_latin-ext_latin-600.0d49524ae3304f2876fb.woff2 Failed to load resource: the server responded with a status of 404 (Not Found)
    
    21:50:35.622 roboto-v18-vietnamese_latin-ext_latin_greek_cyrillic-ext_greek-ext_cyrillic-500.90d1676003d9c28c0499.woff2 Failed to load resource: the server responded with a status of 404 (Not Found)
    
    ...
    
    1. When a link is hit directly there is a problem with routing and the response is "The resource you are looking for has been removed, had its name changed, or is temporarily unavailable."

    I have followed the advice and amended startup.cs and deleted the Home controller. Unfortunately this didn't fix it and I have spent a lot of time experimented with many different approaches including various rewrite rules in web.config, but nothing seems to fix this and some seem to make things worse.

    Any ideas?

    Thor

  • User Avatar
    0
    thor created

    I should add that all the fonts are being loaded as if they were located in the root:

    <a class="postlink" href="http://myAngularWebsite.com/poppins-v5-devanagari_latin-ext_latin-regular.76e766753becb2b7da20.woff2">http://myAngularWebsite.com/poppins-v5- ... da20.woff2</a>

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @thor,

    I assume you are using merged solution for angular right ? If so, are there any fonts in the publish directory when you publish your project to a local folder in your computer ?

  • User Avatar
    0
    thor created

    No, I have installed it as two different app services...

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @thor,

    For your second problem, you also need to publish web.config in angular project to azure. For font problems, do you have font files on your azure website ? If so, probably azure is not serving woff mime types by default. You can overcome it by modifying the web.config for the angular proejct like this.

    <configuration>
      <system.webServer>
        <staticContent>
          <mimeMap fileExtension=".woff" mimeType="application/font-woff" />
          <mimeMap fileExtension=".woff2" mimeType="application/font-woff2" />
        </staticContent>
      </system.webServer>
    </configuration>
    
  • User Avatar
    0
    thor created

    Here is a copy of my current web.config from the Angular project. I have experimented with various combinations of the rewrite rules. But they are currently disabled as it doesn't seem to make much difference.

    
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <system.webServer>
        <rewrite>
          <rules>
                
              
    
              
              
          </rules>
        </rewrite>
          
        <staticContent>
            <remove fileExtension=".svg" />
            <remove fileExtension=".eot" />
            <remove fileExtension=".woff" />
            <remove fileExtension=".woff2" />
            <remove fileExtension=".json" />
            <mimeMap fileExtension=".json" mimeType="application/json" />
            <mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
            <mimeMap fileExtension=".eot" mimeType="application/vnd.ms-fontobject" />
            <mimeMap fileExtension=".woff" mimeType="application/font-woff" />
            <mimeMap fileExtension=".woff2" mimeType="application/font-woff" />
        </staticContent>
      </system.webServer>
    </configuration>
    

    The issue seems to be that instead of retrieving the fonts from the root rather than /assets/fonts/poppins/ etc. In other words it looks like a routing issue.

    web.config from host:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
    
      
    
      <system.webServer>
        <handlers>
          <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
        </handlers>
        <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
      </system.webServer>
    </configuration>
    
  • User Avatar
    0
    thor created

    I have resolved the issues. I will try to share my lessons learnt once I complete my Azure experience.

  • User Avatar
    0
    ismcagdas created
    Support Team

    Thanks @thor. By the way, I have published latest version of AspNet Zero to azure and below config solved woff/woff2 issues.

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <system.webServer>
        <staticContent>
          <remove fileExtension=".json" />
          <mimeMap fileExtension=".json" mimeType="application/json" />
          <remove fileExtension=".woff" />
          <mimeMap fileExtension=".woff" mimeType="application/font-woff" />
          <remove fileExtension=".woff2" />
          <mimeMap fileExtension=".woff2" mimeType="application/font-woff" />
        </staticContent>
        
        <rewrite>
          <rules>
            <rule name="Angular Routes" stopProcessing="true">
              <match url=".*" />
              <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" url="/" />
            </rule>
          </rules>
        </rewrite>
      </system.webServer>
    </configuration>