With Dynamics CRM 2011 it has become very easy to access the CRM web services from JavaScript or an HTML web resource. This allows you to add a lot of flexibility on the client side. To access the web services wheter it is the REST or SOAP end point you need to specify the full URL. Depending on the CRM deployment this may or may not contain the organization name. There are several approaches available to ensure your URL works on all different deployments and environments.
CRM 2011 provides a context object which contains several methods to access the URL and information as it is stored on the server. Inside CRM forms the Xrm.Page.context object is available.
For (HTML) web resources you can add a script reference to ClientGlobalContext.js.aspx. Now you can use the JavaScript method GetGlobalContext(), this will return the context object for webresources. The ClientGlobalContext.js.apsx is located in the WebResources directory, if you use a resource name without directory structure (not forward slashes in the CRM resoruce name) add the following script reference to your HTML.
<script src="ClientGlobalContext.js.aspx" type="text/javascript" language="javascript"></script>
Using the context object you have several ways to get the URL. Unfortunatly the methods provided by the context object do not work under all circumstances.
The method getServerUrl will return the full url including the organization name if needed. You can use the following to access the CRM organization service:
var url = context.getServerUrl() + '/XRMServices/2011/OrganizationData.svc';
Important to note is that this method always returns the hostname, port and protocol (http/https) as it is configured in the CRM deployment manager. In a default installation this will be the name of the CRM server. If you have setup an alias in your DNS or for example use the IP address of the server to connect this may cause a problem. Today's Browsers restrict access to resources based on domain names. This prevents cross-site scripting where a resource on a website you trust accesses content on another website which can be damaging. With getServerUrl you always get the configured url, if you use a different URL to connect CRM will still work as expected but an resources access the configured url will throw an access denied error. Because of the way CRM and JavaScript handle errors this may not show up immediately but it will always prevent your call from being executed.
This method does have a use where using the configured url is the best approach. When you for example create a link to inlude in an email you should always use the configured url.
Because the getServerUrl method always uses the configured CRM Server you can also choose to create the URL your self.
var serverUrl = document.location.protocol + "//" + document.location.host + "/" + context.getOrgUniqueName(); var url = serverUrl + '/XRMServices/2011/OrganizationData.svc';
The problem with the code as listed above is that this only works in CRM On premise. For IFD configurations and CRM Online the organization name should not be included in the server url.
The method prependOrgName takes a url absolute to the CRM application root (i.e. /XRMServices/2011/OrganizationData.svc or /WebResources/new_myfile.html) and prefixes this with the organization name if needed.
Most of the times you need to access a resource you can use relative paths to access files on the same website. This does work for access to the organization data service using ajax. Using this approach you eliminate the risk of cross-site scripting all together.
With this approach there is a catch. Will this method works exactly as expected when used in CRM forms, ribbons, etc it does not behave as expected when used from HTML or silverlight resources. The method as implemented in ClientGlobalContext.js.aspx will never prefix the url with the organization name even if it should because you are ON Premise. This is because this method is dependent on a check that is run on the server, this check includes a JavaScript variable depending on the type of connection used. In your custom web resources this check is not run so the variable will never be present.
Since I do work with various environments and configuration I do like to use a single approach that will simply work regardless of all this. The method below basically replaces the prependOrgName and getServerUrl methods on the context object.
function prependOrgName2(context, path) {
if (document.location.pathname.match('^/?' + context.getOrgUniqueName()) != null)
return '/' + context.getOrgUniqueName() + path;
else
return path;
}
function getServerUrl2(context) {
if (document.location.pathname.match('^/?' + context.getOrgUniqueName()) != null) {
return document.location.protocol + "//" + document.location.host + "/" + context.getOrgUniqueName();
}
else {
//Compatible with the context object:
return document.location.protocol + "//" + document.location.host + "/";
//Consistent with the on-premise url
return document.location.protocol + "//" + document.location.host;
}
}These methods work in all circumstances. The getServerUrl method has two returns for IFD and Online configuration the first returns the URL exactly like the context.getServerUrl method would including a trailing slash. The second give you the same url as on-premise so without a trailing slash, if you always append a url such as /XRMServices/... leaving out the trailing slash in both approaches makes most sense.