如何在asp.NET 3.5 WCF Web服务中获取原始请求主体

时间:2015-10-27 18:56:43

标签: asp.net wcf

我已经被要求开发一个C#rest api,它需要记录(在数据库表中)对任何已定义路由的每个请求。每个日志都需要记录请求正文,网址,响应正文和状态(待定,成功或错误)

经过大量的互联网研究,我发现下面的例子,它最接近我的需要,但是它给了我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>

2 个答案:

答案 0 :(得分:0)

您将方法设置为接收(调用)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);

希望有所帮助

答案 1 :(得分:0)

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

更新(最近评论):

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

 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”。这样价值就会被动了化。但这不是实现它的唯一方法。可能当你问另​​一个问题时,所有场景。