如何在WCF REST服务中访问HTTP POST请求正文?
这是服务定义:
[ServiceContract]
public interface ITestService
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "EntryPoint")]
MyData GetData();
}
以下是实施:
public MyData GetData()
{
return new MyData();
}
我使用以下代码来访问HTTP请求:
IncomingWebRequestContext context = WebOperationContext.Current.IncomingRequest;
但IncomingWebRequestContext只允许访问标题,而不是正文。
感谢。
答案 0 :(得分:10)
我认为最佳方式不涉及WebOperationContext
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "EntryPoint", BodyStyle = WebMessageBodyStyle.Bare)]
MyData GetData(System.IO.Stream pStream);
答案 1 :(得分:10)
使用
OperationContext.Current.RequestContext.RequestMessage
答案 2 :(得分:6)
对于迟到的回答感到抱歉,但我想我会添加一些适用于UriTemplate参数的内容来获取请求正文。
[ServiceContract]
public class Service
{
[OperationContract]
[WebInvoke(UriTemplate = "{param0}/{param1}", Method = "POST")]
public Stream TestPost(string param0, string param1)
{
string body = Encoding.UTF8.GetString(OperationContext.Current.RequestContext.RequestMessage.GetBody<byte[]>());
return ...;
}
}
body
从消息正文的原始字节中分配一个字符串。
答案 3 :(得分:1)
似乎因为WCF被设计为与传输协议无关,所以默认情况下,服务方法不提供对特定于HTTP的信息的访问。但是,我刚刚看到一篇很好的文章描述了“ASP.Net兼容模式”,它基本上允许您指定您的服务确实是通过HTTP公开的。
http://blogs.msdn.com/b/wenlong/archive/2006/01/23/516041.aspx
将aspNetCompatibilityEnabled
配置添加到Web.config
,并将AspNetCompatibilityRequirements
属性与所需的服务操作相结合,应该可以解决问题。我将要自己尝试一下。
唧唧滨
答案 4 :(得分:1)
以上答案帮助我提出了这个解决方案。我收到名字/价值对的json。 {“p1”:7514,“p2”:3412,“p3”:“乔史密斯”......}
[OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Bare,
RequestFormat = WebMessageFormat.Json
)]
public Stream getJsonRequest()
{
// Get the raw json POST content. .Net has this in XML string..
string JSONstring = OperationContext.Current.RequestContext.RequestMessage.ToString();
// Parse the XML string into a XML document
XmlDocument doc = new XmlDocument();
doc.LoadXml(JSONstring);
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
{
node.Name // has key
node.InnerText; // has value
答案 5 :(得分:0)
我为前一个答案道歉,我愚蠢地认为我刚刚使用WebOperationContext来获取OperationContext,不幸的是真正的答案更难看。
让我先说一下,必须有更好的方法!
首先,我创建了自己的上下文对象,可以附加到现有的OperationContext对象。
public class TMRequestContext : IExtension<OperationContext> {
private OperationContext _Owner;
public void Attach(OperationContext owner) {
_Owner = owner;
}
public void Detach(OperationContext owner) {
_Owner = null;
}
public static TMRequestContext Current {
get {
if (OperationContext.Current != null) {
return OperationContext.Current.Extensions.Find<TMRequestContext>();
} else {
return null;
}
}
}
}
为了能够访问这个新的上下文对象,您需要将其添加为当前对象的扩展。我是通过创建一个消息检查器类来完成的。
public class TMMessageInspector : IDispatchMessageInspector {
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) {
OperationContext.Current.Extensions.Add(new TMRequestContext());
return null;
}
}
为了使消息检查器工作,您需要创建一个新的“行为”。我使用以下代码完成了此操作。
public class TMServerBehavior : IServiceBehavior {
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) {
foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers) {
foreach (EndpointDispatcher epDisp in chDisp.Endpoints) {
epDisp.DispatchRuntime.MessageInspectors.Add(new TMMessageInspector());
}
}
}
}
您应该能够在配置文件中添加的行为,尽管我是通过创建新主机并在OnOpening方法中手动添加行为对象来实现的。我最终使用这些类不仅仅是访问OperationContext对象。我使用它们来记录和覆盖错误处理和访问http请求对象等。所以,它似乎并不像看起来那么荒谬。几乎,但不完全!
我真的不记得为什么我不能直接访问OperationContext.Current。我有一个微弱的回忆,它总是空的,这个令人讨厌的过程是我得到一个实际包含有效数据的实例的唯一方法。
答案 6 :(得分:0)
这是我所做的:
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;
using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;
namespace YourSpaceName
{
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class YourClassName
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "YourMethodName({id})", BodyStyle = WebMessageBodyStyle.Bare)]
public Stream YourMethodName(Stream input, string id)
{
WebOperationContext ctx = WebOperationContext.Current;
ctx.OutgoingResponse.Headers.Add("Content-Type", "application/json");
string response = $@"{{""status"": ""failure"", ""message"": ""Please specify the Id of the vehicle requisition to retrieve."", ""d"":null}}";
try
{
string response = (new StreamReader(input)).ReadToEnd();
}
catch (Exception ecp)
{
response = $@"{{""status"": ""failure"", ""message"": ""{ecp.Message}"", ""d"":null}}";
}
return new MemoryStream(Encoding.UTF8.GetBytes(response));
}
}
}
此代码只是读取输入并将其写入。 POST请求的主体会自动分配给变量名输入。如您所见,您的UriTemplate中仍然可以包含变量。
答案 7 :(得分:0)
此代码返回正文。需要使用System
、System.Text
、System.Reflection
、System.ServiceModel
public string GetBody()
{
var requestMessage = OperationContext.Current.RequestContext.RequestMessage;
var messageDataProperty = requestMessage.GetType().GetProperty("MessageData", (BindingFlags)0x1FFFFFF);
var messageData = messageDataProperty.GetValue(requestMessage);
var bufferProperty = messageData.GetType().GetProperty("Buffer");
var buffer = bufferProperty.GetValue(messageData) as ArraySegment<byte>?;
var body = Encoding.UTF8.GetString(buffer.Value.Array);
return body;
}
答案 8 :(得分:0)
我能够在此线程上拼凑多个答案来解决我的问题。我想要做的是在 POST 正文中接收一个 JSON 有效负载,而不对其进行任何处理,以便我可以随意解析它。这对我们很重要,因为传入的 JSON 不是一个预先确定的事物,而是几种可能的事物之一。是的,我们可以为每个新事物添加一个单独的调用,但我们正在努力使系统无需更改代码即可扩展。
在之前的尝试中,如果内容类型是“文本/纯文本”,我只能让它工作,但后来我坐在舌头上解释为什么它不能作为“应用程序/json”发送'当有人想打电话时。
所以...从这个页面上的答案...以下签名:
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "test/", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
void TestCall();
然后像这样从正文中获取 JSON:
private string GetJSONFromBody()
{
string json = "";
string contentType = WebOperationContext.Current.IncomingRequest.ContentType;
if (contentType.Contains("application/json"))
{
var requestMessage = OperationContext.Current.RequestContext.RequestMessage;
var messageDataProperty = requestMessage.GetType().GetProperty("MessageData", (BindingFlags)0x1FFFFFF);
var messageData = messageDataProperty.GetValue(requestMessage);
var bufferProperty = messageData.GetType().GetProperty("Buffer");
var buffer = bufferProperty.GetValue(messageData) as ArraySegment<byte>?;
json = Encoding.UTF8.GetString(buffer.Value.Array);
}
else if (contentType.Contains("text"))
{
json = Encoding.UTF8.GetString(OperationContext.Current.RequestContext.RequestMessage.GetBody<byte[]>());
}
return json;
}
但是有人尝试通过这种方式发送 JSON 它将起作用,但最后我能够支持“application/json”。我仍然需要支持“text/plain”,因为已经有应用程序以这种方式调用。