Cần đăng nhập cơ quan phản hồi và yêu cầu asp.net webapi 2 vào cơ sở dữ liệu


103

Tôi đang sử dụng Microsoft Asp.net WebApi2 được lưu trữ trên IIS. Tôi rất đơn giản muốn ghi lại nội dung yêu cầu (XML hoặc JSON) và nội dung phản hồi cho mỗi bài đăng.

Không có gì đặc biệt về dự án này hoặc bộ điều khiển xử lý bài đăng. Tôi không quan tâm đến việc sử dụng các khuôn khổ ghi nhật ký như nLog, elmah, log4net hoặc các tính năng theo dõi tích hợp của API web trừ khi cần thiết phải làm như vậy.

Tôi chỉ đơn giản là muốn biết nơi đặt mã ghi nhật ký của mình và cách lấy JSON hoặc XML thực tế từ yêu cầu và phản hồi đến và đi.

Phương pháp đăng bộ điều khiển của tôi:

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

}

Bạn đang muốn ghi lại Yêu cầu / Phản hồi cho một hành động cụ thể, một tập hợp hoặc tất cả các hành động của bạn trong một bộ điều khiển cụ thể?
LB2

Chỉ quan tâm đến đăng nhập Bài viết. (a) Thời gian Bưu điện (b) cơ thể của xml hoặc json đăng (c) phản ứng (xml hoặc json nội dung) cùng với HTTP Status Code
user2315985

Lý do tôi hỏi là đề xuất xem nên đặt mã trực tiếp vào hành động hay giải pháp chung cho tất cả các hành động. Xem câu trả lời của tôi dưới đây.
LB2

FYI Tôi đã xóa asp.net vì nó không liên quan đến câu hỏi này
Dalorzo

tạo một bộ lọc không phải là một tùy chọn?
Prerak K

Câu trả lời:


192

Tôi muốn khuyên bạn nên sử dụng a DelegatingHandler. Sau đó, bạn sẽ không cần phải lo lắng về bất kỳ mã ghi nhật ký nào trong bộ điều khiển của mình.

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;
    }
}

Chỉ cần thay thế Trace.WriteLinebằng mã ghi nhật ký của bạn và đăng ký trình xử lý WebApiConfignhư sau:

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

Đây là tài liệu đầy đủ của Microsoft cho Trình xử lý Thư .


3
task.Result.Contentlợi nhuận System.Net.Http.ObjectContent. Có cách nào để lấy xml / json thô thay thế không?
Máy tính cá nhân.

4
@SoftwareFactor: ContinueWithResultlà các API nguy hiểm. Nó sẽ là tốt hơn để sử dụng awaitthay vào đó, tức là,var result = await base.SendAsync(request, cancellationToken); var resposeBody = await response.Content.ReadAsStringAsync(); Trace.WriteLine(responseBody); return response;
Stephen Cleary

9
Đây là một giải pháp rất hay, tuy nhiên nó sẽ gây ra lỗi khi phản hồi không chứa nội dung. Nhưng thats đủ dễ dàng để kiểm tra và sửa chữa :)
buddybubble

6
await request.Content.ReadAsStringAsync();Lệnh gọi không dẫn đến lỗi nói rằng luồng yêu cầu đã được đọc trong một số trường hợp nhất định?
Gavin

6
Nếu trình xử lý ủy quyền đọc nội dung của yêu cầu, nó có làm cho nó không khả dụng với trình xử lý đầu cuối thực tế (tức là mvc / webapi) không?
LB2

15

Có nhiều cách tiếp cận để xử lý chung ghi nhật ký Yêu cầu / Phản hồi cho mọi lệnh gọi phương thức WebAPI:

  1. ActionFilterAttribute: Người ta có thể viết tùy chỉnh ActionFilterAttributevà trang trí bộ điều khiển / phương thức hành động để cho phép ghi nhật ký.

    Con: Bạn cần phải trang trí mọi bộ điều khiển / phương thức (bạn vẫn có thể làm điều đó trên bộ điều khiển cơ sở, nhưng nó vẫn không giải quyết được các mối quan tâm xuyên suốt.

  2. Ghi đè BaseControllervà xử lý ghi nhật ký ở đó.

    Con: Chúng tôi đang mong đợi / buộc các bộ điều khiển kế thừa từ bộ điều khiển cơ sở tùy chỉnh.

  3. Đang sử dụng DelegatingHandler.

    Ưu điểm: Chúng tôi không chạm vào bộ điều khiển / phương pháp ở đây với cách tiếp cận này. Trình xử lý ủy quyền nằm riêng lẻ và xử lý một cách duyên dáng việc ghi nhật ký yêu cầu / phản hồi.

Để biết thêm bài viết chuyên sâu, hãy tham khảo http://weblogs.asp.net/fredriknormen/log-message-request-and-response-in-asp-net-webapi này .


Bạn có thể gán bất kỳ bộ lọc hành động nào như sau: public static class WebApiConfig {public static void Register (HttpConfiguration config) {// Cấu hình Web API và các dịch vụ config.Filters.Add (new MyFilter ()) // Các tuyến API Web config.MapHttpAttributeRoutes (); config.Routes.MapHttpRoute (tên: "DefaultApi", routeTemplate: "api / {controller} / {id}", defaults: new {id = RouteParameter.Optional}); }}
Mika Karjunen

11

Một trong những tùy chọn bạn có là sử dụng tạo bộ lọc hành động và trang trí WebApiController / ApiMethod của bạn với nó.

Thuộc tính bộ lọc

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
            }
        }
    }

