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 내에서 실행될 때만 이해할 수 있습니다.
메시지를 작성하는 코드를 표시 할 수 있습니까? 어떻게 클라이언트 클래스를 생성 했습니까 (손, svcutil, VS의 "웹 참조 추가"옵션 사용)? –
안녕하세요, 마이크. 관심을 가져 주셔서 감사합니다. 위의 문제 설명에 훨씬 더 많은 정보를 추가했습니다. – Mills
코드에서 나와 아무 것도 뛰지 않습니다. 검사기가 실제로 호출되고 있는지 예상하기 위해 디버거를 연결하려고 시도 했습니까? –