Как получить необработанное тело запроса в веб-службе WCF asp.NET 3.5
Я был обязан разработать API для отдыха C#, который должен регистрировать (в таблице базы данных) каждый отдельный запрос к любому определенному маршруту. Каждый журнал должен записывать тело запроса, URL, тело ответа и статус (Ожидание, Успех или Ошибка)
После долгих интернет-исследований я нашел приведенный ниже пример, который наиболее близок к тому, что мне нужно, но он дает мне данные в формате 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 ответа
Вам нужно реализовать пользовательский IDispatchMessageInspector для захвата необработанного запроса в методе AfterReceiveRequest. Смотрите мой ответ здесь.
Обновление (для последних комментариев):
Что касается вашего последнего комментария, вы можете изменить содержание сообщения, добавив дополнительную информацию, например, в вашем случае идентификатор; Если вы посмотрите на код в образце внутри метода 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
учебный класс. Так что значение становится derserialized. Но это не единственный способ добиться этого. Вероятно, когда вы задаете другой вопрос, ставьте все сценарии.
Вы устанавливаете свой метод для получения (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);
Надеюсь, поможет