带有 json 自定义错误处理程序的 WCF 4 服务返回 202 Accepted

标签 wcf json exception-handling fault

我有一个配置为使用 json 的 WCF 4 REST 服务。我想捕获异常并返回 400 的 HTTP 状态代码,并将异常消息作为 json 对象。我已经按照网络上的示例来实现我自己的 IErrorHandler 和 IService 接口(interface)来执行此操作。

例如:

http://zamd.net/2008/07/08/error-handling-with-webhttpbinding-for-ajaxjson/

Returning Error Details from AJAX-Enabled WCF Service

http://social.msdn.microsoft.com/Forums/en/wcf/thread/fb906fa1-8ce9-412e-a16a-5d4a2a0c2ac5

然而,就像在这篇文章中一样

jQuery success callback called with empty response when WCF method throws an Exception

我收到一个没有数据的 202 Accepted 响应,这是由于在我尝试创建错误时出现序列化错误。这是从我的服务中记录的,如下所示:

2012-01-31 00:37:19,229 [8] DEBUG JsonWebScriptServiceHostFactory: creating service host
2012-01-31 00:37:19,292 [8] DEBUG JsonErrorHandler.ApplyDispatchBehavior: adding error handler
2012-01-31 00:43:06,995 [10] DEBUG ForemanSvc.GetSessionID
2012-01-31 00:43:39,292 [10] DEBUG ForemanSvc.GetProjects
2012-01-31 00:43:39,448 [10] DEBUG JsonErrorHandler.ProvideFault: creating fault
2012-01-31 00:43:39,635 [10] ERROR ForemanSvc exeption
Type: System.ServiceModel.CommunicationException
Message: Server returned an invalid SOAP Fault.  Please see InnerException for more details.
Source: System.ServiceModel
StackTrace:    
at System.ServiceModel.Channels.MessageFault.CreateFault(Message message, Int32 maxBufferSize)
at System.ServiceModel.Description.WebScriptEnablingBehavior.JsonErrorHandler.ProvideFault(Exception error, MessageVersion version, Message& fault)
at System.ServiceModel.Dispatcher.ErrorBehavior.ProvideFault(Exception e, FaultConverter faultConverter, ErrorHandlerFaultInfo& faultInfo)
at System.ServiceModel.Dispatcher.ErrorBehavior.ProvideMessageFaultCore(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage8(MessageRpc& rpc)
Type: System.Xml.XmlException
Message: Start element 'Fault' from namespace 'http://schemas.microsoft.com/ws/2005/05/envelope/none' expected. Found element 'root' from namespace ''.
Source: System.Runtime.Serialization
StackTrace:    
at System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader, String res, String arg1, String arg2, String arg3)
at System.Xml.XmlExceptionHelper.ThrowStartElementExpected(XmlDictionaryReader reader, String localName, String ns)
at System.Xml.XmlDictionaryReader.ReadStartElement(XmlDictionaryString localName, XmlDictionaryString namespaceUri)
at System.ServiceModel.Channels.ReceivedFault.CreateFault12Driver(XmlDictionaryReader reader, Int32 maxBufferSize, EnvelopeVersion version)
at System.ServiceModel.Channels.MessageFault.CreateFault(Message message, Int32 maxBufferSize)

从该帖子中不清楚如何修复它。我已经尝试了各种方法——使用属性、使用端点行为、尝试没有返回 json 格式或额外信息的简单 CreateMessage——似乎没有任何效果。谁能帮忙?

这是一些代码片段——错误处理程序

public class JsonErrorHandler : IServiceBehavior, IErrorHandler
{
  private static readonly ILog log =
    LogManager.GetLogger(System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType);


  public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
  {
    //Dont do anything
  }

  public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,
                                 Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
  {
    //dont do anything
  }

  public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
  {
    log.IfDebug("JsonErrorHandler.ApplyDispatchBehavior: adding error handler");
    foreach (ChannelDispatcherBase dispatcherBase in serviceHostBase.ChannelDispatchers)
    {
      ChannelDispatcher channelDispatcher = dispatcherBase as ChannelDispatcher; 
      if (channelDispatcher != null)
      {
        channelDispatcher.ErrorHandlers.Add(this);
      } 
    }
  }


