2012-09-21 4 views
2

Windows Forms 응용 프로그램으로 VB.NET 2008로 작성된 WCF 클라이언트 응용 프로그램이 있습니다. 이 클라이언트 응용 프로그램은 다른 회사에서 관리하는 원격 비 WCF 서비스와 성공적으로 통신합니다. 문제는 클라이언트 응용 프로그램이 빌드 된 실행 파일로 실행될 때가 아니라 Visual Studio (VS2008) 내에서 실행될 때만 통신이 성공한 것입니다.Visual Studio 외부에서 실행할 때 WCF 클라이언트의 보내는 메시지가 불완전합니다.

"클라이언트 인증 스키마 '익명'으로 HTTP 요청이 허가되지 않았습니다. 서버에서받은 인증 헤더가 ''입니다. 원격 서버 오류 (401)가 반환되었습니다. "

이 오류의 원인을 조금 더 파헤 쳤습니다. 클라이언트 응용 프로그램이 VS 외부에서 실행될 때 원격 서비스로 전송되는 메시지에는 VS 내부에서 실행될 때 포함되는 섹션이 누락되어 있습니다.

<HttpRequest  xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace"> 
    <Method>POST</Method> 
    <QueryString></QueryString> 
    <WebHeaders> 
    <VsDebuggerCausalityData>uIDPo6ppHQnHmDRGnZfDLPni6RYAAAAAaEkfl5VJXUauv5II8hPneT1AMwBfkoZNgfxEAZ2x4zQACQAA</VsDebuggerCausalityData> 
    <AUTHORIZATION>xxxxxxxxxxxxxxxxxxxxxxxxxxxx</AUTHORIZATION> 
    </WebHeaders> 
</HttpRequest> 
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> 
    <s:Header> 
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none"></Action> 
    </s:Header> 
    <s:Body s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <q1:getNewEvents_PPHS xmlns:q1="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxins"> 
     <loginObject href="#id1" xmlns=""></loginObject> 
    </q1:getNewEvents_PPHS> 
    <q2:LoginObject id="id1" xsi:type="q2:LoginObject" xmlns:q2="java:zoasis.ws.datamodel.general"> 
     <clinicId xsi:type="xsd:int" xmlns="">xxxxx</clinicId> 
     <corporateId xsi:type="xsd:int" xmlns="">x</corporateId> 
     <password xsi:type="xsd:string" xmlns="">xxxxx</password> 
     <userName xsi:type="xsd:string" xmlns="">xxxx</userName> 
    </q2:LoginObject> 
    </s:Body> 
</s:Envelope> 

독립 실행으로 실행되는 클라이언트 응용 프로그램은 동일한 발송 : 응용 프로그램이 VS 내에서 실행 할 때 전송되는 메시지 (제대로 작동 즉, 하나) "X"로 대체 민감한 정보를 다음과 같습니다 전체 HttpRequest에 섹션 것을 제외하고 위와 같이없는 - 모든 < HttpRequest를 > <까지/HttpRequest를 >

비주얼 스튜디오 외부의 클라이언트 응용 프로그램을 실행하는 이유

는 사람이 내려 메시지의 HttpRequest에 부분 원인이 말해 줄 수 ? app.config 파일은 두 경우 모두 동일합니다.

감사합니다.


마이크의 요청에 따라 다음과 같은 정보가 더 있습니다. 클라이언트 프록시는 Visual Studio 2008의 "서비스 참조 추가"를 사용하여 만들어졌습니다.

서비스로 보내는 메시지를 만드는 코드는 아래 세 부분으로 표시됩니다.

첫 번째 부분은 AntechServiceReference라는 클래스입니다. 두 가지 관련 방법이 있습니다. 생성자는 웹 서비스와 상호 작용하는 데 사용될 프록시를 만듭니다. GetPendingDownloads라고하는 다른 메서드는 웹 서비스 메서드를 호출합니다.

Imports WebServiceInterface.AntechServiceReference 
Imports System.Configuration.ConfigurationManager 
Imports System.ServiceModel 
Imports System.ServiceModel.Security 
Imports System.Text 
Imports System.IO 
Imports System.Xml 

