Sunday, April 28, 2013

SharePoint 2013 Workflow: Use HttpSend Activity to call Custom SharePoint WCF Service

SharePoint 2013 workflow are declarative workflow – means no C# code inside the workflow. All you have in declarative workflow, is predefined activities (or maybe your own custom activities). If you would like to do some very customized things to do in workflow (like verifying data in some external system), you can do so with a WCF service. And then from workflow, you can invoke the WCF. In this post I’ll explain how to create a WCF service and invoke it from workflow. I’ve uploaded the source code in MSDN Code Gallery. Please make sure you have March update for Workflow Manager and Service Bus 1.0 installed, unless the source code might not work properly.

Create a WCF Service

I had described once how to create a WCF service and use it with jQuery for SharePoint 2010 and I’ve created the WCF service based on this post. The workflow Service interface is given below:
[ServiceContract]
public interface IServiceForWorkflow
{
    [OperationContract]
    [WebGet(BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, 
        ResponseFormat = WebMessageFormat.Json, UriTemplate = "/GetData?instanceId={wfInstanceId}")]
    WorkflowData GetWorkflowData(string wfInstanceId);

    [OperationContract]
    [WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, Method = "POST", RequestFormat = WebMessageFormat.Json, 
        ResponseFormat = WebMessageFormat.Json, UriTemplate = "/SendEmail?toEmail={to}&emailSubject={subject}")]
    void SendEmail(string to,string subject);
}

 Code Snippet: Service Interface
As shown in above code snippet, I’ve defined the service method endpoints in UriTemplate. In the UriTemplate, for example /GetData?instanceId={wfInstanceId}, the {wfInstanceId} value is mapped to GetWorflowData method parameter ‘wfInstanceId’. From workflow using HttpSend activity, the service can be invoked as ‘http://serverurl/_vti_bin/ServiceFielName.svc/GetData?instanceId={wfInstanceId}”. The current site url is retrievable from Workflow Context using LookupWorkflowContextProperty activity. Image 1 shows how the HttpSend Activity is used to call the service method from workflow.

Create Workflow

For simplicity, I’ve created a list workflow and once you deploy you need to manually associate the workflow with list/library. You can create either site or list workflow in SharePoint 2013. And if you want you can associate your workflow later instead of development time – maybe through feature receiver. The workflow I’ve created is need to be started manually. To manage workflow development in better ways, I’ve grouped my workflow activities in few groups (parse context values, call wcf service, parse service data etc.) – each group is a sequence of actions.





Use HttPSend Activity to call WCF Service:
To call WCF Service, you need to use HttPSend activity. In this activity you can configure HTTP Verb (GET, POST, MERGE etc.). Since our WCF service will return json data, we need to add two headers (accept and content-type) in the HTtPSend activity to support json data. Also you need to set authorization header to empty string to work properly. Please add the following headers in the HttPSend activity:
Accept "application/json; odata=verbose"
Content-Type "application/json; odata=verbose"
Authorization “”
In the HttpSend activity, the service response returned can be stored in a variable of type DynamicValue. You can read more about DynamicValue at http://msdn.microsoft.com/en-us/library/windowsazure/jj193505(v=azure.10).aspx. The following image shows how you can pass different properties in HttpSend activity:
image
Image 1: HttpSend Activity in use
In the above image, conetent-type and accept header is used to support json data whereas the ‘Authorization’ header is used for WCF service to be invoked properly from workflow.

Parse Service Response:
In the code example provided as part of this post, a workflow variable “WorkflowData” is used to store the returned service response. To retrieve the dynamic data property you can use GetDynamicValueProperty<T> activity, passing the property name. If you have a collection of items returned from service, you can use foreach control block from workflow Toolbox and can apply the GetDynamicValueProperty for each item in foreach. The response from service is stored in a workflow property ‘WorkflowData’ as shown in image 1. The response from service is the object of the following type:
    public class WorkflowData
    {
        public string Subject { get; set; }
        public bool SendEmail { get; set; }
        public string EmailTo { get; set; }
    }

Code Snippet: Workflow data returned from service

The service response is of type ‘WorkflowData’ as defined in above code snippet. To read any property of the ServiceResponse variable (as shown in Image 1) which is of type WorkflowData, GetDynamicValueProperty<T> is used as shown below – where WorkflowData.SendEmail is parsed:
image
Image 2: Parse Service Response property ‘SendEmail’

Finally in the workflow, based on the parsed value (SendEmail property), I’m updating the workflow status.

 

Download Source Code