Bộ điều khiển WebApi

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

hoặc là

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

Hi vọng điêu nay co ich.


Tôi thích cách tiếp cận này nhưng để nhận được phản hồi, tôi phải ghi đè OnActionExecuted. Vấn đề là yêu cầu tại thời điểm đó đã được chuyển đổi thành POCO của tôi thay vì là xml hoặc json. Có suy nghĩ gì không?
user2315985

Ý tôi là ban đầu, đăng nhập dữ liệu trong OnActionExecuting và sau đó đơn giản hãy để bài đăng thực hiện công việc của nó. Tôi hiểu từ câu hỏi của bạn là bạn chỉ muốn ghi lại dữ liệu cho mỗi bài đăng được thực hiện.
Prerak K

3
Tôi muốn ghi lại cả dữ liệu yêu cầu và phản hồi mỗi khi ai đó đăng bài.
user2315985

2
bạn có thể sử dụng OnActionExecuted và thử "(actionExecutedContext.ActionContext.Response.Content as ObjectContent) .Value.ToString ()" để nhận phản hồi và ghi lại nó.
Prerak K

Làm cách nào để nhận yêu cầu từ bên trong OnActionExecuted?
user2315985,

3

Dễ dàng truy cập vào thông báo yêu cầu. Lớp cơ sởApiController của bạn , chứa thuộc .Requesttính , như tên cho thấy, chứa yêu cầu ở dạng phân tích cú pháp. Bạn chỉ cần kiểm tra nó để tìm bất cứ thứ gì bạn muốn ghi lại và chuyển nó đến cơ sở ghi nhật ký của bạn, tùy theo nó có thể là gì. Mã này bạn có thể đặt khi bắt đầu hành động của mình, nếu bạn cần thực hiện chỉ một hoặc một số ít.

Nếu bạn cần thực hiện điều đó trên tất cả các hành động (tất cả có nghĩa là nhiều hơn một số ít có thể quản lý), thì những gì bạn có thể làm là ghi đè .ExecuteAsyncphương thức để nắm bắt mọi lệnh gọi hành động cho bộ điều khiển của bạn.

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

Tôi đang làm điều này, và tôi vẫn chưa đánh giá nó, chỉ là trực giác của tôi cho tôi biết rằng điều này có thể rất chậm?
Marcus

Tại sao bạn nghĩ nó sẽ chậm? ExecuteAsynclà những gì được gọi bởi khung công tác và việc triển khai lớp bộ điều khiển cơ sở là những gì thực sự được thực thi hành động. Đây chỉ là gọi vào nhật ký của bạn như một phần của quá trình thực thi đã diễn ra. Họ chỉ phạt ở đây là thời gian để khai thác thực tế.
LB2

Không, ý tôi là, 'rất chậm' khi ghi lại mọi yêu cầu.
Marcus

2
Chà, đó là một câu hỏi về các yêu cầu, và đó là yêu cầu mà OP đã nêu. Đó là một câu hỏi về khối lượng mà trang web xử lý, hiệu suất của cơ sở ghi nhật ký, v.v. Đó là điều vượt quá bài đăng của OP.
LB2

0

Đây có vẻ là một chủ đề khá cũ nhưng lại chia sẻ một giải pháp khác.

Bạn có thể thêm phương thức này vào tệp global.asax của mình, tệp này sẽ được kích hoạt sau khi yêu cầu HTTP kết thúc.

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

Đây thực sự là một chủ đề cũ nhưng tôi đã dành nhiều thời gian (tìm kiếm trên internet) để làm những điều này vì vậy tôi sẽ chỉ đăng giải pháp của tôi ở đây.

Ý tưởng

  1. Ghi đè phương thức ExecuteAsync của APicontroller để theo dõi yêu cầu Inbound, trong giải pháp của tôi, tôi tạo Base_ApiController làm cha của các bộ điều khiển API của dự án.
  2. Sử dụng System.Web.Http.Filters.ActionFilterAttribute để theo dõi phản hồi gửi đi của bộ điều khiển api
  3. *** (Bổ sung) *** Sử dụng System.Web.Http.Filters.ExceptionFilterAttribute để ghi lại khi xảy ra ngoại lệ.

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);

        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.