Public Class AntechLabDataAccess 
    ' This class controls all data interaction with the remote Antech web service. 

    Private ClassName As String = "AntechLabDataAccess" 
    Private mErrText As String 
    Private mAntServProxy As ZoasisGroupServicesPortClient 
    Private mLoginObject As WebServiceInterface.AntechServiceReference.LoginObject 
    Private mLabEventIDs As WebServiceInterface.AntechServiceReference.LabAccessionIdObject() 

    Public Sub New() 
     Dim Action As String = "" 
     Dim CustomBehavior As MessageEndPointBehavior 

     Try 
      mErrText = "" 
      Action = "Creating proxy for web service. " 

      ' Create a proxy to the remote web service for use in this object. Supply client credentials 
      ' from app.config 

      mAntServProxy = New ZoasisGroupServicesPortClient("ZoasisGroupServicesPort") 

      ' Retrieve access credentials for this web service from app.config. 
      Action = "Setting up login object. " 
      mLoginObject = New WebServiceInterface.AntechServiceReference.LoginObject 
      If Not AppSettings("ClinicID") Is Nothing Then 
       mLoginObject.clinicId = Integer.Parse(AppSettings("ClinicID")) 
      End If 
      If Not AppSettings("CorporateID") Is Nothing Then 
       mLoginObject.corporateId = Integer.Parse(AppSettings("CorporateID")) 
      End If 
      If Not AppSettings("Password") Is Nothing Then 
      mLoginObject.password = AppSettings("Password") 
      End If 
      If Not AppSettings("UserName") Is Nothing Then 
       mLoginObject.userName = AppSettings("UserName") 
      End If 

      ' Add our custom behavior to the proxy. This handles creation of the message credentials 
      ' necessary for web service authorization. 
      Action = "Adding custom behavior to the proxy. " 
      CustomBehavior = New MessageEndPointBehavior 
      mAntServProxy.Endpoint.Behaviors.Add(CustomBehavior) 

     Catch ex As Exception 
      mErrText = "Error caught in class [" & ClassName & "], method [New]. Action = " & Action & " Message = " & ex.Message & ". " 
      If Not ex.InnerException Is Nothing Then 
       mErrText &= "Additional Info: " & ex.InnerException.ToString & ". " 
      End If 
      Throw New Exception(mErrText) 
     End Try 

    End Sub 

    Public Sub GetPendingDownloads() 
     Dim Action As String = "" 

     Try 
      mErrText = "" 
      Action = "Calling getNewEvents_PPHS. " 
      mLabEventIDs = mAntServProxy.getNewEvents_PPHS(mLoginObject) 

     [catches are here] 

     End Try 

    End Sub 

End Class 

프록시를 만드는 것 외에도 위의 생성자는 끝점 동작을 추가합니다. 그 행동은 다음에 표시된 클래스에서 정의됩니다.

Imports System.ServiceModel.Dispatcher 
Imports System.ServiceModel.Channels 
Imports System.Configuration.ConfigurationManager 
Imports System.Text 

Public Class MessageInspector 
    Implements IClientMessageInspector 
    ' This class gives access to the outgoing SOAP message before it is sent so it can 
    ' be customized. 

    Private mUserName As String 
    Private mPassword As String 
    Private mErrText As String 

    Public Sub New() 
     Dim CredentialsProvided As Boolean 

     CredentialsProvided = False 
     mUserName = AppSettings("CliCredUserName") 
     If Not mUserName Is Nothing Then 
      If mUserName.Trim <> "" Then 
       CredentialsProvided = True 
      End If 
     End If 

     If CredentialsProvided Then 
      CredentialsProvided = False 
      mPassword = AppSettings("CliCredPassword") 
      If Not mPassword Is Nothing Then 
       If mPassword.Trim <> "" Then 
        CredentialsProvided = True 
       End If 
      End If 
     End If 

     If CredentialsProvided Then 
      mUserName = mUserName.Trim 
      mPassword = mPassword.Trim 
     Else 
      Throw New Exception("This class (MessageInspector) requires information from the app.config file - specifically " _ 
      & "AppSettings values for CliCredUserName and CliCredPassword. One or both of these is missing. ") 
     End If 

    End Sub 

    Public Sub AfterReceiveReply(ByRef reply As System.ServiceModel.Channels.Message, ByVal correlationState As Object) Implements System.ServiceModel.Dispatcher.IClientMessageInspector.AfterReceiveReply 
     ' Not Implemented 
    End Sub 

    Public Function BeforeSendRequest(ByRef request As System.ServiceModel.Channels.Message, ByVal channel As System.ServiceModel.IClientChannel) As Object Implements System.ServiceModel.Dispatcher.IClientMessageInspector.BeforeSendRequest 

     Dim HTTPMsgHdr As HttpRequestMessageProperty 
     Dim objHTTPRequestMsg As Object = Nothing 
     Dim Auth As String = "" 
     Dim Action As String = "" 
     Dim BinaryData As Byte() 

     Try 
      Action = "Checking HTTP headers. " 
      If request.Properties.TryGetValue(HttpRequestMessageProperty.Name, objHTTPRequestMsg) Then 
       Action = "Changing existing HTTP header. " 
       HTTPMsgHdr = CType(objHTTPRequestMsg, HttpRequestMessageProperty) 
       If Not HTTPMsgHdr Is Nothing Then 
        If String.IsNullOrEmpty(HTTPMsgHdr.Headers("AUTHORIZATION")) Then 
         Auth = mUserName & ":" & mPassword 
         ReDim BinaryData(Auth.Length) 
         BinaryData = Encoding.UTF8.GetBytes(Auth) 
         Auth = Convert.ToBase64String(BinaryData) 
         Auth = "Basic " & Auth 
         HTTPMsgHdr.Headers("AUTHORIZATION") = Auth 
        End If 
       Else 
        Throw New Exception("Received unexpected empty object HTTPMsgHdr from request properties. " _ 
        & "This error occurred in class ""MessageInspector"" and function ""BeforeSendRequest."" ") 
       End If 
      End If 

     Catch ex As Exception 
      mErrText = "Error caught in BeforeSendRequest function of MessageInspector class: Action = " _ 
      & Action & "; Message = " & ex.Message & " " 
      If Not ex.InnerException Is Nothing Then 
       mErrText &= "Additional Information: " & ex.InnerException.ToString & " " 
      End If 
      Throw New Exception(mErrText) 
     End Try 

     Return Convert.DBNull 

    End Function 
