【发布时间】: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 没有被调用的原因。