1

私がここに持っているものの簡単な概要:

WCF Soap サービスは HTTPS 経由で実行されており、タイプ証明書のメッセージ資格情報が使用されています。(mex 以外に) 2 つのエンドポイントを使用しています。1 つは通常のサービス呼び出し用、もう 1 つはストリーミング ファイル用です。これがサービスの私のweb.configです(アイテムのいくつかの名前を編集しました):

<bindings>
  <wsHttpBinding>
    <binding name="wsHttpEndpointBinding">
      <security mode="TransportWithMessageCredential">
        <message clientCredentialType="Certificate" />
      </security>
    </binding>
  </wsHttpBinding>
  <basicHttpBinding>
      <binding name="streamBinding" transferMode="Streamed" messageEncoding="Mtom" maxReceivedMessageSize="2147483646">
          <security mode="TransportWithMessageCredential">
              <message clientCredentialType="Certificate" />
          </security>
      </binding>
  </basicHttpBinding>        
</bindings>
<services>
  <service behaviorConfiguration="Services.Behavior" name="MyInterface">
    <endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" name="wsHttpEndpoint" contract="IMyInterface" />
    <endpoint address="stream" binding="basicHttpBinding" bindingConfiguration="streamBinding" name="streamEndpoint" contract="IMyInterface" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  </service>
</services>
<behaviors>
  <serviceBehaviors>
    <behavior name="Service.Behavior">
      <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="true" />
      <dataContractSerializer maxItemsInObjectGraph="2147483646" />
      <serviceCredentials>
        <serviceCertificate findValue="CN=Server.Temp.Dev" />
      </serviceCredentials>
    </behavior>
  </serviceBehaviors>
</behaviors>

このサービスには、私がテストしているメソッド DownloadDocument があります。署名は次のとおりです。

DocumentDownloadResponse DownloadDocument(DocumentDownloadRequest リクエスト);

渡されたデータが有効でない場合、サービスは例外をスローし、DownloadDocument はそれらの例外をキャッチして、応答オブジェクトでエラー メッセージを返すことに注意してください。

DocumentDownloadRequest は次のようになります。

[MessageContract]
public class DocumentDownloadRequest
{
    [MessageHeader]
    public string SecurityToken { get; set; }

    [MessageHeader]
    public string LoginName { get; set; }

    [MessageHeader]
    public string DocumentId { get; set; }
}

そして DownloadDocumentResponse:

[MessageContract]
public class DocumentDownloadResponse : ServiceResponse<Stream>
{
    public DocumentDownloadResponse()
    {
        Data = Stream.Null;
    }

    [MessageHeader(MustUnderstand = true)]
    public bool Success { get; set; }

    [MessageHeader(MustUnderstand = true)]
    public string ErrorMessage { get; set; }

    [MessageBodyMember(Order = 1)]
    public Stream Data { get; set; }
}

クライアントから呼び出す方法は次のとおりです。

        var soapServiceClient = new SoapServiceClient("streamEndpoint");
        bool success;
        Stream stream;
        string errorMessage =
            soapServiceClient.DownloadDocument(documentId, loginName, securityToken, out success, out stream);

        serviceClient.Close();

SecurityToken と LoginName は検証が必要な項目です。奇妙なのは、私のテスト クライアントから、有効なデータを指定して DownloadDocument を呼び出すと、何度でもファイルを完全にダウンロードできることです。ただし、無効な LoginName または SecurityToken を渡すと、正しくないデータを示すエラー メッセージが表示されます (予想どおり)。ただし、無効なデータを 3 回渡すと、クライアントはタイムアウトします。サービスをローカルで実行しても、この問題は発生しません。すべてが期待どおりに実行されます。奇妙なことに、フィドラーを開いた状態で実行すると、この問題は発生しません。開発サーバーでサービスを実行すると、問題が発生します。

開発サーバーの構成は、ローカルで実行しているものと一致します。SvcTraceTool を使用すると、最初の 2 つの成功した呼び出しのみが記録され、失敗した呼び出しは記録されないことを除いて、エラーは表示されません。エンドポイントが何らかの形で閉じられたように思えます。

崖:

1) 2 つのエンドポイントを持つサービス。そのうちの 1 つはストリーミング (私が関心のあるもの) です。2) ストリーミング エンドポイントを使用して、有効なデータを含むファイルをダウンロードするメソッドを呼び出すことができます。3) サービスは正しくないデータを 2 回キャッチし、3 回目はハングします。SvcTraceTool にログが記録されず、クライアントがタイムアウトします。

何か案は?

ありがとう

4

1 に答える 1

2

ロドリゴに答えるために、詳細を投稿することにしました。

まず、エラーを適切に処理するために、生成されたプロキシ クラスを次のようにラップします。

public class ProxyWrapper<TServiceClientType, TResultType>
    where TServiceClientType : class, ICommunicationObject
{
    private static string _endPoint;

    public ProxyWrapper(string endPoint = "")
    {
        _endPoint = endPoint;
    }

    public TResultType Wrap(Func<string, TServiceClientType> constructWithEndpoint,
                                    Func<TServiceClientType, TResultType> codeBlock)
    {
        TResultType result = default(TResultType);
        TServiceClientType client = default(TServiceClientType);
        try
        {
            client = constructWithEndpoint(_endPoint);
            result = codeBlock(client);
            client.Close();
        }
        catch (Exception)
        {
            if (client != null)
            {
                client.Abort();
            }
            throw;
        }
        return result;
    }
}

次に、サービス呼び出しをラップするクライアント クラスを作成します。DownloadDocument メソッドは次のとおりです。

    public MyServiceResponse<Stream> DownloadDocument(string loginName,
                                                            string documentId)
    {

        var proxyWrapper = new MyProxyWrapper<DocumentDownloadResponse>(StreamEndpoint);
        DocumentDownloadResponse response =
            proxyWrapper.Wrap((client) =>
            {
                Stream data;
                bool success;
                string errorMessage = client.DownloadDocument(documentId, loginName,
                                                              out success,
                                                              out data);
                return new DocumentDownloadResponse
                {
                    Data = data,
                    Success = success,
                    ErrorMessage = errorMessage
                };
            });

        var result = new MyServiceResponse<Stream>
        {
            Success = response.Success,
            ErrorMessage = response.ErrorMessage
        };

        if (!response.Success)
        {
            result.Data = null;
            response.Data.Close();
        }
        else
        {
            result.Data = response.Data;
        }

        return result;
    }

注: MyProxyWrapper は ProxyWrapper を継承し、WCF クライアント プロキシ クラスを指定します。実際の呼び出しは次のようになります。

var myClient = new MyClient();
var downloadDocumentResponse = myClient.DownloadDocument(someId);
                                using (
                                    Stream output =
                                        File.OpenWrite(someFilePath))
                                {
                                    downloadDocumentResponse.Data.CopyTo(output, 2048);
                                    downloadDocumentResponse.Data.Close();
                                }

ストリームで .Close() を呼び出す 2 つの領域に注意してください。1 回はファイルを書き込んだ後、もう 1 回は response.Success == false です。

于 2012-10-12T18:13:34.150 に答える