End Class 
:

Imports System.ServiceModel.Description 

Public Class MessageEndPointBehavior 
    Implements IEndpointBehavior 
    ' This class is used to make our custom message inspector available to the system. 

    Public Sub AddBindingParameters(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint, ByVal bindingParameters As System.ServiceModel.Channels.BindingParameterCollection) Implements System.ServiceModel.Description.IEndpointBehavior.AddBindingParameters 
     ' Not Implemented 
    End Sub 

    Public Sub ApplyClientBehavior(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint, ByVal clientRuntime As System.ServiceModel.Dispatcher.ClientRuntime) Implements System.ServiceModel.Description.IEndpointBehavior.ApplyClientBehavior 
     ' Add our custom message inspector to the client runtime list of message inspectors. 
     clientRuntime.MessageInspectors.Add(New MessageInspector()) 
    End Sub 

    Public Sub ApplyDispatchBehavior(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint, ByVal endpointDispatcher As System.ServiceModel.Dispatcher.EndpointDispatcher) Implements System.ServiceModel.Description.IEndpointBehavior.ApplyDispatchBehavior 
     ' Not Implemented 
    End Sub 

    Public Sub Validate(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint) Implements System.ServiceModel.Description.IEndpointBehavior.Validate 
     ' Not Implemented 
    End Sub 
End Class 

코드의 마지막 부분은 사용자 지정 메시지 속성 그 자체입니다 : 메시지가 발송되기 전에이 동작의 목적은 HTTP 헤더에 인증 정보를 삽입하는 사용자 정의 메시지 관리자를 추가하는 것입니다

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
    <configSections> 
    </configSections> 
    <appSettings> 
     <!-- Client Credentials --> 
     <add key="CliCredUserName" value="xxxxxxx"/> 
     <add key="CliCredPassword" value="xxxxxxx"/> 

     <!-- Login Object Fields --> 
     <add key="ClinicID" value="xxxxx"/> 
     <add key="CorporateID" value="x"/> 
     <add key="Password" value="xxxxx"/> 
     <add key="UserName" value="xxxx"/> 

    </appSettings> 

    <system.serviceModel> 
     <diagnostics> 
     <messageLogging logEntireMessage="false" logMalformedMessages="false" 
      logMessagesAtServiceLevel="false" logMessagesAtTransportLevel="false" /> 
     </diagnostics> 
     <bindings> 
     <basicHttpBinding> 
      <binding name="ZoasisGroupServicesPort" closeTimeout="00:01:00" 
       openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" 
       allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" 
       maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" 
       messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" 
       useDefaultWebProxy="true"> 
       <readerQuotas maxDepth="32" maxStringContentLength="118192" maxArrayLength="16384" 
        maxBytesPerRead="4096" maxNameTableCharCount="16384" /> 
       <security mode="Transport"> 
        <transport clientCredentialType="None" proxyCredentialType="None" 
        realm="" /> 
        <message clientCredentialType="UserName" algorithmSuite="Default" /> 
       </security> 
      </binding> 
     </basicHttpBinding> 
     </bindings> 
     <client> 
     <endpoint address="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 
      binding="basicHttpBinding" bindingConfiguration="ZoasisGroupServicesPort" 
      contract="AntechServiceReference.ZoasisGroupServicesPort" name="ZoasisGroupServicesPort" /> 
     </client> 
    </system.serviceModel> 
    <system.net> 
     <!-- Important: The following setting strips off the "HTTP/1.1 100 Continue" banner from incoming 
      messages. Unless this is done, incoming XML messages are not recognized as XML. --> 
     <settings> 
      <servicePointManager expect100Continue="false"/> 
     </settings> 
    </system.net> 
