我正在把头发撕成一片,所以忍受我(这是一个很长的帖子)。
IErrorHandler
和IServiceBehavior
实现以捕获异常并提供序列化为JSON的错误我遇到的问题是,无论何时从我的WCF服务抛出异常,都会触发$.ajax
调用的成功处理程序。响应为空,状态文本为“Success”,响应代码为202 / Accepted。
IErrorHandler
实现确实被使用了,因为我可以单步执行它并观察创建的FaultMessage。最后发生的是success
回调引发错误,因为响应文本在期望JSON字符串时为空。 error
回调永远不会触发。
提供一点洞察力的一件事是从端点行为中删除enableWebScript
选项。当我这样做时发生了两件事:
{ d: "result" }
,只有"result"
)。error
回调被触发,但响应只是来自IIS的400 / Bad Request黄色死亡屏幕的HTML,而不是我的序列化故障。我尝试过关于关键字“jquery ajax asp.net wcf faultcontract json”的随机组合的前10个或更多结果中显示的内容,所以如果你打算用谷歌搜索答案,那就不要不好意思。我希望SO之前有人遇到过这个问题。
最终我想要达到的目标是:
Exception
FaultContact
ShipmentServiceErrorHandler
ShipmentServiceFault
(作为JSON)返回给客户端。error
回调,以便处理第4项。可能与:
有关更新1
我检查了跟踪System.ServiceModel活动的输出,并且在调用UpdateCountry方法之后的某个时刻抛出了一个异常,消息是
服务器返回了无效的SOAP错误。
就是这样。一个内部异常抱怨序列化程序期望一个不同的根元素,但我不能破译其他很多东西。
更新2
因此,随着一些更多的麻烦,我得到了一些工作,虽然不是我认为理想的方式。这是我做的:
<enableWebScript />
选项。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
}
调用WCF方法以:
开头 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>
备注
我注意到这个问题正在获得一些收藏。我确实找到了这个问题的解决方案,我希望在找到一些时间后给出答案。请继续关注!
答案 0 :(得分:3)
答案 1 :(得分:1)
我在不同的情况下有相同的症状,所以这可能会有所帮助。也可能没有帮助。
以下是我正在做的事情和解决方案的简要总结:
我发布了一个WCF服务的REST实现,我们从一个传统的ASP页面托管。我发现我必须将输入设置为流并从中读取,完成后处理流。我相信,正是在这一点上,正如你所描述的那样,我正在收到带有“成功”文本的202回复。我发现通过不处理流我得到了我期望的错误条件的响应。
以下是最终代码的摘要:
[WebHelp(Comment="Expects the following parameters in the post data:title ...etc")]
public int SaveBook(Stream stream)
{
NameValueCollection qString;
StreamReader sr = null;
string s;
try
{
/**************************************************************************************
* DO NOT CALL DISPOSE ON THE STREAMREADER OR STREAM *
* THIS WILL CAUSE THE ERROR HANDLER TO RETURN A PAGE STATUS OF 202 WITH NO CONTENT *
* IF THERE IS AN ERROR *
* ***********************************************************************************/
sr = new StreamReader(stream);
s = sr.ReadToEnd();
qString = HttpUtility.ParseQueryString(s);
string title = qString["title"];
//Do what we need
//Then Return something
int retRecieptNum = UtilitiesController.SubmitClientEntryRequest(entryReq);
return retRecieptNum;
}
catch (Exception ex)
{
throw new WebProtocolException(System.Net.HttpStatusCode.Forbidden, ex.Message, this.GetExceptionElement(true, "BookRequest", ex.Message), false, ex);
}
finally
{
}
}
希望这对你有所帮助,也许尝试使用一个流,看看它是怎么回事。
答案 2 :(得分:1)
你看过JSON.NET了吗?我正在使用它将c#中的对象转换为JSON友好字符串,然后通过电线将其传回我的客户端,在那里我将其解析为JSON对象。最后我摆脱了它并前往JSON2进行stringify。这是我使用的ajax调用:
function callScriptMethod(url, jsonObject, callback, async) {
callback = callback || function () { };
async = (async == null || async);
$.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
url: url,
data: JSON.stringify(jsonObject),
dataType: 'json',
async: async,
success: function (jsonResult) {
if ('d' in jsonResult)
callback(jsonResult.d);
else
callback(jsonResult);
},
error: function () {
alert("Error calling '" + url + "' " + JSON.stringify(jsonObject));
callback([]);
}
});
}
答案 3 :(得分:0)
这是另一个镜头。我将离开原来的尝试,因为该解决方案可以帮助其他人。
要触发$ .ajax调用的错误条件,您需要在响应中输入错误代码
protected virtual void ApplyHttpResponseSettings(ref Message fault, System.Net.HttpStatusCode statusCode, string statusDescription)
{
var httpResponse = new HttpResponseMessageProperty()
{
//I Think this could be your problem, if this is not an error code
//The error condition will not fire
//StatusCode = statusCode,
//StatusDescription = statusDescription
//Try forcing an error code
StatusCode = System.Net.HttpStatusCode.InternalServerError;
};
httpResponse.Headers[HttpResponseHeader.ContentType] = "application/json";
httpResponse.Headers["jsonerror"] = "true";
fault.Properties.Add(HttpResponseMessageProperty.Name, httpResponse);
}
希望我的第二个诱惑对你更有用!
答案 4 :(得分:0)
我在WCF中遇到了类似的问题,并且在我的解决方案中集成了MVC和WCF,因此使用了ASP.NET兼容性。我要做的是抛出WebFaultException然后检查接收端(java或其他.NET客户端)的响应状态。如果WebOperationContext.Current不为null,则您的自定义错误可能会抛出该错误。你可能已经意识到了这一点,但我想我会把它扔出去。
throw new WebFaultException(HttpStatusCode.BadRequest);