एक डेटाबेस में asp.net webapi 2 अनुरोध और प्रतिक्रिया निकाय लॉग इन करने की आवश्यकता है


103

मैं IIS पर होस्ट Microsoft Asp.net WebApi2 का उपयोग कर रहा हूं। मैं बहुत ही अनुरोध शरीर (XML या JSON) और प्रत्येक पोस्ट के लिए प्रतिक्रिया निकाय को लॉग करना चाहूंगा।

इस परियोजना या पोस्ट को नियंत्रित करने वाले नियंत्रक के बारे में कुछ खास नहीं है। मुझे लॉगिंग फ्रेमवर्क जैसे nLog, elmah, log4net या वेब एपीआई की अंतर्निहित ट्रेसिंग विशेषताओं का उपयोग करने में कोई दिलचस्पी नहीं है, जब तक कि ऐसा करना आवश्यक न हो।

मैं बस यह जानना चाहता हूं कि मेरा लॉगिंग कोड कहां रखा जाए और आने वाले और बाहर जाने वाले अनुरोध और प्रतिक्रिया से वास्तविक JSON या XML कैसे प्राप्त करें।

मेरी नियंत्रक पोस्ट विधि:

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

}

क्या आप किसी विशेष कंट्रोलर, किसी सेट, या किसी विशेष कंट्रोलर में आपके सभी कार्यों के लिए रिक्वेस्ट / रिस्पांस लॉग करना चाहते हैं?
LB2

केवल पोस्ट लॉगिंग में रुचि है। (ए) एक्सएमएल या जोंस के पोस्ट (बी) बॉडी का समय (सी) प्रतिक्रिया (एक्सएमएल या
जसन

कारण मैं पूछ रहा था कि सुझाव देना है कि क्या सीधे कार्रवाई में कोड डाला जाए, या सभी कार्यों के सामान्य समाधान। नीचे मेरा जवाब देखें।
LB2

FYI करें मैंने asp.net को हटा दिया क्योंकि यह इस सवाल पर असर नहीं कर रहा है
Dalorzo

फाइलर बनाना एक विकल्प नहीं है?
प्रेरक के।

जवाबों:


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खतरनाक एपीआई हैं। यह कहीं बेहतर होगा उपयोग करने के लिए await, बजाय यानी,var result = await base.SendAsync(request, cancellationToken); var resposeBody = await response.Content.ReadAsStringAsync(); Trace.WriteLine(responseBody); return response;
स्टीफन Cleary

9
यह एक बहुत अच्छा समाधान है, हालांकि यह एक त्रुटि फेंक देगा जब प्रतिक्रिया में कोई शरीर नहीं होता है। लेकिन यह आसान जाँच और ठीक करने के लिए काफी आसान है :)
11

6
क्या कॉल में await request.Content.ReadAsStringAsync();यह कहते हुए त्रुटि नहीं होती है कि अनुरोध धारा को पहले से ही कुछ विशेष परिस्थितियों में पढ़ा गया है?
गैविन

6
यदि प्रतिनिधि हैंडलर अनुरोध के शरीर को पढ़ता है, तो क्या यह वास्तविक टर्मिनल हैंडलर (यानी mvc / webapi) के लिए अनुपलब्ध नहीं होगा?
LB2

15

प्रत्येक WebAPI विधि कॉल के लिए अनुरोध / प्रतिक्रिया लॉगिंग को सामान्य रूप से संभालने के लिए कई तरीके हैं:

  1. ActionFilterAttribute: एक कस्टम लिख सकता है ActionFilterAttributeऔर लॉगिंग को सक्षम करने के लिए कंट्रोलर / एक्शन के तरीकों को सजा सकता है ।

    Con: आपको प्रत्येक नियंत्रक / विधियों को सजाने की आवश्यकता है (फिर भी आप इसे आधार नियंत्रक पर कर सकते हैं, लेकिन फिर भी यह क्रॉस कटिंग चिंताओं को संबोधित नहीं करता है।

  2. ओवरराइड BaseControllerऔर हैंडल लॉगिंग वहाँ।

    Con: हम एक कस्टम बेस कंट्रोलर से नियंत्रकों की अपेक्षा / मजबूर कर रहे हैं।

  3. का उपयोग कर DelegatingHandler

    लाभ: हम इस दृष्टिकोण के साथ यहां नियंत्रक / विधि को नहीं छू रहे हैं। प्रतिनिधि हैंडलर अलगाव में बैठता है और इनायत से अनुरोध / प्रतिक्रिया लॉगिंग को संभालता है।

अधिक indepth लेख के लिए, इस http://weblogs.asp.net/fredriknormen/log-message-request-and-response-in-asp-net-webapi देखें ।


आप निम्नानुसार कोई भी एक्शनफिल्टर निर्दिष्ट करने में सक्षम हैं: सार्वजनिक स्थैतिक वर्ग WebApiConfig {सार्वजनिक स्थैतिक शून्य रजिस्टर (HttpConfiguration config) {// वेब API कॉन्फ़िगरेशन और सेवाएँ config.Filters.Add (नया Mylilter ()) // वेब एपीआई मार्ग config.MapHttpAttributeRoutes (); config.Routes.MapHttpRoute (नाम: "DefaultApi", pathTemplate: "api / {कंट्रोलर} / {id}", चूक: नया {id = RouteParameter.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 को ओवरराइड करना होगा। समस्या यह है कि उस बिंदु पर अनुरोध पहले से ही xml या json होने के बजाय मेरे POCO में परिवर्तित हो गया है। कोई विचार?
user2315985

मूल रूप से मेरा मतलब था, OnActionExecuting में डेटा लॉग इन करें और फिर पोस्ट को अपना काम करने दें। जिसे मैं आपके सवाल से समझ गया था कि आप बस किए गए प्रत्येक पोस्ट के लिए डेटा लॉग इन करना चाहते हैं।
प्रेरक के

3
मैं हर बार किसी पोस्ट के अनुरोध और प्रतिक्रिया डेटा दोनों को लॉग इन करना चाहता हूं।
user2315985

2
आप OnActionExecuted का उपयोग कर सकते हैं और कोशिश कर सकते हैं "(actionExecutedContext.ActionContext.Response.Content as ObjectContent) ।Value.ToString ()" प्रतिक्रिया प्राप्त करने के लिए और इसे लॉग इन करें।
प्रेरक के।

OnActionExecuted के भीतर मुझे अनुरोध कैसे मिलेगा?
user2315985

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. इनबाउंड अनुरोध पर नज़र रखने के लिए एपीकंट्रोलर पद्धति के एक्ज़ीक्यूटासंस्क ओवरराइड, मेरे समाधान में मैं अपने प्रोजेक्ट के एपीआई नियंत्रकों के माता-पिता के रूप में Base_ApiController बनाता हूं।
  2. एपीआई नियंत्रक की आउटबाउंड प्रतिक्रिया को ट्रैक करने के लिए System.Web.Http.Filters.ActionFilterAttribute का उपयोग करें
  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 Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.