【问题标题】:How to get the raw request body in an asp.NET 3.5 WCF Web Service如何在 asp.NET 3.5 WCF Web 服务中获取原始请求正文
【发布时间】:2016-01-27 08:33:55
【问题描述】:

我被要求开发一个 C# rest api,它需要记录(在数据库表中)对任何已定义路由的每个请求。每条日志都需要记录请求体、url、响应体和状态(Pending、Success or Error)

经过大量互联网研究,我找到了下面的示例,它最接近我的需要,但是它给了我XML格式的数据,我需要原始格式,即Json。

var payload = System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.ToString()

更新 - 解决方案

vendettamit交谈后,我得到了以下解决方案,我认为值得在这里分享:

这是我的服务:

using System;
using System.ServiceModel;
using System.ServiceModel.Web;
using AdvLinkForWebService.BusinessRules;
using AdvLinkForWebService.JsonModel;

namespace AdvLinkForWebService
{

    [ServiceContract]
    public interface IService{

        [OperationContract]
        [WebInvoke(Method = "POST",
                   RequestFormat = WebMessageFormat.Json,
                   ResponseFormat = WebMessageFormat.Json,
                   BodyStyle = WebMessageBodyStyle.Bare,
                   UriTemplate = "gaterequest/{param}")]
        ReturnMessage PostgateRequest(JsonData data, string param);

    }

    public class Service : IService
    {
        // Any new Rout will follow this template:
        public ReturnMessage PostgateRequest(JsonData data, string param)
        {

            // This is the return value
            var ret = new ReturnMessage();

            try {

                // Business Rules resides inside gateBusinessRules
                var businessRuleHandler = new gateBusinessRules();
                businessRuleHandler.DoPost(data, param);

                ret.type = true;
                ret.message = "OK";

                // Log success, if nothing wrong had happened
                Utils.logSuccess();

            } catch (Exception e) {
                // Log exception, if something wrong had happened
                ret.type = false;
                ret.message = "NOK: " + e.Message;
                Utils.logException(e.ToString());
            }
            return ret;
        }


    }
}

这是我的 Utils 类,它封装了日志操作:

using System;
using System.Data.SqlClient;
using System.Data;
using System.ServiceModel.Web;

namespace AdvLinkForWebService
{

    public class Utils
    {

        public static string DB_CONNECTION_STRING = "Data Source=XXX.XXX.XXX.XXX;User Id=XXX;Password=XXX";

        public static int logOperation(string type, string payload){

            var url = System.ServiceModel.Web.WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri.OriginalString;
            var method = System.ServiceModel.Web.WebOperationContext.Current.IncomingRequest.Method;
            var userAgent = System.ServiceModel.Web.WebOperationContext.Current.IncomingRequest.UserAgent;

            int key = 0;

            // Do stuff to insert url, method, user agent and request payload in the database
            // the generated key from the insertion will be returned as the key variable

            return key;

        }

        public static void logResponse(int resCode, string resPayload)
        {

            int logId =  (int) System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.Properties["logID"];

            // Do stuff to update the log record in the database based on the ID
            // This method updates response code and response payload
        }

        public static void logSuccess()
        {

            int logId =  (int) System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.Properties["logID"];

            // Do stuff to update the log record in the database based on the ID
            // This method just updates log status to success
        }

        public static void logException(string error)
        {
            WebOperationContext ctx = WebOperationContext.Current;
            ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.BadRequest;

            int logId =  (int) System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.Properties["logID"];

            // Do stuff to update the log record in the database based on the ID
            // This method just updates log status to error and log the error message
        }


        public Utils()
        {
        }
    }
}

这是负责从请求和响应中记录原始 Json 的类:

using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text;
using System.Xml;

namespace AdvLinkForWebService.MessageInspector
{
    public class IncomingMessageLogger : IDispatchMessageInspector
    {

        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            // Set up the message and stuff
            Uri requestUri = request.Headers.To;
            HttpRequestMessageProperty httpReq = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
            MemoryStream ms = new MemoryStream();
            XmlDictionaryWriter writer = JsonReaderWriterFactory.CreateJsonWriter(ms);
            request.WriteMessage(writer);
            writer.Flush();

            // Log the message in the Database
            string messageBody = Encoding.UTF8.GetString(ms.ToArray());
            var logID = Utils.logOperation("I", messageBody);

            // Reinitialize readers and stuff
            ms.Position = 0;
            XmlDictionaryReader reader = JsonReaderWriterFactory.CreateJsonReader(ms, XmlDictionaryReaderQuotas.Max);
            Message newMessage = Message.CreateMessage(reader, int.MaxValue, request.Version);

            // Put the ID generated at insertion time in a property
            // in order to use it over again to update the log record
            // with the response payload and, OK or error status
            request.Properties.Add("logID", logID);
            newMessage.Properties.CopyProperties(request.Properties);

            request = newMessage;
            return requestUri;
        }

