Skip to main content

 

 

 

Template:OutSystems/Documentation_KB/Breadcrumb_New_Layout

 

 

Template:OutSystems/OSLanguageSwitcher

 

 

 

OutSystems

例: 異なる文字エンコードを使用する

このシナリオ例では、SOAPリクエストのコンテンツにおける異なる文字エンコードの使用のサポートを追加します。

追加設定を行わない場合、SOAPリクエストのときに使用される下位.NETフレームワークであるWCF(Windows Communication Framework)は、UTF-8、UTF-16、ビッグエンディアンUnicodeのみをサポートします。他のエンコードを使用しようとすると、多くの場合、以下のいずれかの実行時エラーが発生します。

FailedSystem.ServiceModel.ProtocolException: The content type text/xml; 
charset=ISO-8859-1 of the response message does not match the content
type of the binding (text/xml; charset=utf-8).If using a custom encoder, 
be sure that the IsContentTypeSupported method is implemented properly.

または:

System.ServiceModel.ProtocolException: There is a problem with the XML 
that was received from the network.See inner exception for more details. 
---> System.Xml.XmlException: The encoding in the declaration 
'iso-8859-1' does not match the encoding of the document 'utf-8'.

Microsoftが提供する「Custom Message Encoder: Custom Text Encoder」のコード例に従って一連のヘルパークラスを実装することにより、他の文字エンコードのサポートを追加することができます。

他の文字エンコードのサポートを追加するには、以下の手順を実行します。

1.Integration Studioでエクステンションを作成し、使用するエンコーディングを定義するアクションを定義します。

以下の例では、Text型のEncoding入力パラメータを持つ「SetEncoding」というアクションをIntegration Studioで定義しています。

2.[Edit Source Code .NET]をクリックします。Visual Studio .NETで、プロジェクトのターゲットフレームワークを設定し、System.ServiceModelおよびSystem.Runtime.Serializationアセンブリに参照を追加します。

3.以下に示すコードをコピーして、Css[yourExtensionName]クラス定義などの下にある[yourExtensionName].csファイルに貼り付け、ヘルパークラスを作成します。
注記: これらのクラスがWCFアーキテクチャにどのように適合するかの詳細については、前述のMicrosoftのページをご覧ください。

// required 'using' statements at the beginning of the file
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;
using System.Xml;

// Custom message encoder that operates both in streaming and buffered mode. 
// It uses the XmlReader and XmlWriter to read and write the messages respectively.
// As opposed to the optimized XML readers and writers of WCF that support only UTF-8, 
// UTF-16 and Big-Endian Unicode encodings, these readers and writers support all 
// .NET platform-supported encodings.

public class CustomTextMessageEncoder : MessageEncoder {

    private CustomTextMessageEncoderFactory factory;
    private XmlWriterSettings writerSettings;
    private string contentType;

    public CustomTextMessageEncoder(CustomTextMessageEncoderFactory factory) {
        this.factory = factory;

        this.writerSettings = new XmlWriterSettings();
        this.writerSettings.Encoding = Encoding.GetEncoding(factory.CharSet);
        this.contentType = string.Format("{0}; charset={1}",
            this.factory.MediaType, this.writerSettings.Encoding.HeaderName);
    }

    public override bool IsContentTypeSupported(string contentType) {
        return base.IsContentTypeSupported(contentType) || contentType == MediaType;
    }

    public override string ContentType {
        get {
            return this.contentType;
        }
    }

    public override string MediaType {
        get {
            return factory.MediaType;
        }
    }

    public override MessageVersion MessageVersion {
        get {
            return this.factory.MessageVersion;
        }
    }

    public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType) {
        byte[] msgContents = new byte[buffer.Count];
        Array.Copy(buffer.Array, buffer.Offset, msgContents, 0, msgContents.Length);
        bufferManager.ReturnBuffer(buffer.Array);

        MemoryStream stream = new MemoryStream(msgContents);
        return ReadMessage(stream, int.MaxValue);
    }

    public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType) {
        XmlReader reader = XmlReader.Create(stream);
        return Message.CreateMessage(reader, maxSizeOfHeaders, this.MessageVersion);
    }

    public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset) {
        MemoryStream stream = new MemoryStream();
        XmlWriter writer = XmlWriter.Create(stream, this.writerSettings);
        message.WriteMessage(writer);
        writer.Close();

        byte[] messageBytes = stream.GetBuffer();
        int messageLength = (int)stream.Position;
        stream.Close();

        int totalLength = messageLength + messageOffset;
        byte[] totalBytes = bufferManager.TakeBuffer(totalLength);
        Array.Copy(messageBytes, 0, totalBytes, messageOffset, messageLength);

        ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, messageOffset, messageLength);
        return byteArray;
    }

    public override void WriteMessage(Message message, Stream stream) {
        XmlWriter writer = XmlWriter.Create(stream, this.writerSettings);
        message.WriteMessage(writer);
        writer.Close();
    }
}


