Потрібно увійти до бази даних бази даних asp.net webapi 2 та відповіді


103

Я використовую Microsoft Asp.net WebApi2, розміщений на IIS. Я дуже просто хотів би записати тіло запиту (XML або JSON) та орган відповіді для кожної публікації.

У цьому проекті чи контролері, що обробляє повідомлення, немає нічого особливого. Мені не цікаво використовувати рамки ведення журналів, такі як nLog, elmah, log4net, або вбудовані функції відстеження веб-API, якщо цього не потрібно.

Мені просто хочеться знати, куди потрібно помістити свій код реєстрації та як отримати фактичний JSON або XML з вхідного та вихідного запиту та відповіді.

Мій метод повідомлення контролера:

public HttpResponseMessage Post([FromBody])Employee employee)
{
   if (ModelState.IsValid)
   {
      // insert employee into to the database
   }

}

Ви хочете записати запит / відповідь на певну дію, набір або всі ваші дії в певному контролері?
LB2

Цікавить лише реєстрація повідомлення. (a) Час публікації (b) в тілі xml або json, опублікованому (c) відповіді (вміст xml чи json) разом із Http Status Code
user2315985

Причина, яку я просив, полягає в тому, щоб запропонувати, чи вводити код безпосередньо в дію, або загальне рішення для всіх дій. Дивіться мою відповідь нижче.
LB2

FYI Я видалив asp.net, оскільки це питання не стосується цього питання
Далорцо

це створення файлу не варіант?
Прерак К

Відповіді:


194

Я рекомендую використовувати DelegatingHandler. Тоді вам не потрібно буде турбуватися про будь-який код реєстрації у ваших контролерах.

public class LogRequestAndResponseHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (request.Content != null)
        {
            // log request body
            string requestBody = await request.Content.ReadAsStringAsync();
            Trace.WriteLine(requestBody);
        }
        // let other handlers process the request
        var result = await base.SendAsync(request, cancellationToken);

        if (result.Content != null)
        {
            // once response body is ready, log it
            var responseBody = await result.Content.ReadAsStringAsync();
            Trace.WriteLine(responseBody);
        }

        return result;
    }
}

Просто замініть Trace.WriteLineсвій код реєстрації та зареєструйте обробника WebApiConfigтаким чином:

config.MessageHandlers.Add(new LogRequestAndResponseHandler());

Ось повна документація Microsoft для обробників повідомлень .


3
task.Result.Contentповертає System.Net.Http.ObjectContent. Чи є натомість отримати сировинний xml / json?
ПК.

4
@SoftwareFactor: ContinueWithі Resultє небезпечними API. Набагато краще використовувати це, awaitа саме,var result = await base.SendAsync(request, cancellationToken); var resposeBody = await response.Content.ReadAsStringAsync(); Trace.WriteLine(responseBody); return response;
Стівен Клірі

9
Це дуже круте рішення, однак воно призведе до помилки, коли відповідь не містить тіла. Але це досить просто перевірити і виправити :)
buddybubble

6
Чи заклик await request.Content.ReadAsStringAsync();не призводить до помилки, кажучи, що потік запиту вже був прочитаний у певних обставинах?
Гевін

6
Якщо делегуючий обробник читає тіло запиту, чи не зробить він недоступним фактичний обробник терміналу (тобто mvc / webapi)?
LB2

15

