When creating an instance of a FeatureLayer, I've run across a bug that occurs when passing a relative URL for the service. For example:
var layer = new FeatureLayer({url:"/arcgis/rest/services/someFolder/someService/MapServer/0"});
The layer loads its service properties ok, but when querying the service for features, it includes the layer ID in the URL twice:
https://hostname/arcgis/rest/services/someFolder/someService/MapServer/0/0/query?f=json&etc=etc
Needless to say, the queries fail to execute. When initializing the FeatureLayer object, the logic "thinks" it parses the URL into a path and layerID. For example, if you pass an absolute URL, the following properties will be set:
//sample URL = https://hostname/arcgis/rest/services/someFolder/someService/MapServer/0
url: https://hostname/arcgis/rest/services/someFolder/someService/MapServer
layerId: 0
However, if you pass the relative URL:
//sample URL = /arcgis/rest/services/someFolder/someService/MapServer/0
url: /arcgis/rest/services/someFolder/someService/MapServer/0
layerId: undefined
After the layer's service info gets loaded, the state becomes:
url: /arcgis/rest/services/someFolder/someService/MapServer/0
layerId: 0
Queries to the service join the url and layerId properties, thus you get:
/arcgis/rest/services/someFolder/someService/MapServer/0/0
The problem can be tracked down to the parse function of the esri/layers/support/arcgisLayerUrl module, which uses regular expressions to parse the service URL into various components. This works fine for absolute URLs, but fails to work with relative URLs.
One possible workaround is to convert all your URLs to absolute. However, this can be a problem if different users access the same server with different host names.
Should you be using a locally hosted copy of the API, a more robust workaround would be to fix the aforementioned parse method...in this case, directly in the init.js file. The first two statements of that function are as follows:
c=a.urlToObject(c);
var b=c.path.match(g)||c.path.match(f);
I've inserted the following code between those lines to convert relative URLs to absolute prior to being tested against the regular expressions:
if (c.path.indexOf("http") !== 0) {
if (c.path.indexOf("/") === 0) {
if (c.path.indexOf("//") === 0)
c.path = window.location.protocol + c.path;
else
c.path = window.location.protocol + "//" + window.location.hostname + c.path;
} else if (c.path.indexOf(".") === 0)
c.path = window.location.protocol + "//" + window.location.hostname + window.location.pathname.substring(0, window.location.pathname.lastIndexOf("/")) + c.path;
}
However, I've minified it when updating the init.js file. So basically, in init.js, you would search for:
c=a.urlToObject(c);var b=c.path.match(g)||c.path.match(f);
and replace it with:
c=a.urlToObject(c);if(c.path.indexOf("http")!==0){if(c.path.indexOf("/")===0){if(c.path.indexOf("//")===0)c.path=window.location.protocol+c.path;else c.path=window.location.protocol+"//"+window.location.hostname+c.path;}else if(c.path.indexOf(".")===0)c.path=window.location.protocol+"//"+window.location.hostname+window.location.pathname.substring(0,window.location.pathname.lastIndexOf("/"))+c.path;}var b=c.path.match(g)||c.path.match(f);
As an aside, relative URLs worked fine in 3.x, and the 4.x documentation doesn't specify that absolute URLs are required.
This solution is specific to 4.16 and may not work in other versions.
This post is for the benefit of those like me who experience this problem, and need a solution *now*. I will not respond to requests to submit this to ESRI support.
Experiencing this bug in 4.12 as well.
Here's a simplified solution for 4.18. In init.js, search for:
p=b.urlToObject(p);p=p.path.match(k)||p.path.match(r);
...and replace with:
p=b.urlToObject(b.makeAbsolute(p));p=p.path.match(k)||p.path.match(r);
Although there's no mention of it in the release notes, this has been fixed in 4.19.