logo
shape

Apex SOAP Callouts

Apex Soap callouts are a type of functionality available in the Apex programming language that allow developers to make web service calls to external systems using SOAP (Simple Object Access Protocol) APIs.

Soap Callouts in Apex
Generate an Apex class using WSDL2Apex for a SOAP web service, write unit tests that achieve 100% code coverage for the class using a mock response, and run your Apex tests.
Be sure the Remote Sites necessary are set up in the remote settings of your org. Select a WSDL document from your network file system. The WSDL document can be at most 1MB. When the document is parsed, each namespace becomes an Apex class. The generated classes contain methods corresponding to each of the operations defined in the WSDL document and also contain methods for logging in to and out of the service, and for querying the endpoint for the service's location. The generated classes are contained in a single Apex class named after the WSDL document. It should annotated with the webservice keyword so that methods in the class can be exposed through the SOAP web service and annotated with the global keyword so that the class is visible to all Apex code, regardless of the namespace the class is contained in. The generated Apex class is annotated with the virtual keyword so that the class can be extended.

//Generated by wsdl2apex

public class ParkService {
    public class byCountryResponse {
        public String[] return_x;
        private String[] return_x_type_info = new String[]{'return','http://parks.services/',null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{'http://parks.services/','false','false'};
        private String[] field_order_type_info = new String[]{'return_x'};
    }
    public class byCountry {
        public String arg0;
        private String[] arg0_type_info = new String[]{'arg0','http://parks.services/',null,'0','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://parks.services/','false','false'};
        private String[] field_order_type_info = new String[]{'arg0'};
    }
    public class ParksImplPort {
        public String endpoint_x = 'https://th-apex-soap-service.herokuapp.com/service/parks';
        public Map inputHttpHeaders_x;
        public Map outputHttpHeaders_x;
        public String clientCertName_x;
        public String clientCert_x;
        public String clientCertPasswd_x;
        public Integer timeout_x;
        private String[] ns_map_type_info = new String[]{'http://parks.services/', 'ParkService'};
        public String[] byCountry(String arg0) {
            ParkService.byCountry request_x = new ParkService.byCountry();
            request_x.arg0 = arg0;
            ParkService.byCountryResponse response_x;
            Map response_map_x = new Map();
            response_map_x.put('response_x', response_x);
            WebServiceCallout.invoke(
              this,
              request_x,
              response_map_x,
              new String[]{endpoint_x,
              '',
              'http://parks.services/',
              'byCountry',
              'http://parks.services/',
              'byCountryResponse',
              'ParkService.byCountryResponse'}
            );
            response_x = response_map_x.get('response_x');
            return response_x.return_x;
        }
    }
}
                    
Creating a Class, Test Class, & Unit Tests
Create a test class named ParkLocatorTest that uses a mock class called ParkServiceMock to mock the callout to the web service. The class must have a country method that uses the ParkService class and a method must return an array of available park names for a given country passed to the web service.

public class ParkLocator {
    public static String[] country(String country) {
        ParkService.ParksImplPort service = new ParkService.ParksImplPort();
        return service.byCountry(country);
    }
}

                    

Create a test class named ParkLocatorTest and create unit tests to cover all lines of code included in the ParkLocator class, resulting in 100% code coverage.

@isTest
public class ParkLocatorTest {
    @isTest static void testCountry() {
        ParkServiceMock mock = new ParkServiceMock();
        Test.setMock(WebServiceMock.class, mock);
        Test.startTest();
        String[] result = ParkLocator.country('US');
        Test.stopTest();
        System.assertEquals(2, result.size);
        System.assertEquals('Yellowstone', result[0]);
        System.assertEquals('Yosemite', result[1]);
    }
}


                    

Remember to wrap the actual callout in `Test.startTest()` and `Test.stopTest()` to make sure it is included in the asynchronous processing and properly measured against governor limits. The next step is to create a mock class called ParkServiceMock that implements the WebServiceMock interface.

@isTest
global class ParkServiceMock implements WebServiceMock {
    global void doInvoke(
        Object stub,
        Object request,
        Map response,
        String endpoint,
        String soapAction,
        String requestName,
        String responseNS,
        String responseName,
        String responseType) {
        
        ParkService.byCountryResponse res = new ParkService.byCountryResponse();
        res.return_x = new String[]{'Yellowstone', 'Yosemite'};
        response.put('response_x', res);
    }
}
Most Asked Questions about SOAP Callouts in Apex
Can we make a synchronous and asynchronous SOAP callout in Apex? If yes, how?

Yes, you can make both synchronous and asynchronous SOAP callouts in Apex. Synchronous callouts are made when the platform waits for the callout to complete before proceeding with the transaction. This is typically done using HTTP or HTTPS protocols. However, there is a limit of 120 seconds for the total time of all callouts in a transaction. If the callout exceeds this limit, the system will throw a runtime exception.

For asynchronous callouts, you can use the @future annotation. This way, the platform does not wait for the callout to complete before moving onto the next transaction. It's a way to get around the limit. However, remember that methods with the @future annotation can't be used in Visualforce controllers in `getMethodName()`, `setMethodName()`, or the constructor. Also, they cannot be used in trigger context. As of 2023, Salesforce also introduced a more modern way to do asynchronous processing called Queueable Apex which supports chaining of jobs.

Can we handle SOAP callout errors in Apex?

Yes, error handling in SOAP callouts can be done using try/catch blocks in Apex. For example, the WebServiceCallout.invoke method can throw a CalloutException if the callout fails. We can wrap the callout in a try block and catch the CalloutException to handle errors. This is a good practice to avoid unhandled exceptions which can disrupt the flow of execution.

Is it possible to make SOAP callouts to services with self-signed SSL certificates in Apex?

As of the last update in 2021, Salesforce does not allow callouts to services with self-signed SSL certificates. The server you're making the callout to must have a CA-signed SSL certificate. Self-signed certificates are typically not considered as secure because they lack the chain of trust which is provided by CA-signed certificates. The services you make callouts to must adhere to certain requirements, including having a valid CA-signed SSL certificate.

© All rights Reserved. 2023