Існує кілька підходів до загального обліку запитів / відповідей для кожного виклику методу WebAPI:

  1. ActionFilterAttribute: Можна записати власні ActionFilterAttributeта прикрасити контролер / методи дій, щоб увімкнути ведення журналу.

    Con: Вам потрібно прикрасити кожен контролер / методи (все-таки це можна зробити на базовому контролері, але все ще він не стосується наскрізних проблем.

  2. Замініть BaseControllerта обробіть лісозаготівлю там.

    Con: Ми очікуємо / змушуємо контролерів успадкувати від користувацького базового контролера.

  3. Використання DelegatingHandler.

    Перевага: Ми не торкаємося контролера / методу при такому підході. Делегуючий обробник сидить ізольовано і витончено обробляє запит / відповідь.

Для отримання додаткової статті, зверніться до цієї http://weblogs.asp.net/fredriknormen/log-message-request-and-response-in-asp-net-webapi .


Ви можете призначити будь-який actionfilter наступним чином: загальнодоступний статичний клас WebApiConfig {публічний статичний недійсний реєстр (конфігурація HttpConfiguration) {// Конфігурація веб-API та сервіс config.Filters.Add (новий MyFilter ()) // Конфігурація маршрутів веб-API.MapHttpAttributeRoutes (); config.Routes.MapHttpRoute (назва: "DefaultApi", routeTemplate: "api / {контролер} / {id}", за замовчуванням: new {id = RouteParameter.O optional}); }}
Міка Карюнен

11

Один із можливих варіантів - створити фільтр дій та прикрасити ним WebApiController / ApiMethod.

Атрибут фільтра

public class MyFilterAttribute : System.Web.Http.Filters.ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (actionContext.Request.Method == HttpMethod.Post)
            {
                var postData = actionContext.ActionArguments;
                //do logging here
            }
        }
    }

Контролер WebApi

[MyFilterAttribute]
public class ValuesController : ApiController{..}

або

[MyFilterAttribute]
public void Post([FromBody]string value){..}

Сподіваюся, це допомагає.


Мені подобається такий підхід, але для отримання відповіді я повинен замінити OnActionExecuted замість цього. Проблема полягає в тому, що запит на той момент вже перетворений на мій POCO, а не в xml або json. Будь-які думки?
користувач2315985

Спочатку я мав на увазі реєстрацію даних у OnActionExecuting, а потім просто дозвольте публікації виконати свою роботу. Я зрозумів з вашого запитання, що ви просто хочете записати дані для кожної зробленої публікації.
Прерак К

3
Я хочу реєструвати як запит, так і дані відповіді кожного разу, коли хтось публікує повідомлення.
користувач2315985

2
ви можете використовувати OnActionExecuted та спробувати "(actionExecutedContext.ActionContext.Response.Content як ObjectContent) .Value.ToString ()", щоб отримати відповідь та записати її.
Прерак К

Як отримати запит від OnActionExecuted?
користувач2315985

3

Отримати доступ до повідомлення запиту дуже просто. Ваш базовий клас,ApiController містить .Requestвластивість , яка, як випливає з назви, містить запит у розібраному вигляді. Ви просто досліджуєте його на предмет того, що ви хочете ввійти, і передаєте його у свій лісозаготівельний заклад, залежно від того, що це буде. Цей код ви можете поставити на початку вашої дії, якщо вам потрібно зробити це лише для однієї або жменьки.

Якщо вам потрібно зробити це у всіх діях (все це означає більше ніж керована жменька), тоді ви можете переосмислити .ExecuteAsyncметод перехоплення кожного виклику дій вашого контролера.

public override Task<HttpResponseMessage> ExecuteAsync(
    HttpControllerContext controllerContext,
    CancellationToken cancellationToken
)
{
    // Do logging here using controllerContext.Request
    return base.ExecuteAsync(controllerContext, cancellationToken);
}

Я роблю це, і я цього ще не орієнтував, просто моя інтуїція підказує, що це може бути дуже повільним?
Маркус

Як ви думаєте, чому це буде повільно? ExecuteAsyncце те, що викликається рамкою, а реалізація базового класу контролерів - це те, що насправді виконує дію. Це просто заклик до вашого журналу як частина виконання, що вже відбувається. Вони лише покарання - це час зробити фактичну лісозаготівлю.
LB2

Ні, я маю на увазі "дуже повільно", як при реєстрації кожного запиту.
Маркус

2
Ну, це питання вимог, і це вимога, заявлена ​​ОП. Це питання обсягу оброблюваного сайту, продуктивності лісозаготівлі тощо. Це поза посадою ОП.
LB2

0

Це здається досить старою темою, але чертовий обмін іншим рішенням.

Ви можете додати цей метод у свій файл global.asax, який буде запускатися кожного разу після закінчення запиту HTTP.

void Application_EndRequest(Object Sender, EventArgs e)
    {
        var request = (Sender as HttpApplication).Request;
        var response = (Sender as HttpApplication).Response;

        if (request.HttpMethod == "POST" || request.HttpMethod == "PUT")
        {


            byte[] bytes = request.BinaryRead(request.TotalBytes);
            string body = Encoding.UTF7.GetString(bytes);
            if (!String.IsNullOrEmpty(body))
            {


                // Do your logic here (Save in DB, Log in IIS etc.)
            }
        }
    }

0

Це дійсно стара тема, але я витратив багато часу (пошук в Інтернеті), щоб зробити це, тому я просто опублікую тут своє рішення.

Концепція

  1. Перевизнайте ExecuteAsync методу APicontroller для відстеження вхідного запиту, у своєму рішенні я створюю Base_ApiController як батьківський контролер API мого проекту.
  2. Використовуйте System.Web.Http.Filters.ActionFilterAttribute для відстеження вихідної відповіді контролера api
  3. *** (Додатково) *** Використовуйте System.Web.Http.Filters.ExceptionFilterAttribute для входу в журнал, коли виникають винятки.

1. MyController.cs

    [APIExceptionFilter]  // use 3.
    [APIActionFilter]     // use 2.
    public class Base_APIController : ApiController
    {
        public   bool  IsLogInbound
        {
            get
            { return   ConfigurationManager.AppSettings["LogInboundRequest"] =="Y"? true:false ;     }
        }
        /// <summary>
        /// for logging exception
        /// </summary>
        /// <param name="controllerContext"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override Task<HttpResponseMessage> ExecuteAsync(
         HttpControllerContext controllerContext,
         CancellationToken cancellationToken
         )
        {
            // Do logging here using controllerContext.Request
            // I don't know why calling the code below make content not null Kanit P.
            var content = controllerContext.Request.Content.ReadAsStringAsync().Result.ToString(); // keep request json content
             // Do your own logging!
            if (IsLogInbound)
            {
                try
                {
                    ErrLog.Insert(ErrLog.type.InboundRequest, controllerContext.Request,
                         controllerContext.Request.RequestUri.AbsoluteUri
                         , content);
                }
                catch (Exception e) { }
            }

            // will not log err when go to wrong controller's action (error here but not go to APIExceptionFilter)
            var t = base.ExecuteAsync(controllerContext, cancellationToken);
            if (!t.Result.IsSuccessStatusCode)
            { 
            }
            return t;

        }

2. APIActionFilter.cs

    public class APIActionFilter : System.Web.Http.Filters.ActionFilterAttribute
    {
        public bool LogOutboundRequest
        {
            get
            { return ConfigurationManager.AppSettings["LogInboundRequest"] == "Y" ? true : false; }
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            try {

                var returndata = actionExecutedContext.Response.Content.ReadAsStringAsync().Result.ToString(); 
             //keep Json response content
             // Do your own logging!
                if (LogOutboundRequest)
                {
                    ErrLog.Insert(ErrLog.type.OutboundResponse, actionExecutedContext.Response.Headers,
                       actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerName
                      + "/"
                      + actionExecutedContext.ActionContext.ActionDescriptor.ActionName
                      , returndata );
                }
            } catch (Exception e) {

            }
     

        } 
    }
}

3. APIExceptionFilter.cs

    public class APIExceptionFilter : ExceptionFilterAttribute
    {
    public bool IsLogErr
    {
        get
        { return ConfigurationManager.AppSettings["LogExceptionRequest"] == "Y" ? true : false; }
    }


    public override void OnException(HttpActionExecutedContext context)
    {
        try
        { 
            //Do your own logging!
            if (IsLogErr)
            {
                ErrLog.Insert(ErrLog.type.APIFilterException, context.Request,
                    context.ActionContext.ControllerContext.ControllerDescriptor.ControllerName
                    + "/"
                    + context.ActionContext.ActionDescriptor.ActionName
                    , context.Exception.ToString() + context.Exception.StackTrace);
            }
        }catch(Exception e){

        }

        if (context.Exception is NotImplementedException)
        {
            context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
        }
        else {
            context.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError);

        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.