Dot-Net
WCF - 如果伺服器拋出 FaultException,則中止從客戶端到伺服器的流
我們有一個 WCF 服務,它接受來自客戶端的流(客戶端將文件上傳到伺服器)。但是,如果伺服器在流之前或期間拋出 FaultException,客戶端只會繼續流直到結束,此時它會從伺服器接收到 FaultException - 浪費客戶端的時間和頻寬。
類似問題:
採取以下(簡化的WCF服務)
Namespace JP_WCF <ServiceContract> _ Public Interface IJP_WCF <OperationContract> _ <FaultContract(GetType(JP_WCF_Fault))> _ Sub UploadFile(request As JP_WCF_FileUpload) <OperationContract> _ <FaultContract(GetType(JP_WCF_Fault))> _ Function fakeError(ByVal int1 As Integer, ByVal int2 As Integer) As Integer <OperationContract> _ <FaultContract(GetType(JP_WCF_Fault))> _ Function Ping() As Date End Interface <MessageContract> _ Public Class JP_WCF_FileUpload Implements IDisposable <MessageHeader(MustUnderstand:=True)> _ Public FileName As String <MessageHeader(MustUnderstand:=True)> _ Public Length As Long <MessageBodyMember(Order:=1)> _ Public FileByteStream As System.IO.Stream Public Sub Dispose() Implements IDisposable.Dispose If FileByteStream IsNot Nothing Then FileByteStream.Close() FileByteStream = Nothing End If End Sub End Class <DataContract> _ Public Class JP_WCF_Fault <DataMember> _ Public Property EventID() As Integer <DataMember> _ Public Property Message() As String <DataMember> _ Public Property Description() As String Public Sub New(ByVal _EventID As Integer, ByVal _Message As String, ByVal _Description As String) Me.EventID = _EventID Me.Message = _Message Me.Description = _Description End Sub End Class End Namespace範例伺服器方法:
Try Dim sourceStream As Stream = request.FileByteStream Dim uploadFolder As String = "C:\upload\" Dim filePath As String = Path.Combine(uploadFolder, request.FileName) Using targetStream = New FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None) sourceStream.CopyTo(targetStream) targetStream.Close() sourceStream.Close() End Using Catch ex As Exception Throw New FaultException(Of JP_WCF_Fault)(New JP_WCF_Fault(8, ex.Message, ex.ToString), ex.Message) End Try範例客戶端方法:
Dim fileInfo As New System.IO.FileInfo(filePath) Dim startTime As DateTime = DateTime.Now Console.WriteLine("Starting V2 upload: " + DateTime.Now.ToString()) Dim JPCS As New JP_WCFService.JP_WCFClient() Using stream As New System.IO.FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read) Using uploadStreamWithProgress As New JP_StreamWithProgress(stream) AddHandler uploadStreamWithProgress.ProgressChanged, AddressOf uploadStreamWithProgress_ProgressChanged Try JPCS.UploadFile(fileInfo.Name, fileInfo.Length, uploadStreamWithProgress) Catch ex As FaultException(Of JP_WCFService.JP_WCF_Fault) Console.WriteLine("Upload Error: " & ex.Detail.Message & " (EventID: " & ex.Detail.EventID.ToString & ")") End Try End Using End Using Dim endTime As DateTime = DateTime.Now Dim durationInMS As Double = (endTime - startTime).TotalMilliseconds Console.WriteLine(vbCr & "V2 Upload Completed: " + DateTime.Now.ToString() + " (" + durationInMS.ToString() + ")") JPCS.Close()網路配置
<system.serviceModel> <bindings> <customBinding> <binding name="JP_WCFBinding"> <!-- maxReceivedMessageSize 600MB, maxBufferSize 2MB --> <binaryMessageEncoding compressionFormat="GZip" /> <httpsTransport transferMode="Streamed" maxReceivedMessageSize="629145600" maxBufferSize="2097152"/> </binding> </customBinding> </bindings> <services> <service behaviorConfiguration="JP_WCFbehavior" name="JP_WCF.JP_WCFServices"> <endpoint address="" binding="customBinding" bindingConfiguration="JP_WCFBinding" contract="JP_WCF.IJP_WCF"/> </service> </services> <behaviors> <serviceBehaviors> <behavior name="JP_WCFbehavior"> <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel>應用程序配置
<system.serviceModel> <bindings> <customBinding> <binding name="CustomBinding_IJP_WCF"> <binaryMessageEncoding compressionFormat="GZip" /> <httpsTransport transferMode="Streamed" /> </binding> </customBinding> </bindings> <client> <endpoint address="https://dev-wcf.localhost/JP_WCF.svc" binding="customBinding" bindingConfiguration="CustomBinding_IJP_WCF" contract="JP_WCFService.IJP_WCF" name="CustomBinding_IJP_WCF" /> </client> </system.serviceModel>
您的客戶有雙工通道嗎?如果是這樣,那麼在上傳文件時回調客戶端合約以發送資訊是非常直接的。
如果沒有,那麼這裡的一種好方法是使用記憶體緩衝區將數據以塊的形式流式傳輸到伺服器。我在下面舉了幾個很好的例子。
要點是您將文件拆分為客戶端上的塊並將其逐塊發送到伺服器。如果任何塊失敗,那麼它可以重試該塊,或者優雅地失敗而不發送任何更多數據。
這些引用使用一個
StreamWithProgress類來處理客戶端上的這個邏輯。希望這可以幫助。
如果您擔心此呼叫的性能,您始終可以在流式傳輸之前進行伺服器呼叫以檢查此上傳的有效性。這樣,如果出現問題,您可以完全避免流式傳輸文件,並避免應用程序中的任何異常狀態(也很昂貴)。
因此,您將相對快速地訪問伺服器以驗證諸如
- 有效的文件位置
- 寫入該位置的權限
- 有效文件的大小
- 任何相關的業務規則
然後,您可以撥打電話,而無需嘗試使用異常來管理應用程序流。記住:**例外應該是針對特殊情況的。**這樣,如果您的應用程序確實引發了異常,則意味著發生了非常異常的事情並且速度下降更可口(因為這種情況在理論上非常罕見)。