使用 IParameterInspector 进行 WCF 参数验证并在客户端处理 FaultException

WCF parameter validation using IParameterInspector and handle FaultException at client

我有一个 WCF Rest 服务,它在调用实际服务方法之前使用 IParameterInspector 进行输入参数验证。现在这些休息服务被 iPhone 消费了。如果参数无效,那么我会抛出我想在 iPhone(或可能是 Android)端处理的故障异常。 好吧,我在 Whosebug 中引用了很多 link,我在代码中使用了下面的 link 作为参考。

WCF Parameter Validation with Interceptor
以下是我的分步代码片段。

=> FaultExceptionResponse class 用于 FaultException<T>

[DataContract]
public class FaultExceptionResponse
{
        [DataMember]
        public bool Success { get; set; }

        [DataMember]
        public string ResponseString { get; set; }
}

=> 下面 class 验证参数。

public class ValidationParameterInspectorAttribute : Attribute, IParameterInspector, IOperationBehavior
    {

        public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
        {
        }

        public object BeforeCall(string operationName, object[] inputs)
        {
            if (operationName == "GetCommunicationDetailById")
            {
                var communicationChatViewModel = inputs.FirstOrDefault() as CommunicationChatViewModel;

                if (communicationChatViewModel != null &&
                    (communicationChatViewModel.iConsultCommunicationID <= 0))
                {
                    //ErrorLogger.LogErrorMessageToElmah(new ArgumentException("API Name: GetCommunicationDetailById   Parameter cannot be less than zero.", "iConsultCommunicationID"));
                    var fc = new FaultExceptionResponse { Success = false, ResponseString = "Invalid parameter found while fetching communication detail !" };
                    throw new FaultException<FaultExceptionResponse>(fc, new FaultReason(fc.ResponseString));
                }
            }
            return null;
        }

        public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
        {
        }

        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        {
            dispatchOperation.ParameterInspectors.Add(this);
        }

        public void Validate(OperationDescription operationDescription)
        {
        }
    }

=> 然后我就这样装饰我的 API

[OperationContract]
[ValidationParameterInspector]
[FaultContract(typeof(FaultExceptionResponse))]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "/GetCommunicationDetailById")]
 CommunicationChatDetailList GetCommunicationDetailById(CommunicationChatViewModel communicationParaViewModel);

一切正常,但是当参数无效时,在 iPhone 端抛出此 faultException,它仅显示以下错误信息。

Error: {
    AFNetworkingOperationFailingURLResponseErrorKey = "<NSHTTPURLResponse: 0x7faa605faa90> { URL: http://192.168.151.40/MyWCF/Service1.svc/GetCommunicationDetailById } { status code: 400, headers {\n    \"Cache-Control\" = private;\n    \"Content-Length\" = 3319;\n    \"Content-Type\" = \"text/html\";\n    Date = \"Fri, 25 Sep 2015 15:45:14 GMT\";\n    Server = \"Microsoft-IIS/7.5\";\n    \"X-AspNet-Version\" = \"4.0.30319\";\n    \"X-Powered-By\" = \"ASP.NET\";\n} }";
    NSErrorFailingURLKey = "http://192.168.151.40/MyWCF/Service1.svc/GetCommunicationDetailById";
    NSLocalizedDescription = "Request failed: bad request (400)";
    NSUnderlyingError = "Error Domain=AFNetworkingErrorDomain Code=-1016 \"Request failed: unacceptable content-type: text/html\" UserInfo={AFNetworkingOperationFailingURLResponseErrorKey=<NSHTTPURLResponse: 0x7faa605faa90> { URL: http://192.168.151.40/LKPracooWCF/Service1.svc/GetCommunicationDetailById } { status code: 400, headers {\n    \"Cache-Control\" = private;\n    \"Content-Length\" = 3319;\n    \"Content-Type\" = \"text/html\";\n    Date = \"Fri, 25 Sep 2015 15:45:14 GMT\";\n    Server = \"Microsoft-IIS/7.5\";\n    \"X-AspNet-Version\" = \"4.0.30319\";\n    \"X-Powered-By\" = \"ASP.NET\";\n} }, NSLocalizedDescription=Request failed: unacceptable content-type: text/html, NSErrorFailingURLKey=http://192.168.151.40/LKPracooWCF/Service1.svc/GetCommunicationDetailById
};

我没有找到我的自定义错误信息!!!!现在,如果我在 Advanced Rest Client Application 中测试相同的测试用例,那么我会收到如下所示的自定义错误消息。

Status - 400 Bad Request
<p class="heading1">Request Error</p>
<p>The server encountered an error processing the request. The exception message is 'Invalid parameter found while fetching communication detail !'. See server logs for more details. The exception stack trace is: </p>
<p>......</p>

所以我想要的是如何在客户端 (iPhone) 端处理这个 faultException FaultException<FaultExceptionResponse>???。

问题是您的服务应该 return Json 但异常导致响应内容类型为 text/html。您可以放弃使用 FaultException 并切换到 WebFaultException 以及显式设置响应的内容类型。例如:

WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";
var exception = new WebFaultException<string>(
    "{ \"Success\" = \"false\", " + 
    "\"ResponseString\" = \"Invalid parameter found while fetching communication detail !\" }",
    HttpStatusCode.BadRequest);
throw exception;

只需扩展 MattC 给出的答案即可。非常感谢马特。我正在 post 将此详细代码作为答案,只是为了将来 reader 谁会在 SO 中使用此 link。还有一点,我的博客里也写了博客post-krishnrajrana.wordpress.com

伙计们,你们是如何处理客户端异常的(比如 iPhone 设备或​​ android 设备)

public object BeforeCall(string operationName, object[] inputs)
{
    if (operationName == "GetCommunicationDetailById")
    {
        var model = inputs.FirstOrDefault() as TestViewModel;

        if (model != null &&
            (model.iConsultCommunicationID <= 0))
        {
            WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";
            var wfc = new WebFaultException<Response>(new Response(false, "Invalid parameter found while fetching detail !"), System.Net.HttpStatusCode.OK);
            throw wfc;
        }
    }
    else if (operationName == "AnotherMethod")
    {
        ............
    }

    // OR you can use switch case
    switch(operationName)
    {
        Case "Method":
            // your logic
    }

    return null;
}