C# WCF RESTful 服务中的访问请求正文

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1287802/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 14:42:36  来源:igfitidea点击:

Access Request Body in a WCF RESTful Service

c#wcfhttprest

提问by urini

How do I access the HTTP POST request body in a WCF REST service?

如何访问 WCF REST 服务中的 HTTP POST 请求正文?

Here is the service definition:

这是服务定义:

[ServiceContract]
public interface ITestService
{
    [OperationContract]
    [WebInvoke(Method = "POST", UriTemplate = "EntryPoint")]
    MyData GetData();
}

Here is the implementation:

这是实现:

public MyData GetData()
{
    return new MyData();
}

I though of using the following code to access the HTTP request:

我虽然使用以下代码来访问 HTTP 请求:

IncomingWebRequestContext context = WebOperationContext.Current.IncomingRequest;

But the IncomingWebRequestContext only gives access to the headers, not the body.

但是 IncomingWebRequestContext 只允许访问标题,而不是正文。

Thanks.

谢谢。

回答by Darrel Miller

My apologies for the previous answer, I stupidly assumed that I had just cast WebOperationContext to get at the OperationContext, unfortunately the real answer is much more ugly.

我为之前的答案道歉,我愚蠢地认为我刚刚投射了 WebOperationContext 来获得 OperationContext,不幸的是,真正的答案要丑得多。

Let me preface this with, there must be a better way!

让我先介绍一下,一定有更好的方法!

First I created my own context object, that could be attached to the existing OperationContext object.

首先,我创建了自己的上下文对象,该对象可以附加到现有的 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;
                }
            }
        }
}

In order to be able to access this new context object, you need to add it as an extension to the current one. I did that by creating a message inspector class.

为了能够访问这个新的上下文对象,您需要将它添加为当前对象的扩展。我通过创建一个消息检查器类来做到这一点。

public class TMMessageInspector : IDispatchMessageInspector {

        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) {

            OperationContext.Current.Extensions.Add(new TMRequestContext());
            return null;
        }
}

In order for the message inspector to work you need to create a new "behaviour". I did this using the following code.

为了让消息检查器工作,您需要创建一个新的“行为”。我使用以下代码完成了此操作。

    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());
                }
            }

        }
}

The behaviour you should be able to add in the config file, although I did it by creating a new host and adding the behaviour object manually in the OnOpening method. I ended up using these class for much more than just accessing the OperationContext object. I used them for logging and overriding the error handling and access to the http request object,etc. So, it is not quite as ridiculous solution as it seems. Almost, but not quite!

您应该能够在配置文件中添加行为,尽管我是通过创建一个新主机并在 OnOpening 方法中手动添加行为对象来实现的。我最终使用这些类不仅仅是访问 OperationContext 对象。我使用它们来记录和覆盖错误处理以及对 http 请求对象的访问等。因此,它并不像看起来那么荒谬。几乎,但不完全!

I really don't remember why I could not just access OperationContext.Current directly. I have a faint recollection that it was always empty and this nasty process was the only way I could get an instance that actually contained valid data.

我真的不记得为什么我不能直接访问 OperationContext.Current。我隐约记得它总是空的,这个讨厌的过程是我获得一个实际包含有效数据的实例的唯一方法。

回答by kasthor

Best way i think doesn't involve WebOperationContext

我认为不涉及 WebOperationContext 的最佳方式

[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "EntryPoint", BodyStyle = WebMessageBodyStyle.Bare)]
MyData GetData(System.IO.Stream pStream);

回答by Haw-Bin

It seems that because WCF is designed to be transport protocol-agnostic, a service method doesn't provide access to HTTP-specific information by default. However, I just came across a nice article describing "ASP.Net Compatibility Mode" which essentially allows you to specify that your service is indeed intended to be exposed via HTTP.

似乎因为 WCF 被设计为与传输协议无关,所以默认情况下服务方法不提供对 HTTP 特定信息的访问。但是,我刚刚看到一篇描述“ASP.Net 兼容模式”的好文章,它基本上允许您指定您的服务确实打算通过 HTTP 公开。

http://blogs.msdn.com/b/wenlong/archive/2006/01/23/516041.aspx

http://blogs.msdn.com/b/wenlong/archive/2006/01/23/516041.aspx

Adding the aspNetCompatibilityEnabledconfiguration to Web.config, combined with the AspNetCompatibilityRequirementsattribute to the desired service operations, should do the trick. I'm about to try this myself.

aspNetCompatibilityEnabled配置添加到Web.config,并结合AspNetCompatibilityRequirements所需服务操作的属性,应该可以解决问题。我要自己试试这个。

Haw-Bin

郝斌

回答by sajith

Use

OperationContext.Current.RequestContext.RequestMessage

OperationContext.Current.RequestContext.RequestMessage

回答by amackay11

The above answers helped me come up with this solution. I am receiving json with name/value pairs. {"p1":7514,"p2":3412, "p3":"joe smith" ... }

上面的答案帮助我想出了这个解决方案。我正在接收带有名称/值对的 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

回答by Gerard Sexton

Sorry for the late answer but I thought I would add what works with UriTemplate parameters to get the request body.

抱歉回答晚了,但我想我会添加与 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 ...;
    }
}

bodyis assigned a string from the raw bytes of the message body.

body从消息正文的原始字节分配一个字符串。

回答by GuruKay

Here is what I did:

这是我所做的:

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));
        }
    }
}

This code simply reads the input and writes it out. the body of the POST request is automatically assigned to input irrespect of the variable name. As you can see, you can still have variables in your your UriTemplate.

此代码只是读取输入并将其写出。无论变量名称如何,POST 请求的主体都会自动分配给输入。如您所见,您的 UriTemplate 中仍然可以有变量。