        public void BeforeSendReply(ref Message reply, object correlationState)
        {           
            MemoryStream ms = new MemoryStream();
            XmlDictionaryWriter writer = JsonReaderWriterFactory.CreateJsonWriter(ms);
            reply.WriteMessage(writer);
            writer.Flush();

            // Log the response in the Database         
            HttpResponseMessageProperty prop = (HttpResponseMessageProperty) reply.Properties["httpResponse"];
            int statusCode = (int) prop.StatusCode;
            string messageBody = Encoding.UTF8.GetString(ms.ToArray());
            Utils.logResponse(statusCode, messageBody);

            // Reinitialize readers and stuff
            ms.Position = 0;
            XmlDictionaryReader reader = JsonReaderWriterFactory.CreateJsonReader(ms, XmlDictionaryReaderQuotas.Max);
            Message newMessage = Message.CreateMessage(reader, int.MaxValue, reply.Version);
            newMessage.Properties.CopyProperties(reply.Properties);
            reply = newMessage;

        }

    }

    public class InsepctMessageBehavior : IEndpointBehavior
    {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new IncomingMessageLogger());
        }

        public void Validate(ServiceEndpoint endpoint)
        {
        }
    }

    public class InspectMessageBehaviorExtension : BehaviorExtensionElement
    {
        public override Type BehaviorType
        {
            get { return typeof(InsepctMessageBehavior); }
        }

        protected override object CreateBehavior()
        {
            return new InsepctMessageBehavior();
        }
    }

}

最后,这是使一切正常运行所必需的 xml 配置:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <services>
            <service name="AdvLinkForWebService.Service">
                <endpoint address=""
                          binding="webHttpBinding"
                          contract="AdvLinkForWebService.IService"
                          behaviorConfiguration="defaultWebHttpBehavior"/>
            </service>
        </services>
        <behaviors>
            <endpointBehaviors>             
                <behavior name="defaultWebHttpBehavior">
                    <inspectMessageBehavior/>
                    <webHttp defaultOutgoingResponseFormat="Json"/>
                </behavior>
            </endpointBehaviors>
        </behaviors>
        <extensions>
            <behaviorExtensions>
                <add name="inspectMessageBehavior"
                     type="AdvLinkForWebService.MessageInspector.InspectMessageBehaviorExtension, AdvLinkForWebService"/>
            </behaviorExtensions>
        </extensions>
    </system.serviceModel>
</configuration>

【问题讨论】:

  • 查看我的 answer here 以在 WCF Rest API 中捕获原始数据。
  • vendettamit,我已经尝试过了,但是我的 AfterReceiveRequest 方法没有被调用。也许我错过了一个 xml 配置(该应用程序将部署在 IIS 上)。我要编辑我的问题,包括 web.config 和 service.svc,你能检查一下我错过了什么吗?
  • 也发布您的配置。让我们看看遗漏了什么。
  • 已发布,如您所见,它非常简约,因为它不是 VisualStudio 项目,而是 SharpDevelop 项目。
  • 我没有看到 MessageInspector 行为被注入到配置的任何地方。这就是您的 AfterReceiveRequest 没有被调用的原因。

标签: asp.net wcf


【解决方案1】:

您将方法设置为接收 (Invoke) json 消息,也可以将其设置为返回 json 作为响应,将 WebGet 添加到您的操作中:

[OperationContract]
        [WebInvoke(Method = "POST",
                   RequestFormat = WebMessageFormat.Json,
                   ResponseFormat = WebMessageFormat.Json,
                   BodyStyle = WebMessageBodyStyle.Bare,
                   UriTemplate = "gaterequest/{param}")]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
        ReturnMessage PostgateRequest(JsonData data, string param);

希望对你有帮助

【讨论】:

    【解决方案2】:

    您需要实现自定义 IDispatchMessageInspector 以在 AfterReceiveRequest 方法中捕获原始请求,请参阅我的 answer here

    更新(最新评论):

    针对您最近的评论,您可以修改消息内容以添加其他信息,例如您的 ID;如果您查看 MessageString 方法中的示例代码,它会根据接收到的 WebContent 的类型创建一个新的消息编写器。如果是 Json,那么将使用 JsonReader。只需在消息正文字符串中添加您的信息,如下所示:

     string messageBody = Encoding.UTF8.GetString(ms.ToArray());
     messageBody = messageBody.Insert(<correct index position>, "<your new ID>");
    
     ms.Position = 0;
     XmlDictionaryReader reader = JsonReaderWriterFactory.CreateJsonReader(new StringReader(messageBody), XmlDictionaryReaderQuotas.Max);
    
     Message newMessage = Message.CreateMessage(reader, int.MaxValue, message.Version);
    

    注意:此策略需要在您的 JsonData 类中添加额外的“ID”。这样该值就会被反序列化。但这不是实现它的唯一方法。可能当您问另一个问题时,请放置所有场景。

    【讨论】:

    • 更改消息正文是个好主意,但是方法 JsonReaderWriterFactory.CreateJsonReader 不接受 StringReader 作为它的第一个参数,它应该是一个 Stream。
    • 我得到了这个工作。不再需要更改请求内容,而是在请求中添加一个属性。我将通过这些更改更新我的问题。非常感谢!
    • 很高兴它帮助您前进! :)
    • vendettamit,你能看看我的new question?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-24
    • 1970-01-01
    • 2011-04-28
    • 1970-01-01
    • 2011-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多