How to expose a public Salesforce Web Service using a top-down approach (contract first) WSDL interface ?


How can we expose a public Web service using Salesforce (e.g no authentication needed to access this web service) ? Let’s add some challenge by using a WSDL contract imposed by your service Provider and that you have to follow when implementing this web service using Salesforce.


I have proposed a solution to support multiple delegated authentication service when having one Salesforce Organisation and said that this service can be exposed in Salesforce. After some investigation, it seems a little bit challenging to use a top-down approach when implementing web service in Salesforce. Why ?
There is no (documented?) way to expose a Web Service in Salesforce starting from WSDL. If you are coming from Java World, there is an option -server when you used WSDL2Java to generate the skeleton classes for the Web service implementation. I do not find a equivalent option in Salesforce using WSDL2Apex. My understanding of WSDL2Apex is that it’s provides you the stub classes to call a Web Service from Salesforce (Web Service Callout).
Salesforce propose a bottom-up model to implement your Web Service. The drawback of this approach is that you cannot set the different namespace/target namespace inherent to your Web Service, as it is automatically generated by Salesforce and you cannot override it (yet ?).

Proposed Solution

I have been working with Web Service since decade now and I remember that we can expose/call Web Service using the old plain HTTPRequest/ HTTPResponse with XML over HTTP(/HTTPS). This seems to be the right path to go. But in order to not re-inventing the wheel, i will “deviate” the REST annotation from Salesforce, and adapt it to my need to serve SOAP Request/Response (@RestResource annotation) imposed by the WSDL contract. You will find this strange and a bit overwhelmed solution (and yes it is ;-)), but I do not find any other solution. I really hope that Salesforce will proposed a way to expose a Web Service starting from the WSDL contract.

Required Steps

  • Get the WSDL Contract, in our case I will use the DelegatedAuthentication.wsdl exposed by Salesforce
  • Use the excellent SOAPUI tools to create SOAP requests and SOAP responses covering all uses cases. Do not forget to handle properly the SOAP Exception that might happened
  • To store theses SOAP templates, I was first thinking using Salesforce Custom Settings, but I found that I cannot create a text document greater that 255 characters. So converge to use the Static Resources for any Web Site published by Salesforce. Then I just have to load the SOAP Request/Response from this static resource and do required processing on thoses files (replacing tags template).
  • Create REST Apex Class that support @HttpGet and @HttpPost annotation. @HttpGet will just return the WSDL exposed by the REST Service and @Httppost will handle the SOAP request.
  • Go to you Org | Setup | Remote Site and add your remote site URL to avoid the error “Unauthorized endpoint”
  • Zip your static resources (SOAP Request and Response + WSDL Interface contract). Go to you Org | Setup | Static Resource and add your zip file. This access the XML file from the static resource, just use the following URL: where
      developper is your site name,
      SR_DelegatedAuthenticationService is the name of your static resource,
      and DAS_Response_true.xml is the name of your XML file in the static resource.
  • The Apex code just use DOM Document to parse and validate the SOAP Request
  • Sample Apex Code

    global class MultipleDelegatedAutNService {
        global static void doGet() {
        	RestContext.response.addHeader('Content-Type', 'application/xml');
            String wsdlFile = '';
    		RestContext.response.responseBody = Blob.valueOf(getBody(wsdlFile));
        // Just checking that it's actually XML
        private static String parseXMLStr(String toParse) {
          DOM.Document doc = new DOM.Document();
          try {
            DOM.XMLNode root = doc.getRootElement();
            return walkThrough(root);
          } catch (System.XMLException e) {  // invalid XML
            return e.getMessage();
        // Recursively walk through the XML
    		14:22:49.043 (43847766)|USER_DEBUG|[96]|DEBUG|*** parseXMLStr=
    		Element: Envelope namespace:
    		Element: Body namespace:
    		Element: Authenticate namespace:
    		Element: username namespace:,
    		Element: password namespace:, text=5F91558D-0274-4423-B397-AA7065EA5074
    		Element: sourceIp namespace:, text=INVENSYS
        // Reference:
        private static String walkThrough(DOM.XMLNode node) {
          String result = '\n';
          if (node.getNodeType() == DOM.XMLNodeType.COMMENT) {
            return 'Comment (' +  node.getText() + ')';
          if (node.getNodeType() == DOM.XMLNodeType.TEXT) {
            return 'Text (' + node.getText() + ')';
          if (node.getNodeType() == DOM.XMLNodeType.ELEMENT) {
            result += 'Element: ' + node.getName() + ' Namespace: ' + node.getNamespace();
            if (node.getText().trim() != '') {
              result += ', text=' + node.getText().trim();
            if (node.getAttributeCount() > 0) { 
              for (Integer i = 0; i< node.getAttributeCount(); i++ ) {
                result += ', attribute #' + i + ':' + node.getAttributeKeyAt(i) + '=' + node.getAttributeValue(node.getAttributeKeyAt(i), node.getAttributeKeyNsAt(i));
            for (Dom.XMLNode child: node.getChildElements()) {
              result += walkThrough(child);
            return result;
          return '';  //should never reach here
    	private Boolean isValid_DA_SOAPRequest(Dom.XMLNode node) {
    		Boolean isValidSoapRequest = false;
    		left as an exercise - Applied your logic here
    		return isValidSoapRequest;
        private static String getBody(String url){
        	Http h = new Http();
    		HttpRequest webReq = new HttpRequest();
    		HttpResponse res = h.send(webReq);
    		res.setHeader('Content-Type', 'application/xml'); 
    		return res.getbody();
        //1. Before you can access external servers from an endpoint or redirect endpoint using Apex or any other feature, you must add the remote site to a list of authorized remote sites in the Salesforce user interface. 
        //   Unauthorized endpoint, please check Setup->Security->Remote site settings - Add your site endpoint
        //2.The return type of you WS MUST be void, so you can return any content type, in our case a valid SOAP response
        global static void doPost() {
        	//RestRequest restRequest = RestContext.request;
        	//String soapRequestBody= restRequest.requestBody ; 
        	String soapRequestBody = RestContext.request.requestBody.toString();
        	System.debug('*** soapRequestBody=' + soapRequestBody);
        	System.debug('*** parseXMLStr=' + parseXMLStr(soapRequestBody));    	
        	RestContext.response.addHeader('Content-Type', 'application/xml');
            String urlSuccess = '';
            //String urlFailed = '';
    		left as an exercise - Applied your logic here
    		RestContext.response.responseBody = Blob.valueOf(getBody(urlSuccess));

    Screen Shot of my PoC

    HTTP GET on URL provide your the WSDL interface
    01-WSL Exposition from GET - 2015-09-20_17-48-59

    Calling you webservice from SOAPUI

    Configure the remote site setting to expose publically your Web Service
    02-1- Remote Site - 2015-09-20_17-31-00

    Use the static resource to store your SOAP Request/Response/ and WSDL files.
    02-2-Static resource - 2015-09-20_17-50-27

    Thanks for reading.