I’ve uploaded the source code in MSDN code gallery. Please download the source code from http://code.msdn.microsoft.com/SharePoint-2013-Workflow-e14e0e7a

Conclusion

MSDN has good numbers of examples available to start with workflow. Let me give you some links to these examples that might be helpful:
You can get more examples of workflow manager at http://msdn.microsoft.com/en-us/library/windowsazure/jj193482(v=azure.10).aspx.

18 comments:

  1. I cannot access my customized WCF Service deployed on MOSS2013. HTTPCode is badrequest. Have you enabled your WCF service access anonymously?

    ReplyDelete
  2. Accessing WCF service is not related to anonymous access. If your WCF service deployed properly, you should be able to access the WCF service. You can download the code for this post from msdn code gallery and find out if the WCF service works.

    ReplyDelete
    Replies
    1. I can access right now. Thank you so much:) 
      Two tight details:
      1. Using SharePoint default factory 'Microsoft.SharePoint.Client.Services.MultipleBaseAddressWebServiceHostFactory, Microsoft.SharePoint.Client.ServerRuntime, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' in WCF .svc
      2. Add Authorization="" to RequestHeaders property at HttpSend action

      Delete
    2. This comment has been removed by the author.

      Delete
  3. I've been getting Bad Request 400 response. The solution is to change the Factory in .svc file to use MultipleBaseAddressWebServiceHostFactory used for REST services instead of MultipleBaseAddressBasicHttpBindingServiceHostFactory that is used for SOAP services.

    ReplyDelete
  4. Where can I get Microsoft.SharePoint.Client.ServerRuntime.dll? I have downloaded SharePoint Server 2013 Client Components but it's not there. Thanks

    ReplyDelete
  5. I used LookupWorkflowContextProperty -> Current Site to get the site URL which I expected to be http://xzy.local/ but instead it returned http://xyz.local/pm my Project Management sub-web. Do you know any other way in 2013 Workflow to return the site collection url? Thanks Sohel

    ReplyDelete
  6. @Russell, As I can remember I couldn't find any property to get Site Collection Url. Maybe it's by design as workflow will be executed under current site context?

    ReplyDelete
  7. Hi Sohel, I was getting 401 error while calling custom WCF service (Hosted in SP) from workflow. Based on code you shared I changed the request header autherization with "" and it's working fine. I just want to know this is the only solution did you find or any other solution also available for same problem. Please also help me to know your opinion to set request header autherization ="" for production environment.

    ReplyDelete
  8. @Sourabh, the idea of using Authorization header to empty string can be found at http://social.msdn.microsoft.com/Forums/windowsazure/en-US/d3b8dd8f-a1ab-4a52-be58-dfb3e1f2b2eb/httpsend-to-call-custom-wcf-service?forum=wflmgr

    ReplyDelete
  9. Great Post Sohel, all is working well.

    My only issue is that when I invoke the WCF service from the HttpSend activity, the WCF service always runs in the context of the account which is the service account of the Workflow Manager. It never runs with the context of the current logged in user.

    When I do SPContext.Current.Web.CurrentUser inside the service, I always get the service account of the Workflow Manager. However, If I call the service from the browser, the correct user is returned so the issue is not with the WCF service.

    My guess is that it has to do something with the Authorization header being set as "". Do you have any idea?

    ReplyDelete
  10. Vardhaman, I'm not sure but I reckon this is expected. Workflow Service is running under the workflow service account and I expect workflow manager doesn't impersonate the service call. You can dig dipper into the problem or you can go for a simple solution - pass the current username/id in the service call and impersonate the code with the passed user.

    ReplyDelete
    Replies
    1. Yes Impersonating the user by passing the credentials is a workaround I am already considering.

      Just was curious as to why the service call was not made from the current user's context by the workflow manager.

      Delete
  11. Frist thank you for this great effort it help me in WCF creating and call , but i need to ask you about set workflow status activity in SharePoint 2013 , because i couldn't find it in tool box, and also couldn't do it as custom activity.

    thanks in advance

    ReplyDelete
  12. @abdelmohsen, I think it's not shown by default. Try to browse and add the SharePoint Designer activities dll from workflow installation directory (mabye in program files directory)

    ReplyDelete
    Replies
    1. thanks to replay.
      I add the Microsoft.sharepoint.desgin.activity and i can get it.

      Delete
  13. I have tested HTTPSend activity's authorization attribute as "" with HTTPs site (Site and workflow service app both are running on HTTPs) and it's working fine.

    ReplyDelete