Я пишу веб-службу (используя ASP.NET MVC), и в целях поддержки мы хотели бы иметь возможность регистрировать запросы и ответы как можно ближе к необработанному, беспроводному формату (т.е. включая HTTP метод, путь, все заголовки и тело) в базу данных.
Я не уверен в том, как получить эти данные наименее "искаженным" способом. Я могу заново составить то, что, по моему мнению, выглядит как запрос, проверив все свойства HttpRequest
объекта и построив из них строку (и аналогично для ответа), но я действительно хотел бы получить фактические данные запроса / ответа, которые отправлено по телеграфу.
Я счастлив использовать любой механизм перехвата, такой как фильтры, модули и т. Д., И решение может быть специфичным для IIS7. Однако я бы предпочел оставить его только в управляемом коде.
Какие-нибудь рекомендации?
Изменить: я отмечаю, что у HttpRequest
него есть SaveAs
метод, который может сохранить запрос на диск, но он восстанавливает запрос из внутреннего состояния, используя загрузку внутренних вспомогательных методов, к которым нельзя получить доступ публично (вот почему это не позволяет сохранить в предоставленный пользователем поток не знаю). Так что это начинает выглядеть так, как будто мне придется сделать все возможное, чтобы восстановить текст запроса / ответа из объектов ... стон.
Изменить 2: Обратите внимание, что я сказал весь запрос, включая метод, путь, заголовки и т. Д. Текущие ответы смотрят только на потоки тела, которые не включают эту информацию.
Изменить 3: здесь никто не читает вопросы? На данный момент пять ответов, но ни один из них даже не намекает на способ получить весь необработанный запрос по сети. Да, я знаю, что могу захватывать выходные потоки, заголовки, URL-адрес и все такое из объекта запроса. Я уже сказал, что в вопросе см .:
Я могу заново составить то, что, по моему мнению, выглядит как запрос, проверив все свойства объекта HttpRequest и построив из них строку (и аналогично для ответа), но я бы очень хотел получить фактические данные запроса / ответа. это отправлено по телеграфу.
Если вы знаете, что полные необработанные данные (включая заголовки, URL-адрес, метод http и т. Д.) Просто не могут быть получены, это было бы полезно знать. Точно так же, если вы знаете, как получить все это в необработанном формате (да, я все еще имею в виду заголовки, URL-адрес, метод http и т.д.) без необходимости его восстанавливать, о чем я просил, тогда это было бы очень полезно. Но рассказывать мне, что я могу восстановить его из HttpRequest
/ HttpResponse
объектов, бесполезно. Я знаю это. Я уже сказал это.
Обратите внимание: прежде чем кто-либо начнет говорить, что это плохая идея или ограничит масштабируемость и т. Д., Мы также будем внедрять механизмы регулирования, последовательной доставки и предотвращения повторного воспроизведения в распределенной среде, поэтому в любом случае потребуется ведение журнала базы данных. Я не ищу обсуждения того, хорошая ли это идея, я ищу, как это можно сделать.
источник
Ответы:
Определенно использовать
IHttpModule
и реализоватьBeginRequest
иEndRequest
события.Все «сырые» данные присутствуют между
HttpRequest
иHttpResponse
, их просто нет в едином необработанном формате. Вот части, необходимые для создания дампов в стиле Fiddler (примерно как можно ближе к необработанному HTTP):request.HttpMethod + " " + request.RawUrl + " " + request.ServerVariables["SERVER_PROTOCOL"] request.Headers // loop through these "key: value" request.InputStream // make sure to reset the Position after reading or later reads may fail
Для ответа:
"HTTP/1.1 " + response.Status response.Headers // loop through these "key: value"
Обратите внимание, что вы не можете прочитать поток ответа, поэтому вам нужно добавить фильтр в выходной поток и захватить копию.
В вашем
BeginRequest
вам нужно будет добавить фильтр ответа:HttpResponse response = HttpContext.Current.Response; OutputFilterStream filter = new OutputFilterStream(response.Filter); response.Filter = filter;
Магазин,
filter
где вы можете добраться до него вEndRequest
обработчике. Предлагаю вHttpContext.Items
. Затем можно получить полные данные ответа в форматеfilter.ReadStream()
.Затем реализуйте
OutputFilterStream
использование шаблона Decorator в качестве оболочки для потока:/// <summary> /// A stream which keeps an in-memory copy as it passes the bytes through /// </summary> public class OutputFilterStream : Stream { private readonly Stream InnerStream; private readonly MemoryStream CopyStream; public OutputFilterStream(Stream inner) { this.InnerStream = inner; this.CopyStream = new MemoryStream(); } public string ReadStream() { lock (this.InnerStream) { if (this.CopyStream.Length <= 0L || !this.CopyStream.CanRead || !this.CopyStream.CanSeek) { return String.Empty; } long pos = this.CopyStream.Position; this.CopyStream.Position = 0L; try { return new StreamReader(this.CopyStream).ReadToEnd(); } finally { try { this.CopyStream.Position = pos; } catch { } } } } public override bool CanRead { get { return this.InnerStream.CanRead; } } public override bool CanSeek { get { return this.InnerStream.CanSeek; } } public override bool CanWrite { get { return this.InnerStream.CanWrite; } } public override void Flush() { this.InnerStream.Flush(); } public override long Length { get { return this.InnerStream.Length; } } public override long Position { get { return this.InnerStream.Position; } set { this.CopyStream.Position = this.InnerStream.Position = value; } } public override int Read(byte[] buffer, int offset, int count) { return this.InnerStream.Read(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { this.CopyStream.Seek(offset, origin); return this.InnerStream.Seek(offset, origin); } public override void SetLength(long value) { this.CopyStream.SetLength(value); this.InnerStream.SetLength(value); } public override void Write(byte[] buffer, int offset, int count) { this.CopyStream.Write(buffer, offset, count); this.InnerStream.Write(buffer, offset, count); } }
источник
Следующий метод расширения в HttpRequest создаст строку, которую можно вставить в скрипт и воспроизвести.
namespace System.Web { using System.IO; /// <summary> /// Extension methods for HTTP Request. /// <remarks> /// See the HTTP 1.1 specification http://www.w3.org/Protocols/rfc2616/rfc2616.html /// for details of implementation decisions. /// </remarks> /// </summary> public static class HttpRequestExtensions { /// <summary> /// Dump the raw http request to a string. /// </summary> /// <param name="request">The <see cref="HttpRequest"/> that should be dumped. </param> /// <returns>The raw HTTP request.</returns> public static string ToRaw(this HttpRequest request) { StringWriter writer = new StringWriter(); WriteStartLine(request, writer); WriteHeaders(request, writer); WriteBody(request, writer); return writer.ToString(); } private static void WriteStartLine(HttpRequest request, StringWriter writer) { const string SPACE = " "; writer.Write(request.HttpMethod); writer.Write(SPACE + request.Url); writer.WriteLine(SPACE + request.ServerVariables["SERVER_PROTOCOL"]); } private static void WriteHeaders(HttpRequest request, StringWriter writer) { foreach (string key in request.Headers.AllKeys) { writer.WriteLine(string.Format("{0}: {1}", key, request.Headers[key])); } writer.WriteLine(); } private static void WriteBody(HttpRequest request, StringWriter writer) { StreamReader reader = new StreamReader(request.InputStream); try { string body = reader.ReadToEnd(); writer.WriteLine(body); } finally { reader.BaseStream.Position = 0; } } } }
источник
HttpRequestBaseExtensions
и изменить ,HttpRequest
чтобыHttpRequestBase
в каждом месте.Вы можете использовать серверную переменную ALL_RAW, чтобы получить исходные заголовки HTTP, отправленные с запросом, затем вы можете получить InputStream как обычно:
string originalHeader = HttpHandler.Request.ServerVariables["ALL_RAW"];
проверить: http://msdn.microsoft.com/en-us/library/ms524602%28VS.90%29.aspx
источник
HttpContext.Current.Request
для получения текущего контекста за пределами контроллеров MVC, страниц ASPX и т. Д., Просто убедитесь, что сначала он неНу, я работаю над проектом и сделал, может быть, не слишком глубокий журнал, используя параметры запроса:
Взглянем:
public class LogAttribute : ActionFilterAttribute { private void Log(string stageName, RouteData routeData, HttpContextBase httpContext) { //Use the request and route data objects to grab your data string userIP = httpContext.Request.UserHostAddress; string userName = httpContext.User.Identity.Name; string reqType = httpContext.Request.RequestType; string reqData = GetRequestData(httpContext); string controller = routeData["controller"]; string action = routeData["action"]; //TODO:Save data somewhere } //Aux method to grab request data private string GetRequestData(HttpContextBase context) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < context.Request.QueryString.Count; i++) { sb.AppendFormat("Key={0}, Value={1}<br/>", context.Request.QueryString.Keys[i], context.Request.QueryString[i]); } for (int i = 0; i < context.Request.Form.Count; i++) { sb.AppendFormat("Key={0}, Value={1}<br/>", context.Request.Form.Keys[i], context.Request.Form[i]); } return sb.ToString(); }
Вы можете украсить свой класс контроллеров, чтобы полностью записать его:
[Log] public class TermoController : Controller {...}
или зарегистрируйте только некоторые отдельные методы действий
[Log] public ActionResult LoggedAction(){...}
источник
По какой причине вам нужно сохранить его в управляемом коде?
Стоит упомянуть, что вы можете включить ведение журнала Failed Trace в IIS7, если вам не нравится изобретать колесо заново. При этом регистрируются заголовки, тело запроса и ответа, а также многое другое.
источник
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
в конецRegister(..)
inWebApiConfig.cs
, но это может варьироваться в зависимости от версии.Я согласился с подходом McKAMEY. Вот модуль, который я написал, который поможет вам начать работу и, надеюсь, сэкономит вам время. Очевидно, вам нужно подключить Logger к чему-то, что вам подходит:
public class CaptureTrafficModule : IHttpModule { public void Init(HttpApplication context) { context.BeginRequest += new EventHandler(context_BeginRequest); context.EndRequest += new EventHandler(context_EndRequest); } void context_BeginRequest(object sender, EventArgs e) { HttpApplication app = sender as HttpApplication; OutputFilterStream filter = new OutputFilterStream(app.Response.Filter); app.Response.Filter = filter; StringBuilder request = new StringBuilder(); request.Append(app.Request.HttpMethod + " " + app.Request.Url); request.Append("\n"); foreach (string key in app.Request.Headers.Keys) { request.Append(key); request.Append(": "); request.Append(app.Request.Headers[key]); request.Append("\n"); } request.Append("\n"); byte[] bytes = app.Request.BinaryRead(app.Request.ContentLength); if (bytes.Count() > 0) { request.Append(Encoding.ASCII.GetString(bytes)); } app.Request.InputStream.Position = 0; Logger.Debug(request.ToString()); } void context_EndRequest(object sender, EventArgs e) { HttpApplication app = sender as HttpApplication; Logger.Debug(((OutputFilterStream)app.Response.Filter).ReadStream()); } private ILogger _logger; public ILogger Logger { get { if (_logger == null) _logger = new Log4NetLogger(); return _logger; } } public void Dispose() { //Does nothing } }
источник
Encoding.UTF8
, а можетEncoding.Default
при чтении потока запроса? Или просто используйтеStreamReader
( сИтак, похоже, ответ - «нет, вы не можете получить необработанные данные, вам нужно восстановить запрос / ответ из свойств проанализированных объектов». Ну что ж, я сделал реконструкцию.
источник
используйте IHttpModule :
namespace Intercepts { class Interceptor : IHttpModule { private readonly InterceptorEngine engine = new InterceptorEngine(); #region IHttpModule Members void IHttpModule.Dispose() { } void IHttpModule.Init(HttpApplication application) { application.EndRequest += new EventHandler(engine.Application_EndRequest); } #endregion } } class InterceptorEngine { internal void Application_EndRequest(object sender, EventArgs e) { HttpApplication application = (HttpApplication)sender; HttpResponse response = application.Context.Response; ProcessResponse(response.OutputStream); } private void ProcessResponse(Stream stream) { Log("Hello"); StreamReader sr = new StreamReader(stream); string content = sr.ReadToEnd(); Log(content); } private void Log(string line) { Debugger.Log(0, null, String.Format("{0}\n", line)); } }
источник
если для случайного использования, чтобы обойти узкий угол, как насчет чего-нибудь грубого, как показано ниже?
Public Function GetRawRequest() As String Dim str As String = "" Dim path As String = "C:\Temp\REQUEST_STREAM\A.txt" System.Web.HttpContext.Current.Request.SaveAs(path, True) str = System.IO.File.ReadAllText(path) Return str End Function
источник
Вы можете сделать это
DelegatingHandler
без использованияOutputFilter
упомянутых в других ответах в .NET 4.5 с помощьюStream.CopyToAsync()
функции.Я не уверен в деталях, но это не вызывает всех плохих вещей, которые происходят, когда вы пытаетесь напрямую прочитать поток ответа.
Пример:
public class LoggingHandler : DelegatingHandler { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { DoLoggingWithRequest(request); var response = await base.SendAsync(request, cancellationToken); await DoLoggingWithResponse(response); return response; } private async Task DologgingWithResponse(HttpResponseMessage response) { var stream = new MemoryStream(); await response.Content.CopyToAsync(stream).ConfigureAwait(false); DoLoggingWithResponseContent(Encoding.UTF8.GetString(stream.ToArray())); // The rest of this call, the implementation of the above method, // and DoLoggingWithRequest is left as an exercise for the reader. } }
источник
Я знаю, что это не управляемый код, но я собираюсь предложить фильтр ISAPI. Прошло несколько лет с тех пор, как я имел «удовольствие» поддерживать свой собственный ISAPI, но насколько я помню, вы можете получить доступ ко всему этому материалу как до, так и после того, как ASP.Net сделал это.
http://msdn.microsoft.com/en-us/library/ms524610.aspx
Если HTTPModule недостаточно хорош для того, что вам нужно, я просто не думаю, что есть какой-либо управляемый способ сделать это с требуемым количеством деталей. Хотя это будет больно.
источник
Я согласен с остальными, используйте IHttpModule. Взгляните на ответ на этот вопрос, который делает почти то же самое, что вы задаете. Он регистрирует запрос и ответ, но без заголовков.
Как отслеживать запросы ScriptService WebService?
источник
Возможно, лучше сделать это вне вашего приложения. Вы можете настроить обратный прокси, чтобы делать такие вещи (и многое другое). Обратный прокси-сервер - это в основном веб-сервер, который находится в вашей серверной комнате и стоит между вашим веб-сервером (-ами) и клиентом. См. Http://en.wikipedia.org/wiki/Reverse_proxy
источник
Согласитесь с FigmentEngine,
IHttpModule
похоже, это правильный путь.Загляните внутрь
httpworkerrequest
,readentitybody
иGetPreloadedEntityBody
.Для получения
httpworkerrequest
вам необходимо сделать следующее:(HttpWorkerRequest)inApp.Context.GetType().GetProperty("WorkerRequest", bindingFlags).GetValue(inApp.Context, null);
где
inApp
находится объект httpapplication.источник
HttpRequest
иHttpResponse
pre MVC использовали,GetInputStream()
иGetOutputStream()
это можно было использовать для этой цели. Я не изучал эту часть в MVC, поэтому я не уверен, что они доступны, но может быть идеей :)источник