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