javascriptangularjs.htaccessangular-ui-router

How to properly return 404 in angular JS with dynamic urls (slugs) and ng-include?


I'm currently building a portfolio site for our company in AngularJS with UI-router. We're using the HTML5 url scheme, and I've got the standard .htaccess in place to rewrite requests to index.html ( though in production this will be running on a .NET server ).

Some sections of the site are made up of freeform html documents rendered via ng-include directives, to allow our developers and designers the maximum freedom to create some pages however they want - for example client case studies that may warrant quite different layouts and media.

The pages are all loaded via a slug parameter to a state, something like '/work/some-case-study-name'. The state handler then takes the slug parameter and supplies that as url to an html file for the ng-include to load. For example the url above would resolve to '/partials/case-studies/some-case-study-name.html'. This is all loaded into a directive.

At the moment, it seems to be impossible to catch a mis-typed slug url though. It appears that either through the .htaccess setup or angular's routing system, when ng-include fails to find the requested html file, it always returns 200OK and just renders the route of the site.

Does anyone know of a way to make ng-include return a 404 if the document it's trying to load does not exist?

thanks!


Solution

  • I'm not great at .htaccess rewrite configuration, but after trying several examples, this was the only one I could find that reliably returned a 404 for resources that genuinely don't exist in an angular html5 setup:

    https://ejd.dev/2013/05/31/angular-html5Mode-with-yeoman/

    Specifically:

    # Apache .htaccess
    
    # angularjs pushstate (history) support:
    # See http://www.josscrowcroft.com/2012/code/htaccess-for-html5-history-pushstate-url-routing/
    <ifModule mod_rewrite.c>
        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteCond %{REQUEST_URI} !index
        RewriteCond %{REQUEST_URI} !.*\.(css|js|html|png) #Add extra extensions needed.
        RewriteRule (.*) index.html [L]
    </ifModule>
    

    The difference between this and others (including the official example on the angular site) seems to be the rule matching specific file extensions. With this in place I get a 404 when trying to load a non-existent file via ng-include, which is exactly what I'm after.

    EDIT:

    Also - for .NET peeps, here's the equivalent web.config:

    <?xml version="1.0"?>
    <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" />
                <add input="{REQUEST_URI}" pattern=".*\.(css|js|html|png|jpg)" negate="true" />
              </conditions>
              <action type="Rewrite" url="/" />
            </rule>
          </rules>
        </rewrite>
      </system.webServer>
    </configuration>