+ Start a Discussion
paul-lmipaul-lmi 

Exposing Apex Web Service class via Force.com Sites

It seems like this isn't working.  Back in April we had a thread going regarding exposing WebService classes via Sites to allow for public, non-authenticated webservices.

 

http://boards.developerforce.com/t5/Apex-Code-Development/SOAP-request-to-APEX-webservice-without-requiring-authentication/m-p/182108#M29174

 

In implementing this in one of our sandboxes, we keep getting the a soap fault message

 

sample request

 

 

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sel="http://soap.sforce.com/schemas/class/SelfServiceAPI">
   <soapenv:Body>
      <sel:insertTicket>
         <sel:email>test@test.com</sel:email>
         <sel:name>test user</sel:name>
         <sel:product>widgets</sel:product>
         <sel:subject>testing api ticket submit</sel:subject>
         <sel:description>testticketdescription</sel:description>
      </sel:insertTicket>
   </soapenv:Body>
</soapenv:Envelope>

 

 

and the response:

 

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sf="http://soap.sforce.com/2006/08/apex">
   <soapenv:Body>
      <soapenv:Fault>
         <faultcode>sf:INVALID_SESSION_ID</faultcode>
         <faultstring>INVALID_SESSION_ID: Invalid Session ID found in SessionHeader: Illegal Session</faultstring>
      </soapenv:Fault>
   </soapenv:Body>
</soapenv:Envelope>

 

So here's the question (hopefully Bulent is reading :) ), can we get a working example (XML request and response) for using an Apex Web Service via Sites?  It would seem that the login() API call is a requirement no matter where the API call is targeted at, since that's the only way to get a session, so...how do you execute a login for a Sites guest user?

 

 

Best Answer chosen by Admin (Salesforce Developers) 
paul-lmipaul-lmi

Got it!

 

 

 

So, the proper steps to expose a webservice to via sites

  

 

  1. Create Global Class with defined Web Service methods
  2. In the site you want to use, go to Public Access Settings, and add the class you created
  3. In Apex Classes, go to the class you created, and generate WSDL
  4. Edit the endpoint in the WSDL to point it at the site, instead of the default API endpoint (optional if you want, and can, set this on the fly in your programming language)
In production/developer orgs, this will be
https://<yourcompanyinsites>.secure.force.com/<sitename>

 

 

 

In sandbox, this will be
https://<yourcompanyinsites>.<sandboxname>.<sandboxinstance>.force.com/<sitename>

 

 

 

<companyinsites> - is the unique sites namespace you chose when creating your first force.com site, this is listed on the main Sites setup page.
<sitename> - is the unique name you chose for the site.  this is case sensitive
<sandboxinstance> - is the SF instance it's running on, like cs3
<sandboxname> - is the name you chose when creating your sandbox

  

Later in this thread, it was noted that .NET has a special header handling requirement that needs to be met to work with this solution.  In .NET, before executing any webservices calls based on this flow, execute the following (or just add it as part of the instantiation of your .NET wrapper class)

  

System.Net.ServicePointManager.Expect100Continue = false;

 

 

 

 

 

 

 

 

 

All Answers

SuperfellSuperfell

What URL are you sending the request to (it looks like the wrong one based on the namespaces in the response)

paul-lmipaul-lmi

https://lmisupport.logmein.cs3.force.com/services/Soap/class/SelfServiceAPI

 

where lmisupport is the site prefix, and logmein is our namespace

 

Thanks for the quick reply!

paul-lmipaul-lmi

Got it!

 

 

 

So, the proper steps to expose a webservice to via sites

  

 

  1. Create Global Class with defined Web Service methods
  2. In the site you want to use, go to Public Access Settings, and add the class you created
  3. In Apex Classes, go to the class you created, and generate WSDL
  4. Edit the endpoint in the WSDL to point it at the site, instead of the default API endpoint (optional if you want, and can, set this on the fly in your programming language)
In production/developer orgs, this will be
https://<yourcompanyinsites>.secure.force.com/<sitename>

 

 

 

In sandbox, this will be
https://<yourcompanyinsites>.<sandboxname>.<sandboxinstance>.force.com/<sitename>

 

 

 

<companyinsites> - is the unique sites namespace you chose when creating your first force.com site, this is listed on the main Sites setup page.
<sitename> - is the unique name you chose for the site.  this is case sensitive
<sandboxinstance> - is the SF instance it's running on, like cs3
<sandboxname> - is the name you chose when creating your sandbox

  

Later in this thread, it was noted that .NET has a special header handling requirement that needs to be met to work with this solution.  In .NET, before executing any webservices calls based on this flow, execute the following (or just add it as part of the instantiation of your .NET wrapper class)

  

