3

Say I want to retrieve my API endpoint from a Visualforce Page or a Force.com Site, I can try:

Url.getSalesforceBaseUrl().toExternalForm();

But the return value is all over the place. It could be any of the following:

  • http://pod.salesforce.com (from a batch apex class)
  • https://c.na1.visual.force.com (a local Visualforce Page)
  • https://mysite.secure.force.com (from a Force.com Site)
  • https://ns.pod.visual.force.com (some page in a managed package)

This wreaks havoc on the Remote Site Settings, and gets worse with Sites and Communities.

So how do I get just "https://na1.salesforce.com"?

6

I think this may be a platform shortcoming. GetSalesforceBaseUrl() has caveats including:

  • the returned protocol (http vs https) is inconsistent,
  • visualforce context affects the result (as we saw with the subdomain),
  • use of My Domain and Sandboxes also affects the result, making string manipulation risky,

One reliable way to get the true pod/instance endpoint is to use the Salesforce Identity API.

Here's a memoized static we can use which mitigates all the above factors:

/**
 * Determines the true API hostname for a Salesforce org using the Identity API.
 * eg 'https://pod.salesforce.com' (most orgs)
 * eg 'https://custom.my.salesforce.com' (my domain)
 * eg 'https://custom--dev.pod.my.salesforce.com' (sandbox orgs)
 */
static public String protocolAndHost {
    get {
        if (protocolAndHost == null) {
            //memoize
            String orgId = UserInfo.getOrganizationId();
            String userId = UserInfo.getUserId();
            String sessionId = UserInfo.getSessionId();

            //use getSalesforceBaseUrl within batches and schedules (not Visualforce), and fix inconsistent protocol
            if (sessionId == null) return Url.getSalesforceBaseUrl().toExternalForm().replace('http:', 'https:');

            PageReference pr = new PageReference('/id/' + orgId + '/' + userId);
            pr.getParameters().put('oauth_token', sessionId);
            pr.getParameters().put('format', 'json');

            //within test context use url class, else derive from identity api
            String data = Test.isRunningTest() ? '{"urls": {"rest": "' + Url.getSalesforceBaseUrl().toExternalForm() + '"}}' : pr.getContent().toString();
            Map<String,Object> result = (Map<String,Object>)Json.deserializeUntyped(data);
            Map<String,Object> urls = (Map<String,Object>)result.get('urls');

            //compose pod from the REST endpoint
            Url rest = new Url((String)urls.get('rest'));
            protocolAndHost = rest.getProtocol() + '://' + rest.getHost();
        }

        return protocolAndHost;
    }
}
/**
 * The MIT License (MIT)
 * 
 * Copyright (c) 2014 bigass.force.com
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

GetSalesforceBaseUrl() is quirky by design, and 99% useful to serve callers from all contexts for stuff like generating hyperlinks and pointing JavaScript clients etc.

But for off-platform or Apex-code-constrained callers where consistency is valuable, using the Identity URL above allows one to circumvent all the 'smarts' contained in the native method.

  • 1
    Seems this method get the custom domain (devtest--XXXXXXX.cs1.my.salesforce.com) instead of the one only with instance on it (cs1.salesforce.com) if the org has enabled custom domain, though custom domain can still access SFDC REST API – Oscar Tang Jul 28 '16 at 4:04
  • 1
    @OscarTang yep :-) if custom domain enabled, everyone should use it – bigassforce Jul 28 '16 at 7:26
  • would this work in Lightning mode? – Patlatus Sep 10 '18 at 13:07
1

You can query any user's fullPhotoUrl

SELECT fullPhotoUrl FROM User WHERE userType = 'Standard' LIMIT 1

this field always contains at least the URL of an icon. The URL exposes the pod. E.g. https://c.eu2.content.force.com/profilephoto/005/T or https://acmeinc--c.na39.content.force.com/profilephoto/005/F

A regex like /.*\.(.*)\.content/ gives you the pod.

  • Isn't this field only exposed when Chatter is enabled? – Adrian Larson Feb 13 '17 at 20:56
1

You can get the pod of the org via SOQL, assuming the running user has access to the Organization table:

SELECT InstanceName FROM Organization

Depending on your use case, this may be a better option than calling out to the Identity API (which can't be done from synchronous contexts such as a Post Install Handler, etc.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.