  public bool HandleError(Exception error)
  {
    log.IfError("ForemanSvc exeption", error);
    //Tell the system that we handle all errors here.
    return true;
  }

  public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
  {
    log.IfDebug("JsonErrorHandler.ProvideFault: creating fault");

    JsonError msErrObject =
    new JsonError
    {
      Message = error.Message,
      Source = error.Source,
      Detail = error.InnerException != null ? error.InnerException.Message : null
    };


    //The fault to be returned
    fault = Message.CreateMessage(version, "", msErrObject, new DataContractJsonSerializer(msErrObject.GetType()));

    // tell WCF to use JSON encoding rather than default XML
    WebBodyFormatMessageProperty wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);

    // Add the formatter to the fault
    fault.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);

    //Modify response
    HttpResponseMessageProperty rmp = new HttpResponseMessageProperty();

    if (error is SecurityException &&
      (error.Message == "Session expired" || error.Message == "Authentication ticket expired"))
    {
      rmp.StatusCode = HttpStatusCode.Unauthorized;
      rmp.StatusDescription = "Unauthorized";
    }
    else
    {
      // return custom error code, 400.
      rmp.StatusCode = HttpStatusCode.BadRequest;
      rmp.StatusDescription = "Bad request";
    }

    //Mark the jsonerror and json content
    rmp.Headers[HttpResponseHeader.ContentType] = "application/json";
    rmp.Headers["jsonerror"] = "true";

    //Add to fault
    fault.Properties.Add(HttpResponseMessageProperty.Name, rmp);

  } 
}

以及我为服务添加自定义错误处理程序的位置

public class JsonWebScriptServiceHostFactory : WebScriptServiceHostFactory
{
  private static readonly ILog log =
   LogManager.GetLogger(System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType);

  protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
  {
    log.IfDebug("JsonWebScriptServiceHostFactory: creating service host");
    ServiceHost host = base.CreateServiceHost(serviceType, baseAddresses);
    host.Description.Behaviors.Add(new JsonErrorHandler());
    return host;
  } 
}

和自定义错误

[DataContract(Namespace = "VSS.Nighthawk.Foreman", Name = "JsonError")]
public class JsonError
{
  [DataMember]
  public string Message { get; set; }

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

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

最佳答案

您使用的是什么绑定(bind)和编码器,您对它们配置了什么设置?另外,你插入了什么行为?如果您已插入 WebScriptEnablingBehavior(因为 WebScriptServiceHostFactory 会自动将其插入),您的问题可能是 WSEB 插入了它自己的错误处理程序,它会执行很多与您尝试执行的操作相同的操作。

我还要做的是使用 Reflector 并查看嵌入在 WebScriptEnablingBehavior 中的错误处理程序,看看您的做法有何不同,以及您是否可以做您尚未做的任何其他事情。这是一个非常非常棘手且多毛的区域,充满了许多微妙之处,因此您可能没有在第一时间正确处理错误。

您可能还必须完全停止使用 WebScriptEnablingBehavior(如果您正在使用它)——所以请确保您没有停止使用它。您可能必须重新实现自己的 WebScriptEnablingBehavior,从头开始,并将其从头插入到您的服务主机工厂中,而不是只插入一个 JSON 错误处理程序。

希望这对您有所帮助!

关于带有 json 自定义错误处理程序的 WCF 4 服务返回 202 Accepted,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9123574/

相关文章:

c# - 配置 WCF 客户端终结点以使用相对地址(对于 Azure)

jquery - 存储动态表单和使用 json 输入的数据

json - 从 jq json 输出中排除列

ruby-on-rails - Rails 3.2 错误路由问题。错误 ID 与其他对象 ID 冲突

wcf - 在 WCF 中使用 IEnumerable 类型作为返回类型的问题

c# - 通过 Web 服务自动公开数据库表

mysql - Publish Web 不包括一些依赖程序集

json - 来自 Elasticsearch 过滤查询的Ajax 400错误请求

c++ - 关于双重销毁异常对象所需的解释

Java:处理子线程中的异常