System.Net.ServicePointManager.Expect100Continue = false;

 

 

 

 

 

 

 

 

 

This was selected as the best answer
ca_peterson_oldca_peterson_old

I tried following those steps in a C# application, and got back a HTTP 417: expectation failed error when trying to call any webservice methods.

 

Do I in fact need a session? If so, is calling login() on my site url the way to get one?

 

Here's the edit I made to my WSDL:

 

<service name="DiscoveryLicenseService">
  <documentation></documentation>
  <port binding="tns:DiscoveryLicenseBinding" name="DiscoveryLicense">
   <soap:address location="https://sitename.secure.force.com/api/services/Soap/class/DiscoveryLicense"/>
  </port>
 </service>

 

 

From the original:

 

<service name='DiscoveryLicenseService'>
<documentation/>
<port binding='tns:DiscoveryLicenseBinding' name='DiscoveryLicense'>
<soap:address xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/' location='https://cs2-api.salesforce.com/services/Soap/class/DiscoveryLicense'/>
</port>
</service>

 

Any ideas?

 

paul-lmipaul-lmi

I think you're passing in an empty session header.

 

Try ripping out the whole soap header.  Since this doesn't require auth, there's no reason to pass the header.

 

Here's an example request and response.

 

request:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sel="http://soap.sforce.com/schemas/class/SelfServiceAPI">
   <soapenv:Body>
      <sel:insertTicket>
         <sel:email>test@test.com</sel:email>
         <sel:name>test user</sel:name>
         <sel:product>LogMeIn Pro2</sel:product>
         <sel:subject>testing API ticket submit</sel:subject>
         <sel:description>test API ticket description</sel:description>
      </sel:insertTicket>
   </soapenv:Body>
</soapenv:Envelope>

 response:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://soap.sforce.com/schemas/class/SelfServiceAPI">
   <soapenv:Body>
      <insertTicketResponse>
         <result>Ticket created: 00730052</result>
      </insertTicketResponse>
   </soapenv:Body>
</soapenv:Envelope>

 

I use the soapUI plugin for Eclipse to test all this, since it allows you to easily format the request.

 

Also, your endpoint isn't accurate.  It should be <companydefinedinsites>.secure.force.com/<sitename>/etc...

ca_peterson_oldca_peterson_old

I filed a support case about the HTTP error, and they came back noting that .NET languages use an expect header that isn't set due to the lack of the soap header.

 

The fix, is to run this line before making any webservice calls from a .NET language to make sites-based webservices work:

 

System.Net.ServicePointManager.Expect100Continue = false;

 

Also, in .NET at least, you don't have to edit the wsdl, you can instantiate the BlahService object and set the Url attribure to your sites url:

BlahService s = new BlahService();
s.Url = "https://sitesname.secure.force.com/siteurl/services/Soap/class/BlahClass";

 

 

 

 

 

 

paul-lmipaul-lmi

ah, that's good to know.  i opted to change ours because I was handing the WSDL to another dev team in-house, and I wanted it to be a simple import-and-use solution.  I'll pass along the .NET note though, since they are using .NET.  I'll edit my solution post too.

 

I'd post this on the new Cookbook site, but it looks like they are about a month behind in approving solutions, so I'll wait until they are caught up (and approve the 3-5 I've posted since launch).

ashoknaglikar1.3945534997003162E12ashoknaglikar1.3945534997003162E12
Hi Paul,

After following all the steps i am still seeing this error. Can you please suggest me where i have went wrong. I am using SOAP UI to invoke my method. 

Site url: 
http://barclaysCallBack.datamig-centrica.cs86.force.com/services/Soap/class/BarclaysFinalResultWebserivce


XML request: 

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:bar="http://soap.sforce.com/schemas/class/BarclaysFinalResultWebserivce">
   
   <soapenv:Body>
      <bar:SendApplicationDecision>
         <bar:Callback>
            <!--Optional:-->
            <bar:ApplicationId>12345</bar:ApplicationId>
            <!--Optional:-->
            <bar:ApplicationStatus>Approved</bar:ApplicationStatus>
            <!--Optional:-->
            <bar:ExternalReferenceNumber>123456789</bar:ExternalReferenceNumber>
            <!--Optional:-->
            <bar:OutletCode>123</bar:OutletCode>
           </bar:Callback>
      </bar:SendApplicationDecision>
   </soapenv:Body>
</soapenv:Envelope>



 
ashoknaglikar1.3945534997003162E12ashoknaglikar1.3945534997003162E12
I meant its showing me the Seesion ID error.