基本信息
> ASP.NET 3.5与ASP.NET兼容模式下的WCF服务
>将jQuery与this service proxy用于AJAX请求
>自定义IErrorHandler和IServiceBehavior实现来捕获异常并提供Faults,将其序列化为JSON
我正在本地使用Cassini进行测试(我看到一些线程讨论了在本地调试时出现的问题,但在生产环境中工作正常)。
我遇到的问题是,当我的WCF服务抛出异常时,$ .ajax调用的成功处理程序被触发。响应为空,状态文本为“成功”,响应代码为202 / Accepted。
IErrorHandler实现确实被使用,因为我可以通过它来查看创建的FaultMessage。最后发生的是,成功回调会引发错误,因为当期望JSON字符串时,响应文本为空。错误回调从不触发。
提供一点洞察的一件事是从端点行为中删除enableWebScript选项。当我这样做时,发生了两件事情:
>回复不再包裹(即没有{d:“result”},只是“结果”)。
>错误回调被触发,但响应只是来自IIS的400 / Bad Request黄屏死机的HTML,而不是我的序列化故障。
我尝试了十大结果中显示的更多内容,或者来自Google的关于随机组合关键字“jquery ajax asp.net wcf faultcontract json”的更多内容,所以如果你计划在谷歌搜索一个答案,不要打扰。我希望有人在这个问题上遇到过这个问题。
最终我想要实现的是:
>能够在WCF方法中抛出任何类型的异常
>使用FaultContact
>捕获ShipmentServiceErrorHandler中的例外
>将序列化的ShipmentServiceFault(作为JSON)返回给客户端。
>调用错误回调,所以我可以处理项目4。
可能与:
> WCF IErrorHandler Extension not returning specified Fault
更新1
我检查了跟踪System.ServiceModel活动的输出,在调用UpdateCountry方法后的一个时刻,抛出异常,消息是
Server returned an invalid SOAP Fault.
就是这样一个内部的异常引发了串行器期望一个不同的根元素,但是我不能破译出很多其他的东西。
更新2
所以有了更多的烦恼,我有一些工作,虽然不是我认为理想的方式。这是我做的:
>删除< enableWebScript />选项从web.config的端点行为部分。
>从服务方法中删除FaultContract属性。
>实现了WebHttpBehavior的一个子类(称为ShipmentServiceWebHttpBehavior),并覆盖了AddServerErrorHandlers函数来添加ShipmentServiceErrorHandler。
>更改了ShipmentServiceErrorHandlerElement以返回一个ShipmentServiceWebHttpBehavior类型的实例,而不是错误处理程序本身。
>移动< errorHandler />从web.config的服务行为部分到端点行为部分。
这不太理想,因为现在WCF忽略了我想要的服务方法的BodyStyle = WebMessageBodyStyle.WrappedRequest(尽管我现在可以完全省略它)。我也必须更改JS服务代理中的一些代码,因为它正在寻找一个包装({d:…})对象的响应。
以下是所有相关代码(ShipmentServiceFault对象非常自我说明)。
服务
我的服务很简单(截断版本):
[ServiceContract(Namespace = "http://removed")] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class ShipmentService { [OperationContract] [WebInvoke(Method = "POST",ResponseFormat = WebMessageFormat.Json,BodyStyle = WebMessageBodyStyle.WrappedRequest)] [FaultContract(typeof(ShipmentServiceFault))] public string UpdateCountry(Country country) { var checkName = (country.Name ?? string.Empty).Trim(); if (string.IsNullOrEmpty(checkName)) throw new ShipmentServiceException("Country name cannot be empty."); // Removed: try updating country in repository (works fine) return someHtml; // new country information HTML (works fine) } }
错误处理
IErrorHandler,IServiceBehavior实现如下:
public class ShipmentServiceErrorHandlerElement : BehaviorExtensionElement { protected override object CreateBehavior() { return new ShipmentServiceErrorHandler(); } public override Type BehaviorType { get { return typeof(ShipmentServiceErrorHandler); } } } public class ShipmentServiceErrorHandler : IErrorHandler,IServiceBehavior { #region IErrorHandler Members public bool HandleError(Exception error) { // We'll handle the error,we don't need it to propagate. return true; } public void ProvideFault(Exception error,System.ServiceModel.Channels.MessageVersion version,ref System.ServiceModel.Channels.Message fault) { if (!(error is FaultException)) { ShipmentServiceFault faultDetail = new ShipmentServiceFault { Reason = error.Message,FaultType = error.GetType().Name }; fault = Message.CreateMessage(version,"",faultDetail,new DataContractJsonSerializer(faultDetail.GetType())); this.ApplyJsonSettings(ref fault); this.ApplyHttpResponseSettings(ref fault,System.Net.HttpStatusCode.InternalServerError,faultDetail.Reason); } } #endregion #region JSON Exception Handling protected virtual void ApplyJsonSettings(ref Message fault) { // Use JSON encoding var jsonFormatting = new WebBodyFormatMessageProperty(WebContentFormat.Json); fault.Properties.Add(WebBodyFormatMessageProperty.Name,jsonFormatting); } protected virtual void ApplyHttpResponseSettings(ref Message fault,System.Net.HttpStatusCode statusCode,string statusDescription) { var httpResponse = new HttpResponseMessageProperty() { StatusCode = statusCode,StatusDescription = statusDescription }; httpResponse.Headers[HttpResponseHeader.ContentType] = "application/json"; httpResponse.Headers["jsonerror"] = "true"; fault.Properties.Add(HttpResponseMessageProperty.Name,httpResponse); } #endregion #region IServiceBehavior Members public void AddBindingParameters(ServiceDescription serviceDescription,System.ServiceModel.ServiceHostBase serviceHostBase,System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints,System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { // Do nothing } public void ApplyDispatchBehavior(ServiceDescription serviceDescription,System.ServiceModel.ServiceHostBase serviceHostBase) { IErrorHandler errorHandler = new ShipmentServiceErrorHandler(); foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers) { ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher; if (channelDispatcher != null) { channelDispatcher.ErrorHandlers.Add(errorHandler); } } } public void Validate(ServiceDescription serviceDescription,System.ServiceModel.ServiceHostBase serviceHostBase) { // Do nothing } #endregion }
JavaScript
function SaveCountry() { var data = $('#uxCountryEdit :input').serializeBoundControls(); ShipmentServiceProxy.invoke('UpdateCountry',{ country: data },function(html) { $('#uxCountryGridResponse').html(html); },onPageError); }
我之前提到的服务代理需要处理很多事情,但是核心是我们来到这里:
$.ajax({ url: url,data: json,type: "POST",processData: false,contentType: "application/json",timeout: 10000,dataType: "text",// not "json" we'll parse success: function(response,textStatus,xhr) { },error: function(xhr,status) { } });
组态
我觉得这里的问题可能在这里,但是我已经尝试了每个组合的设置,我可以在网上找到一个例子。
<system.serviceModel> <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/> <behaviors> <endpointBehaviors> <behavior name="Removed.ShipmentServiceAspNetAjaxBehavior"> <webHttp /> <enableWebScript /> </behavior> </endpointBehaviors> <serviceBehaviors> <behavior name="Removed.ShipmentServiceServiceBehavior"> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="false"/> <errorHandler /> </behavior> </serviceBehaviors> </behaviors> <services> <service name="ShipmentService" behaviorConfiguration="Removed.ShipmentServiceServiceBehavior"> <endpoint address="" behaviorConfiguration="Removed.ShipmentServiceAspNetAjaxBehavior" binding="webHttpBinding" contract="ShipmentService" /> </service> </services> <extensions> <behaviorExtensions> <add name="errorHandler" type="Removed.Services.ShipmentServiceErrorHandlerElement,Removed,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"/> </behaviorExtensions> </extensions> </system.serviceModel>
笔记
我注意到这个问题正在获得一些喜爱。我找到了解决这个问题的办法,希望在找到一段时间的时候提供一个答案。敬请关注!