2011年5月13日金曜日

REST っぽい Workflow サービスの公開

実際には Workflow サービスにおいて REST 形式での公開は行えません。というのも、MSDN に記載されている通り、メッセージング関係のアクティビティでは定型的な推測のみ行うように用意されており、WebGet 属性や WebInvoke 属性等を付与する事ができないためです。
ですが WCF の機能を利用する事により「それっぽい」形に仕上げる事はできます。
Arguments

前回の記事でも利用したワークフローを WCF を利用し REST っぽい形式で公開するには WCF によるサービスの定義、実装が必要となります。サンプルとして Visual Web Developer にて新規に WCF サービスを作成する流れで説明します。
1: <ServiceContract()>
2: Public Interface IService1
3: 
4:     <OperationContract()>
5:     <WebGet(UriTemplate:="/{Value}")>
6:     Function ExecuteWorkflow(ByVal Value As String) As String
7: 
8: End Interface
WCF サービスのインターフェースを定義します。このあたりは WCF で REST 形式を扱う場合の実装そのものです。WebGet 属性を利用して、引数を受け取るように記載します。続いてサービス部分の実ロジックです。
1: Imports System.Activities
2: Imports System.Activities.XamlIntegration
3: Imports System.Threading
4: Imports System.ServiceModel.Channels
5: Imports System.Web.Hosting
6: 
7: Public Class Service1
8:     Implements IService1
9: 
10:     Private Const WF_NAME As String = "workflow.xaml"
11:     Private Const INPUT_VARIABLE_NAME As String = "INARG"
12:     Private Const OUTPUT_VARIABLE_NAME As String = "OUTARG"
13: 
14:     Private wfResult As String = ""
15:     Private syncEvent As New AutoResetEvent(False)
16: 
17:     Public Function ExecuteWorkflow(Value As String) As String Implements IService1.ExecuteWorkflow
18:         'App_Data フォルダのパスを取得
19:         Dim targetPath = System.IO.Path.Combine(HostingEnvironment.ApplicationPhysicalPath, "App_Data", WF_NAME)
20:         Dim wfFile = ActivityXamlServices.Load(targetPath)
21: 
22:         '入力引数の設定
23:         Dim inArgs As New Dictionary(Of String, Object)
24:         inArgs.Add(INPUT_VARIABLE_NAME, Value)
25: 
26:         Dim wfApps As New WorkflowApplication(wfFile, inArgs)
27:         wfApps.Completed = AddressOf Completed
28: 
29:         Dim resultValue As String = ""
30:         Try
31:             wfApps.Run()
32:             syncEvent.WaitOne()
33:             resultValue = wfResult
34:         Catch ex As Exception
35: 
36:         End Try
37:         Return resultValue
38:     End Function
39: 
40:     Private Sub Completed(ByVal e As WorkflowApplicationCompletedEventArgs)
41: 
42:         If Not ((e.CompletionState = ActivityInstanceState.Canceled) OrElse
43:                   (e.CompletionState = ActivityInstanceState.Faulted)) Then
44:             '正常終了時に結果を取得する
45:             If e.Outputs.ContainsKey(OUTPUT_VARIABLE_NAME) Then
46:                 wfResult = e.Outputs(OUTPUT_VARIABLE_NAME).ToString
47:             End If
48:         End If
49: 
50:         syncEvent.Set()
51:     End Sub
52: 
53: End Class
前回の記事で引数のやりとりについて書きましたが、その内容をそのまま利用しています。行っている処理としては、引数を指定してワークフローを実行しその結果を返却するというシンプルな形です。なお実行するワークフローの xaml ファイルはちゃんと App_Data フォルダを利用するようにしていたのですが、ここのフォルダ位置を取得するのに少し手間取りました。ASP.NET の場合と WCF の場合では微妙に手段を変えなければいけないという制約があり、上記ロジックのように HostEnvironment.ApplicationPhysicalPath にて物理パスを取得する、という方法まで辿り着くのが・・・。
もうひとつREST形式で利用するために web.config ファイルを編集します。
1: <?xml version="1.0" encoding="utf-8"?>
2: <configuration>
3: 
4:     <system.web>
5:         <compilation debug="true" strict="false" explicit="true" targetFramework="4.0" />
6:     </system.web>
7:     <system.serviceModel>
8:         <!-- 追加部分 -->
9:         <services>
10:             <service name="RESTWFService.Service1">
11:                 <endpoint behaviorConfiguration="webHttpBehavior" binding="webHttpBinding"
12:                     bindingConfiguration="" contract="RESTWFService.IService1" />
13:             </service>
14:         </services>
15: 
16:         <behaviors>
17:             <!-- 追加部分 -->
18:             <endpointBehaviors>
19:                 <behavior name="webHttpBehavior">
20:                     <webHttp />
21:                 </behavior>
22:             </endpointBehaviors>
23: 
24:             <serviceBehaviors>
25:                 <behavior>
26:                     <!-- メタデータ情報の開示を避けるには、展開する前に、下の値を false に設定し、上のメタデータのエンドポイントを削除します -->
27:                     <serviceMetadata httpGetEnabled="true"/>
28:                     <!-- デバッグ目的で障害発生時の例外の詳細を受け取るには、下の値を true に設定します。例外情報の開示を避けるには、展開する前に false に設定します -->
29:                     <serviceDebug includeExceptionDetailInFaults="false"/>
30:                 </behavior>
31:             </serviceBehaviors>
32:         </behaviors>
33:         <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
34:     </system.serviceModel>
35:     <system.webServer>
36:         <modules runAllManagedModulesForAllRequests="true"/>
37:     </system.webServer>
38: 
39: </configuration>
WCF 既定の構成となる web.config に2か所記述を追加しています。一つは WCF サービスのエンドポイントの部分、もう一つはそのエンドポイントのベヘイビアの部分です。エンドポイントビヘイビアについては固定的にこういうもの、でいいかと思いますw

サービスのエンドポイント部分は、サービス名を名前空間付きでサービスクラス名、エンドポイントのコントラクトを名前空間付きでインターフェース名を指定し、webHttpBinding を利用するように設定します。
このように WCF サービスを実装し実行すると、通常の REST サービスよろしくブラウザから普通にアクセスできるようになります。
WfRestSvc
本当の REST 形式とは異なり、あくまでもそれっぽい動作を行うだけですが、これを利用する事で通常の Web サービスとして公開する事も可能となります。

0 件のコメント:

コメントを投稿