Angular.js: $location HTML5 Mode - appBase is case sensitive.

Created on 19 Sep 2013  Â·  9Comments  Â·  Source: angular/angular.js

If the application lives in a sub directory, Angular raises an exception if the user types the application url in a different case than what's defined by the base element.

Example:

Markup: <base href="/base/" >
Route: .when('/', { templateUrl: 'partials/gettingstarted', caseInsensitiveMatch: true })

If the application is accessed with http://localhost/base/, the routing works as expected. If the user enters via http://localhost/Base/, Angular throws an exception:

TypeError: Cannot read property '1' of null
    at matchUrl (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:7084:25)
    at Object.$$parse (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:7152:5)
    at $LocationProvider.$get (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:7589:15)
    at Object.invoke (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:3129:28)
    at http://localhost/common/vendor/angular-1.2.0rc1/angular.js:2970:37
    at getService (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:3086:39)
    at Object.invoke (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:3107:13)
    at http://localhost/common/vendor/angular-1.2.0rc1/angular.js:2970:37
    at getService (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:3086:39)
    at Object.invoke (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:3107:13) angular.js:7818
Error: [$injector:cdep] Circular dependency found: 
    at hasOwnPropertyFn (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:60:12)
    at getService (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:3079:17)
    at invoke (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:3107:13)
    at Object.instantiate (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:3141:23)
    at $get (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:5550:28)
    at http://localhost/common/vendor/angular-1.2.0rc1/angular.js:5060:34
    at forEach (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:203:20)
    at nodeLinkFn (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:5047:11)
    at compositeLinkFn (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:4607:15)
    at compositeLinkFn (http://localhost/common/vendor/angular-1.2.0rc1/angular.js:4610:13) angular.js:7818

The $$rewrite function returns no url because the beginsWith function fails to find a match.

ngRoute low bug

Most helpful comment

adding another work-around from a different thread about the same issue https://github.com/angular/angular.js/issues/3703 (thanks! @dcherman)

(function() {
    var base = document.querySelector( "base" );
    var normalized = RegExp( base.href, "i" ).exec( location.href );
    base.href = normalized ? normalized[ 0 ] : base.href;
}());

add this before Angular is loaded and it will dynamically replace the <base href=""> contents with whatever the user typed into the URL.

Although I really don't see why we have to do this, it should be changed to allow mismatched case, or at least an option on $locationProvider or $routeProvider (whichever makes more sense, e.g. $locationProvider.matchBaseCase = true would be default for current functionality). It seems the main argument is because the mismatched case "breaks" caching. But in my tests, when I changed beginsWith() as suggested by @craigg75 , the url rewrite took over after the beginsWith() comparison and changed the address to be the correct case anyway, as long as $locationProvider.html5Mode.rewriteLinks = true which is the default value, thus caching would still work. As it stands now, I've done the workaround I've mentioned, but as a result, I've broken the cache anyway, which is one of the things @IgorMinar wants to avoid.

All 9 comments

caseInsensitive property applies only to url segments of the route and not the base url segments.

making the base path in angular case insensitive would not be sufficient, because html considers the value of base[href] to be case sensitive. so you'd have to generate the base[href] value dynamically depending on the url that use used to enter the app.

furthermore you'd need to configure your http server to accept the base segments in case insensitive way.

lastly all http caching would get messed up because browser's cache treats urls in case sensitive way.

long story short, making base case insensitive would require a lot of work by the developer and wouldn't result in significantly better user experience.

I however think that we should provide you with a better error message and I asked @btford to create a new issue for that.

Case sensitivity for a url in any browser is a big fail for any framework. This really needs to be fixed.
As a workaround just rewrite the beginsWith() function --

function beginsWith(begin, whole) {
if (whole.toLowerCase().indexOf(begin.toLowerCase()) === 0){
return whole.substr(begin.length);
}
}

@IgorMinar

This assessment of the scenario assumes you are using writing an SPA, all content is delivered from a static server and you not taking a mini-SPA approach. Having multiple mini-SPAs contained within 1 parent site is not an uncommon scenario and works particularly well for project teams trying to migrate to a full SPA.

When using a mini-SPA approach each application requires a separate base path - in particular when combined with any server side routing framework.

Whilst I understand not everyone would need this feature, for those who do, it would be helpful if it was an optional configuration setting. As shown by @craigg75, the work required to support such a feature is minimal.

+1 for this, it is painful to keep explaining to users who have tried to type in a URL themselves that they need to be careful with case.

@richardthombs we solved this with a URL rewrite to lower case everything.
Then it doesn't matter what case the user uses.

Just make sure you use lowercase URLs in your links to avoid unnecessary
redirects.
On Jul 22, 2015 5:11 PM, "Richard Thombs" [email protected] wrote:

+1 for this, it is painful to keep explaining to users who have tried to
type in a URL themselves that they need to be careful with case.

—
Reply to this email directly or view it on GitHub
https://github.com/angular/angular.js/issues/4056#issuecomment-123882721
.

Thanks @bstruthers!

Was having the same problem and had to do what @craigg75 suggested.

@IgorMinar your point of view is one-sided and biased. You assume that each and all web servers in the known universe are case sensitive and run on case sensitive environments. However we all know that is not true.

Your assumption is correct when a website is hosted on a web server residing in a Linux environment as in linux the paths /path/to/filename and /Path/To/Filename point to different locations and files.
However websites hosted on a web server in a Windows environment the paths /path/to/filename and /Path/To/Filename point to exactly the same location and file.

So while on a Linux hosted website <base href="/base/" > differs from <base href="/Base/" > it does not differ and is exactly the same on Windows hosted websites.

So making base case insensitive would most definitely result in significantly better user experience. As for most users worldwide http://www.example.com/SomeBase/angular_app and http://www.example.com/somebase/angular_app are exactly the same. Like @richardthombs said try explaining to users those are not the same.

adding another work-around from a different thread about the same issue https://github.com/angular/angular.js/issues/3703 (thanks! @dcherman)

(function() {
    var base = document.querySelector( "base" );
    var normalized = RegExp( base.href, "i" ).exec( location.href );
    base.href = normalized ? normalized[ 0 ] : base.href;
}());

add this before Angular is loaded and it will dynamically replace the <base href=""> contents with whatever the user typed into the URL.

Although I really don't see why we have to do this, it should be changed to allow mismatched case, or at least an option on $locationProvider or $routeProvider (whichever makes more sense, e.g. $locationProvider.matchBaseCase = true would be default for current functionality). It seems the main argument is because the mismatched case "breaks" caching. But in my tests, when I changed beginsWith() as suggested by @craigg75 , the url rewrite took over after the beginsWith() comparison and changed the address to be the correct case anyway, as long as $locationProvider.html5Mode.rewriteLinks = true which is the default value, thus caching would still work. As it stands now, I've done the workaround I've mentioned, but as a result, I've broken the cache anyway, which is one of the things @IgorMinar wants to avoid.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

georgiosd picture georgiosd  Â·  124Comments

cgross picture cgross  Â·  194Comments

coli picture coli  Â·  62Comments

petebacondarwin picture petebacondarwin  Â·  59Comments

leeola picture leeola  Â·  76Comments