【问题标题】:WCF + REST: Where is the request data?WCF + REST:请求数据在哪里?
【发布时间】:2010-12-23 10:41:42
【问题描述】:

我目前正在开发 WCF RESTful 服务。在验证 POST 数据时,如果请求 XML 不符合我们的业务规则,我将抛出异常。

目标是在收到无效请求时向相应的员工发送电子邮件。但是,连同传入的请求标头、方法和 URI,我还想发送已发布的 XML。

我无法找到访问这些数据的方法。 WCF 是否真的在我有机会访问请求正文/数据之前就破坏了它,还是我遗漏了什么?

感谢您的帮助,因为我对为什么无法访问请求数据感到困惑。

【问题讨论】:

    标签: wcf rest request


    【解决方案1】:

    不幸的是,这不受支持 - 我们有类似的需求,并通过反射调用内部成员来实现。我们只是在错误处理程序中使用它(因此我们可以转储原始请求),但它工作正常。我不建议将它用于您不拥有和操作的系统(例如,不要将此代码发送给客户),因为它可以随时通过服务包或其他方式进行更改。

    public static string GetRequestBody()
    {
        OperationContext oc = OperationContext.Current;
    
        if (oc == null)
            throw new Exception("No ambient OperationContext.");
    
        MessageEncoder encoder = oc.IncomingMessageProperties.Encoder;
        string contentType = encoder.ContentType;
        Match match = re.Match(contentType);
    
        if (!match.Success)
            throw new Exception("Failed to extract character set from request content type: " + contentType);
    
        string characterSet = match.Groups[1].Value;
    
        object bufferedMessage = operationContextType.InvokeMember("request",
            BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField,
            null, oc, null);
    
        //TypeUtility.AssertType(bufferedMessageType, bufferedMessage);
    
        object messageData = bufferedMessageType.InvokeMember("MessageData",
            BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetProperty,
            null, bufferedMessage, null);
    
        //TypeUtility.AssertType(jsonBufferedMessageDataType, messageData);
    
        object buffer = jsonBufferedMessageDataType.InvokeMember("Buffer",
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty,
            null, messageData, null);
    
        ArraySegment<byte> arrayBuffer = (ArraySegment<byte>)buffer;
    
        Encoding encoding = Encoding.GetEncoding(characterSet);
    
        string requestMessage = encoding.GetString(arrayBuffer.Array, arrayBuffer.Offset, arrayBuffer.Count);
    
        return requestMessage;
    }
    

    【讨论】:

    • 天哪!这比我的解决方案还要糟糕:-)
    • 同意——这样做的好处是我们不必在每次请求时使用消息检查器从传输层偷运第二份副本。这样我们就可以直接从服务代码中获取原始缓冲区,并且只有在出现问题时才可以。因此,我最初的谨慎。 :) 我希望他们只是将其从 WebOperationContext 中公开,但将其拆开后,我明白为什么他们不这样做(尤其是当您考虑任意大小的流式请求时)。
    • 感谢您的回复。我现在明白你为什么采用这种方法了。有趣的是,为了理解 WCF 的工作方式,您必须深入研究实现。这有点违背了试图抽象出复杂性的目的!
    • Nitzmahone,感谢您提供此示例。访问请求正文是我在处理错误时只想做的事情。感谢您的回答!!!
    • 同意他们需要添加一个 .GetBodyAsString() 方法什么的。我所需要的只是原始 JSON 数据,而我所能得到的只是 JSON 的 XML 表示——我使用的 JSON 库需要原始 JSON,而不是他们认为数据应该是什么样的!
    【解决方案2】:

    所以,如果你声明你的合同是这样的:

    [WebInvoke(Method = "POST", UriTemplate = "create", ResponseFormat=WebMessageFormat.Json)]
     int CreateItem(Stream streamOfData);
    

    (您可以使用 XML 代替) streamOfData 应该是 HTTP POST 的主体。您可以使用以下方式对其进行反序列化:

     StreamReader reader = new StreamReader(streamId);
     String res = reader.ReadToEnd();
     NameValueCollection coll = HttpUtility.ParseQueryString(res);
    

    至少对我们来说是这样的。您可能希望使用不同的方法将字符串放入 XMLDocument 或其他内容中。这适用于我们的 JSON 帖子。可能不是最优雅的解决方案,但它确实有效。

    我希望这会有所帮助。

    格伦

    【讨论】:

    • 格伦,感谢您的回复。我目前有一个操作合同,可以立即将发布的 xml 反序列化为一个对象。即使我可以访问新对象进行处理,我仍然希望原始请求可用。只是正文的简单字符串表示。谢谢!
    【解决方案3】:

    试试这个,

    OperationContext.Current.RequestContext.RequestMessage
    

    【讨论】:

      【解决方案4】:

      以下是您无需反思的方式:

      using (var reader = OperationContext.Current.RequestContext.RequestMessage.GetReaderAtBodyContents ()) {
          if (reader.Read ())
              return new string (Encoding.ASCII.GetChars (reader.ReadContentAsBase64 ()));
                      return result;
          }
      }
      

      如果读者是HttpStreamXmlDictionaryReader(在我的例子中),类对方法ReadContentAsBase64(byte[] buffer, int index, int count) 的实现只需将这些参数传递给Stream.Read 方法。

      拥有byte[] 后,我通过ASCII 编码将字节转换为字符串。为了正确实现,您可以使用消息标头中的内容类型和编码来执行 HTTP 规范。

      【讨论】:

      • 如果 Message.State 已设置为 Read,您的解决方案将不起作用 - 您会收到 InvalidOperationException “此消息无法支持该操作,因为它已被读取。”
      • 以下是您的处理方法:stackoverflow.com/questions/2184806/…
      【解决方案5】:

      您可以在 WCF 服务的自定义 HttpModule 中捕获 HttpApplication.Request.InputStream,读取流并在自定义 HttpModule 的事件处理程序中再次将其位置设置为 0。然后将其存储在会话中,并在实际的OperationContract 中进一步访问。

      例如:

      public class CustomModule : IHttpModule
      {
          public void Dispose()
          {
      
          }
      
          public void Init(HttpApplication context)
          {
              context.AcquireRequestState +=context_AcquireRequestState;
          }
      
          void context_AcquireRequestState(object sender, EventArgs e)
          {
              HttpApplication application = sender as HttpApplication;
              Stream str = application.Request.InputStream;
              StreamReader sr = new StreamReader(str);
              string req = sr.ReadToEnd();
              str.Position = 0;
              application.Session["CurrentRequest"] = req;
          }
       }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-02-06
        • 2013-01-11
        • 1970-01-01
        • 2012-06-06
        • 2011-04-06
        • 1970-01-01
        • 2012-03-29
        相关资源
        最近更新 更多