2012-09-21 13 views
2

我有一個WCF客戶端應用程序,它是用VB.NET 2008作爲Windows窗體應用程序編寫的。此客戶端應用程序與其他公司維護的遠程非WCF服務成功通信。問題是 - 只有當客戶端應用程序從Visual Studio(VS2008)內運行,而不是作爲內置可執行文件運行時,通信纔會成功。當客戶端應用程序作爲可執行文件運行時,遠程服務會返回以下消息:當在Visual Studio外運行時,來自WCF客戶端的傳出消息不完整

「HTTP請求未經客戶端身份驗證方案'匿名'未經授權從服務器接收的身份驗證標頭爲''遠程服務器返回一個錯誤:(401)未經授權。「

挖得更深一點,我注意到了這個錯誤的原因。當客戶端應用程序在VS之外運行時,發送到遠程服務的消息缺少在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> 

當作爲一個獨立的可執行運行時,客戶端應用程序發出相同的如上面除了整個的HttpRequest部分缺少 - 從<的HttpRequest >到<一切/ HttpRequest的>

誰能告訴我,爲什麼在運行Visual Studio以外的客戶端應用程序會導致消息的HttpRequest部分脫落?兩種情況下,app.config文件都是相同的。

謝謝。


每邁克的要求,這裏是一些更多的信息。客戶端代理是使用Visual Studio 2008中的「添加服務引用」創建的。

創建發送到服務的消息的代碼如下所示分爲三部分。

第一部分是一個名爲AntechServiceReference的類。它有兩個相關的方法。其構造函數構建將用於與Web服務交互的代理。另一種稱爲GetPendingDownloads的方法調用Web服務方法。

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 

除了創建代理之外,上面的構造函數還向它添加了一個端點行爲。該行爲在接下來的類中定義。此行爲的目的是添加自定義消息檢查注入授權信息到HTTP報頭被髮送之前,消息出來:

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 

最後一塊的代碼是自定義消息檢查本身:

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 

最後,這裏是配置文件:

<?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客戶端成功地調用服務和下載達ta - 但只能在Visual Studio中運行,這是我不瞭解的部分。

+0

,你能否告訴我們,建立消息的代碼?你是如何生成你的客戶類的(手動使用svcutil,使用VS中的「添加web引用」選項等)? –

+0

嗨,邁克。感謝您的關注。我在上面的問題描述中添加了更多信息。 – Mills

+0

代碼中沒有什麼會跳出來。你有沒有嘗試附加一個調試器,看看你的檢查員是否真的被調用,並做你的期望? –

回答

1

這是我必須要解決這個問題。在MessageInspector類的BeforeSendRequest功能,我不得不添加如下所示(即 行的驚歎號之間的界限 - !!!!!!)代碼

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 

出於某種原因仍不清楚我,當我運行的應用程序的可執行文件,將「HttpRequestMessageProperty.Name」屬性不被傳遞到「BeforeSendRequest」功能request.properties存在。我必須明確地創建它 - 與在Visual Studio中以調試模式運行應用程序不同。 (感謝Mike帕克希爾,爲表明「如果」條件可能無法執行如我所料,原來我需要一個額外的ELSE子句如上圖所示)。

相關問題