</configuration> 

앞서 언급 한 바와 같이,이 성공적으로 서비스 및 다운로드 다를 호출하는 기능 WCF 클라이언트는 다음과 같습니다

마지막으로, 여기에 config 파일입니다 하지만 Visual Studio 내에서 실행될 때만 이해할 수 있습니다.

+0

메시지를 작성하는 코드를 표시 할 수 있습니까? 어떻게 클라이언트 클래스를 생성 했습니까 (손, svcutil, VS의 "웹 참조 추가"옵션 사용)? –

+0

안녕하세요, 마이크. 관심을 가져 주셔서 감사합니다. 위의 문제 설명에 훨씬 더 많은 정보를 추가했습니다. – Mills

+0

코드에서 나와 아무 것도 뛰지 않습니다. 검사기가 실제로 호출되고 있는지 예상하기 위해 디버거를 연결하려고 시도 했습니까? –

답변

1

이 문제를 해결하려면이 작업을 수행해야합니다.에 여전히 몇 가지 이유

Action = "Checking HTTP headers. " 
If request.Properties.TryGetValue(HttpRequestMessageProperty.Name, objHTTPRequestMsg) Then 
    Action = "Changing existing HTTP header. " 
    HTTPMsgHdr = CType(objHTTPRequestMsg, HttpRequestMessageProperty) 
    If Not HTTPMsgHdr Is Nothing Then 
     If String.IsNullOrEmpty(HTTPMsgHdr.Headers("AUTHORIZATION")) Then 
     Auth = mUserName & ":" & mPassword 
     ReDim BinaryData(Auth.Length) 
     BinaryData = Encoding.UTF8.GetBytes(Auth) 
     Auth = Convert.ToBase64String(BinaryData) 
     Auth = "Basic " & Auth 
     HTTPMsgHdr.Headers("AUTHORIZATION") = Auth 
     End If 
    Else 
     Throw New Exception("Received unexpected empty object HTTPMsgHdr from request properties. " _ 
     & "This error occurred in class ""MessageInspector"" and function ""BeforeSendRequest."" ") 
    End If 
' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
' Added this section 
Else 
    Action = "Creating new HTTP header. " 
    HTTPMsgHdr = New HttpRequestMessageProperty 
    If String.IsNullOrEmpty(HTTPMsgHdr.Headers("AUTHORIZATION")) Then 
     Auth = mUserName & ":" & mPassword 
     ReDim BinaryData(Auth.Length) 
     BinaryData = Encoding.UTF8.GetBytes(Auth) 
     Auth = Convert.ToBase64String(BinaryData) 
     Auth = "Basic " & Auth 
     HTTPMsgHdr.Headers("AUTHORIZATION") = Auth 
    End If 
    request.Properties.Add(HttpRequestMessageProperty.Name, HTTPMsgHdr) 
' End of Added section 
' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
End If 

명확하지 - MessageInspector 클래스의 BeforeSendRequest 기능, 나는 아래 (!!!!!! 즉, 느낌표의 행 사이 선) 표시된 코드를 추가했다 나, 실행 파일로 응용 프로그램을 실행할 때 "HttpRequestMessageProperty.Name"속성이 "BeforeSendRequest"함수에 전달되는 request.properties에 존재하지 않습니다. Visual Studio에서 디버그 모드로 응용 프로그램을 실행할 때와 달리 명시 적으로 만들어야합니다. (Mike Parkhill에게 감사드립니다. "If"조건이 예상대로 실행되지 않을 수도 있음을 상기 시켰습니다. 위와 같이 ELSE 절이 필요했습니다.)