LoginSignup
1
0

More than 1 year has passed since last update.

SOAP通信をしてみた(F#)

Posted at

Summary

F#でSOAP通信をしてみました。その時のメモ

wsdlの読込

FSharp.Data.WsdlProviderを使うと手軽に行けるが、うまくいかない場合があったので、Visual Studio よりC#クラスを自動作成(プロジェクト > 接続済みサービス > 接続済サービスの管理 > サービス参照の追加 > WCF Web Service)。その分を使用すると確実

基本形

下記の様なコードで。。。

module myMain =

  open System
  open System.Linq
  open System.ServiceModel
  open System.ServiceModel.Channels
  open System.Security.Cryptography.X509Certificates
  open System.Xml

  open System.ServiceModel.Dispatcher
  open System.ServiceModel.Description


  let testURL = """https://xxx"""
  let endpointAddress = EndpointAddress(testURL)

  // 通信設定
  let tempBinding = new WSHttpBinding()
  tempBinding.Security.Mode <- SecurityMode.Transport
  tempBinding.Security.Transport.ClientCredentialType <- HttpClientCredentialType.Certificate

  // カスタムエンコーダー
  let messageEncodingBindingElementType = typedefof<MessageEncodingBindingElement>
  let elements = tempBinding.CreateBindingElements();

  let elementsWithoutEncodingElement =
    elements |> Seq.filter(fun elm -> messageEncodingBindingElementType.IsAssignableFrom(elm.GetType()) |> not)

  let newEncodingElement =

    // TEXTを使用
    new TextMessageEncodingBindingElement()
    |> fun x ->
      x.MessageVersion <- MessageVersion.Soap12
      x:> BindingElement


  let binding = new CustomBinding(elementsWithoutEncodingElement.Prepend(newEncodingElement))
  let client = new soapClient()
  client.Endpoint.Address <- endpointAddress
  client.Endpoint.Binding <- binding


  // セキュリティ設定
  client.ClientCredentials.ClientCertificate.SetCertificate(
      StoreLocation.CurrentUser
    , StoreName.My
    , X509FindType.FindByThumbprint
    , "拇印"
  )

  client.ClientCredentials.ServiceCertificate.SetDefaultCertificate (
      StoreLocation.CurrentUser
    , StoreName.CertificateAuthority
    , X509FindType.FindByThumbprint
    , "拇印"
  )

  client.request()
  |> printfn "%A"

エンコーディング

MTOM

  let newEncodingElement =
    new MtomMessageEncodingBindingElement()
    |> fun x ->
      x.MessageVersion <- MessageVersion.Soap12
      x:> BindingElement

カスタムエンコーダー使用

(*

  カスタム テキスト エンコーダー
  https://github.com/dotnet/samples/tree/main/framework/wcf/Extensibility/MessageEncoder/Text/CS/library

  上記のプロジェクトをソリューションに追加

*)

  let newEncodingElement =
   new Microsoft.Samples.CustomTextMessageEncoder.CustomTextMessageBindingElement()
    |> fun x -> 
      x.Encoding <- "UTF-8"
      x.MessageVersion <- MessageVersion.Soap11
      x.MediaType <- "text/xml"
      x:> BindingElement

ヘッダーに追記

module Util =

  (*

    see also: Thoughts Compiler(Add custom SOAP headers to your service requests)
    https://ion-sapoval.github.io/.net/c%23/soap/headers/2014/07/29/create-custom-soap-header.html

  *)

  type CustomHeader ()  =
    inherit MessageHeader()

    override _.Name = "Header"
    override _.Namespace = "http://foo"

    override this.OnWriteStartHeader(writer : XmlDictionaryWriter, messageVersion:MessageVersion) =
      writer.WriteStartElement("a", this.Name, this.Namespace);
      this.WriteHeaderAttributes(writer, messageVersion);

    override _.OnWriteHeaderContents(writer :XmlDictionaryWriter, messageVersion:MessageVersion) =
      writer.WriteStartElement("timeStamp")
      writer.WriteString("20220502012300000")
      writer.WriteEndElement()

module myMain =
 
 (* 中略 *)

  // SOAPヘッダー追加
  do
  use scope = new OperationContextScope(client.InnerChannel)
  OperationContext.Current.OutgoingMessageHeaders.Add(new Util.CustomHeader())

Inspector

メッセージ送受信前後で処理を入れる場合

module Util3 =

  (*

    see also : Message Inspector ( https://docs.microsoft.com/ja-jp/dotnet/framework/wcf/samples/message-inspectors )

  *)

  type Inspector () =
    interface IClientMessageInspector with
      member this.AfterReceiveReply(reply: byref<Message>, correlationState: obj): unit =
        Console.WriteLine("IClientMessageInspector.AfterReceiveReply called.");
      member this.BeforeSendRequest(request: byref<Message>, channel: IClientChannel): obj =
        Console.WriteLine("IClientMessageInspector.BeforeSendRequest called.")

  type MyEndpointBehavior () =
    interface IEndpointBehavior with
      member this.AddBindingParameters(endpoint: ServiceEndpoint, bindingParameters: BindingParameterCollection): unit =
        ()
      member this.ApplyClientBehavior(endpoint: ServiceEndpoint, clientRuntime: ClientRuntime): unit =
        clientRuntime.ClientMessageInspectors.Add(new Inspector())
      member this.ApplyDispatchBehavior(endpoint: ServiceEndpoint, endpointDispatcher: EndpointDispatcher): unit =
        ()
      member this.Validate(endpoint: ServiceEndpoint): unit =
        ()

module myMain =

  (* 中略 *)

  client.Endpoint.EndpointBehaviors.Add(Util3.MyEndpointBehavior())

HttpClientで処理する場合

(*

  1. HttpClientでpostする
  2. レスポンス内容からデータを抽出する
  
*)

open System
open System.Net.Http
open System.Net.Security
open System.Security.Authentication
open System.Security.Cryptography.X509Certificates

let mainImpl (client : HttpClient) (url:string) = task {

  let content = "ここに送信内容"
  let! response = client.PostAsync(url,content)
  let! responseContent = response.Content.ReadAsStringAsync()
  return ( responseContent |> (*データ抽出*) )

}

let handler = new HttpClientHandler()
handler.ClientCertificateOptions <- ClientCertificateOption.Manual
handler.SslProtocols <- SslProtocols.Tls12
handler.ClientCertificates.Add((*certの設定*)) |> ignore
handler.ServerCertificateCustomValidationCallback <-
  System.Func<HttpRequestMessage,X509Certificate2,X509Chain,SslPolicyErrors,bool>(
    fun _ _ _ _ ->
      handler.ClientCertificates.Add((*certの設定*)) |> ignore
      true
  )
let client = new HttpClient(handler)

mainImpl client myUrl |> fun x -> x.Result |> printfn "%A"

see also

SOAPヘッダーにXMLデータを挿入したいです。。。
SOAPデータを読み込みたいです。。。
カスタムエンコーダー C# => F#

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0