public class CustomTextMessageEncoderFactory : MessageEncoderFactory {

    internal CustomTextMessageEncoderFactory(string mediaType, string charSet,
        MessageVersion version) {
        this.MessageVersion = version;
        this.MediaType = mediaType;
        this.CharSet = charSet;
        this.Encoder = new CustomTextMessageEncoder(this);
    }

    public override MessageEncoder Encoder { get; }
    public override MessageVersion MessageVersion { get; }
    internal string MediaType { get; }
    internal string CharSet { get; }
}


// Binding elements allow the configuration of the WCF run-time stack.
// To use the custom message encoder in a WCF application, a binding element
// is required that creates the message encoder factory with the appropriate 
// settings at the appropriate level in the run-time stack.

public class CustomTextMessageEncodingBindingElement : MessageEncodingBindingElement {

    public override MessageVersion MessageVersion { get; set; }
    public string MediaType { get; set; }
    public string Encoding { get; set; }

    CustomTextMessageEncodingBindingElement(CustomTextMessageEncodingBindingElement binding)
        : this(binding.Encoding, binding.MediaType, binding.MessageVersion) {
    }

    public CustomTextMessageEncodingBindingElement(string encoding, string mediaType,
        MessageVersion messageVersion) {
        this.MessageVersion = messageVersion;
        this.MediaType = mediaType;
        this.Encoding = encoding;
    }

    public CustomTextMessageEncodingBindingElement(string encoding, MessageVersion messageVersion) {
        this.Encoding = encoding;
        this.MessageVersion = messageVersion;
        if (messageVersion.Envelope == EnvelopeVersion.Soap11) {
            this.MediaType = "text/xml";
        } else if (messageVersion.Envelope == EnvelopeVersion.Soap12) {
            this.MediaType = "application/soap+xml";
        } else {
            this.MediaType = "application/xml";
        }
    }

    public override BindingElement Clone() {
        return new CustomTextMessageEncodingBindingElement(this);
    }

    public override MessageEncoderFactory CreateMessageEncoderFactory() {
        return new CustomTextMessageEncoderFactory(MediaType, Encoding, MessageVersion);
    }

    public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context) {
        if (context == null) {
            throw new ArgumentNullException("context");
        }

        context.BindingParameters.Add(this);
        return context.BuildInnerChannelFactory<TChannel>();
    }
}

4.以下のコードを追加し、Integration Studioで作成されたMssSetEncoding関数プレースホルダを置き換えます。

// required 'using' statements at the beginning of the file
using System.Linq;
using OutSystems.SOAP.API;

/* ...*/

// replace the 'MssSetEncoding' function placeholder with the following code
public void MssSetEncoding(string ssEncoding) {

    ISOAPClient client = SoapRequest.GetCurrentClient();

    // Create a binding based on the one already set-up.
    var customBinding = new CustomBinding(client.Endpoint.Binding);

    // Get the current TextMessageEncodingBindingElement
    // that will be replaced with the custom one defined in a helper class.
    var textEncodingElement = customBinding.Elements.OfType<TextMessageEncodingBindingElement>().Single();

    // Create a new instance of our custom TextMessageEncodingBindingElement 
    // with the correct encoding.
    var customTextEncodingElement = new CustomTextMessageEncodingBindingElement(ssEncoding, client.Endpoint.Binding.MessageVersion);

    // Insert customTextEncodingElement in the correct place.
    // Required to respect the mandatory order of elements in a custom binding: https://docs.microsoft.com/en-us/dotnet/framework/wcf/extending/custom-bindings
    customBinding.Elements.Remove(textEncodingElement);
    customBinding.Elements.Insert(customBinding.Elements.Count - 1, customTextEncodingElement);

    //Replace previous binding with the custom one
    client.Endpoint.Binding = customBinding;
}

5.Visual Studio .NETを停止し、Integration Studioに戻ります。Integration Studioで、[1-Click Publish]ツールバーアイコンをクリックするか、またはF5キーを押して、エクステンションをパブリッシュします。

6.Service Studioで、アプリケーションモジュールにエクステンションのSetEncodingアクションへの参照を追加します。

7.SOAP WebサービスのSOAPコールバックフロー(OnBeforeRequestAdvancedフロー)で、SetEncodingアクションをフローにドラッグし、目的のエンコーディング値をEncoding入力パラメータに入力します。

8.アプリケーションモジュールをパブリッシュしてアプリケーションをテストし、利用中のSOAP Webサービスが正しいエンコードで実行されることと、エンコード不一致による実行時エラーが発生しないことを確認します。

  • Was this article helpful?