2012-10-18 59 views
7

मैं एएसपी.NET वेब एपीआई प्रोजेक्ट पर काम कर रहा हूं और इसे यूआरएल में संस्करण की जानकारी स्वीकार करता हूं।वेब एपीआई - नियंत्रक के बाहर पैरामीटर्स का अनुरोध

उदाहरण के लिए:

  • API/v1/MyController
  • API/v2/MyController

अब मैं अनुरोध संस्करण V1, V2 एक कस्टम के अंदर प्राप्त करना चाहते हैं Nlog के लिए लेआउट रेन्डरर। आम तौर पर मैं इसे नीचे दिए गए उदाहरण की तरह करता हूं।

[LayoutRenderer("Version")] 
public class VersionLayoutRenderer : LayoutRenderer 
{ 
    protected override void Append(System.Text.StringBuilder builder, NLog.LogEventInfo logEvent) 
    { 
     var version = HttpContext.Current.Request.RequestContext.RouteData.Values["Version"]; 
     builder.Append(version); 
    } 
} 

समस्या:HttpContext.Current शून्य है

मेरा मानना ​​है कि इस वजह से मैं NLog के लिए Async wrappers का उपयोग करें और लॉगर से पहले कुछ कॉल भी Async हैं।

लॉगर का एक उदाहरण Ninject.Extensions.WebApi.UsageLogger के अंदर Async कहा जाता है। इस बिंदु पर HttpRequestMessage में सभी जानकारी है जो हमें संस्करण प्राप्त करने के लिए आवश्यक है।

/// <summary> 
/// Initializes a new instance of the <see cref="UsageHandler" /> class. 
/// </summary> 
public UsageHandler() 
{ 
    var kernel = new StandardKernel(); 

    var logfactory = kernel.Get<ILoggerFactory>(); 

    this.Log = logfactory.GetCurrentClassLogger(); 
} 

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     var startTime = DateTime.Now; 

     // Log request 
     await request.Content.ReadAsStringAsync().ContinueWith(c => 
      { 
       this.Log.Info("{0}: {1} called from {2}", request.Method, HttpUtility.UrlDecode(request.RequestUri.AbsoluteUri), ((HttpContextBase)request.Properties["MS_HttpContext"]).Request.UserHostAddress); 
       this.Log.Info("Content-Type: {0}, Content-Length: {1}", request.Content.Headers.ContentType != null ? request.Content.Headers.ContentType.MediaType : string.Empty, request.Content.Headers.ContentLength); 
       this.Log.Info("Accept-Encoding: {0}, Accept-Charset: {1}, Accept-Language: {2}", request.Headers.AcceptEncoding, request.Headers.AcceptCharset, request.Headers.AcceptLanguage); 

       if (!string.IsNullOrEmpty(c.Result)) 
       { 
        if (this.MaxContentLength > 0 && c.Result.Length > this.MaxContentLength) 
        { 
         this.Log.Info("Data: {0}", HttpUtility.UrlDecode(c.Result).Substring(0, this.MaxContentLength - 1)); 
        } 
        else 
        { 
         this.Log.Info("Data: {0}", HttpUtility.UrlDecode(c.Result)); 
        } 
       } 
      }); 

     var response = await base.SendAsync(request, cancellationToken); 

     // Log the error if it returned an error 
     if (!response.IsSuccessStatusCode) 
     { 
      this.Log.Error(response.Content.ReadAsStringAsync().Result); 
     } 

     // Log performance 
     this.Log.Info("Request processing time: " + DateTime.Now.Subtract(startTime).TotalSeconds + "s"); 

     return response; 
    } 

सवाल क्या एक सामान्य तरह से VersionLayoutRenderer काम करने के लिए सबसे अच्छा तरीका क्या होगा? क्या मैं एक संदेश हैडलर जोड़ सकता हूं और कुछ Async गुंजाइश के लिए HttpRequest बांध सकता हूं? यदि ऐसा है तो किसी भी दिशानिर्देश की बहुत सराहना की जाएगी क्योंकि मैं अभी भी Ninject पर उपयोग कर रहा हूं।

समय के लिए मैं उपयोग जानकारी को सीधे उपयोग कॉल में लॉग कॉल में जोड़ता हूं, लेकिन मुझे वास्तव में एक अधिक सामान्य समाधान पसंद आएगा, जहां मैं हमेशा अपने लॉगिंग के अंदर संस्करण जानकारी पर भरोसा कर सकता हूं।

संपादित करें: अधिक विशिष्ट होने के लिए प्रश्न को अपडेट किया गया और अधिक जानकारी शामिल की गई।

+1

क्या आप कोड पोस्ट कर सकते हैं जहां आप async – Jonathan

+0

जोनाथन का उपयोग कर रहे हैं, कृपया अपडेट किए गए प्रश्न को देखें, मुझे उम्मीद है कि इसमें आपकी सारी जानकारी शामिल है और अन्यथा कृपया पूछें। –

उत्तर

1

वास्तविक समस्या वास्तव में तटस्थ wrt है जो आपको निनजेक्ट के साथ क्या करना चाहिए - आपको बस अपनी प्रसंस्करण का चरण प्राप्त करने की आवश्यकता है जैसे कि एसिंक चलाने वाले किसी भी ऑब्जेक्ट में जादू HttpContext.Current पर भरोसा किए बिना सब कुछ चाहिए। पहले डीआई कंटेनर के साथ काम कर लें।

फिर, Ninject उपयोग करने के लिए प्रमुख कदम हैं: -

  1. आपका Bind बयान एक बार चलाने के लिए की जरूरत है। सबसे अच्छा दृष्टिकोण के लिए Ninject.MV3 विकी देखें (जब तक यह विलय नहीं हो जाता है, तब तक Nuotet- आधारित संस्करण के साथ ओओटीबी नहीं है)

  2. @rickythefox (+ 1'd) के रूप में कहते हैं, आपके पंजीकरण को थ्रेड को सेंकना चाहिए वस्तु में/संदर्भ रिश्तेदार डेटा और आप पंजीकरण config ऐसी है कि वह अनुरोध प्रसंस्करण के शुरू में हो सकता है, जब आप धागा है कि पर हों HttpContext.Current

    kernel.Bind<ILogger>() 
    // TODO replace GCCL with something like GetClassLogger(ctx.Request.Service.ReflectedType) - see the wiki for examples 
        .ToMethod(ctx=> ctx.Get<ILoggerFactory>().GetCurrentClassLogger()) 
        .InRequestScope() 
        .WithConstructorArgument("context",c=>HttpContext.Current); 
    

तो बस के निर्माता बनाना हैंडलर ILogger लें, जिसे .Log पर असाइन किया जा सकता है (जो मुझे उम्मीद है कि static नहीं है: डी)

एनबी, लक्ष्य यह है कि आप कभी भी kernel.Get(), कभी भी अवधि नहीं लिखना चाहते हैं।

यहाँ वास्तविक समस्या हालांकि, कि WebAPI के समुचित उपयोग HttpContext.Current या किसी अन्य जादू static तरीकों या इसी तरह कुछ भी (testability के लिए, अपने आप को होस्टिंग संदर्भ से स्वतंत्र बनाने के लिए (स्वयं की मेजबानी का उपयोग कर शामिल नहीं करता है, Owin आदि), और कई और कारण)।

इसके अलावा, यदि आप एनएलओजी (या लॉग 4नेट) का उपयोग कर रहे हैं तो आपको Ninject.Extensions.Logging पैकेज (और स्रोत) भी देखना चाहिए।

+0

रूबेन आपके उत्तर के लिए धन्यवाद। मैंने इसे स्वीकार कर लिया क्योंकि यह रचनात्मक है और मुझे एहसास हुआ कि मैं जिस तरह से सोच रहा था उसके बजाय मैं नहीं जाऊंगा।जब मैं लॉगिंग कर रहा था तो मैं कुछ सामान्य डेटा रखने के लिए 'जेनेरिक' समाधान चाहता था। लेकिन चूंकि यह डेटा हमेशा उपलब्ध नहीं होता है, यह हमेशा थोड़ा मुश्किल होगा। मैं सिर्फ एक संदेशवाहक बनाउंगा जो कुछ कॉल जानकारी को नलॉग के साथ डेटाबेस में लिखता है। इसके अलावा मैं 'Ninlog.Extensions.Logging' का उपयोग' Nlog2' पैकेज के साथ कर रहा हूं, ये बेहतरीन पैकेज हैं। –

+0

@ जोसविंके: सुनने के लिए अच्छा है कि आप क्रमबद्ध हैं। यह सुनिश्चित करना मुश्किल हो सकता है कि जब कंटेनर आपके लिए सभी प्रकार की चाल करने की पेशकश कर रहा है तो सरल समाधान ढूंढना मुश्किल हो सकता है! ... क्या आपने अभी तक http://manning.com/seemann पढ़ा है? यदि आप ऐसा करते हैं तो इन तरह के प्रश्न आपके लिए मुश्किल से उत्पन्न होंगे - यह एक डी कंटेनर के लिए '200 पेज मैनुअल' से कहीं अधिक है, ऐसा लगता है कि यह हो सकता है कि अगर आपने मार्क सीमैन के शीर्ष रेटेड पोस्ट को यहां पर पढ़ा नहीं है तो ... –

+0

इसे पढ़ा नहीं, बस इसे आदेश दिया। मैं उत्सुक हूं लेकिन यह निश्चित रूप से एक अच्छी किताब की तरह दिखता है। वैसे भी सलाह और प्रतिक्रिया के लिए धन्यवाद। –

2

की तरह कुछ का उपयोग कर संदर्भ इंजेक्शन लगाने का प्रयास करें:

kernel.Bind<IDependency>() 
    .To<Mydependency>() 
    .InRequestScope() 
    .WithConstructorArgument("context",c=>HttpContext.Current); 
+0

आपके उत्तर के लिए धन्यवाद, लेकिन क्या आप इस बारे में थोड़ा और विशिष्ट हो सकते हैं कि आप इसे कैसे कार्यान्वित करेंगे? मैं निनजेक्ट के लिए नया हूं और थोड़ा और मदद की सराहना की जाएगी! 'निर्भरता' को 'माई निर्भरता' से बांधने का सबसे अच्छा स्थान क्या होगा? क्या मुझे एक नया 'संदेश हैंडलर' बनाना चाहिए और प्रत्येक कॉल को HttpContext को दोबारा जोड़ना चाहिए? –

+0

मुझे पता चला कि क्यों मेरी निर्भरता को अनुमानित इंजेक्शन नहीं दिया जाता है, कॉल एसिंक घटना से आती हैं। किसी भी मदद की अभी भी बहुत सराहना की जाएगी। –

0

GlobalConfiguration वर्ग आप रूटिंग कॉन्फ़िगरेशन के लिए पहुँच प्रदान कर सकते हैं।

// The code below assumes a map routing convention of api/{version}/{controller}/.... 

// This will give you the configured routes 
var routes  = GlobalConfiguration.Configuration.Routes; 

// This will give you the route templates 
var templates = routes 
    .Select(route => route.RouteTemplate); 

// This will give you the distinct versions for all controllers 
var versions = routes 
    .Select(route => route.RouteTemplate) 
    .Select(template => template.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) 
    .Select(values => values[1]) 
    .Distinct(); 

// This will give you the distinct versions for a controller with the specified name 
var name    = "MyController"; 

var controllerVersions = routes 
    .Select(route => route.RouteTemplate) 
    .Select(template => template.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) 
    .Where(values => String.Equals(values[2], name, StringComparison.OrdinalIgnoreCase)) 
    .Select(values => values[1]) 
    .Distinct(); 

मैं अगर आप एक ज्ञात मूल्य (नियंत्रक का नाम) के साथ संस्करण को हल करने की कोशिश कर रहे हैं या यदि आप गतिशील रूप से इसे हल करने की कोशिश कर रहे यकीन नहीं है। यदि आप वर्तमान HttpContext इंजेक्ट करते हैं, तो आप रूट टेम्पलेट के माध्यम से मार्गों को फ़िल्टर करने के लिए संदर्भ के अनुरोध के यूआरआई का उपयोग कर सकते हैं।

संपादित करें: आपकी टिप्पणियों के बाद मुझे एहसास हुआ कि रूटिंग कॉन्फ़िगरेशन वह नहीं है जो आप बाद में थे।

यदि अंतिम लक्ष्य आपके नियंत्रकों के भीतर लॉगिंग को लागू करना है, तो आप Tracing in ASP.NET Web API पर एक नज़र डालना चाहेंगे क्योंकि वेब एपीआई इंफ्रास्ट्रक्चर में अंतर्निहित ट्रेसिंग के लिए समर्थन है।

+0

आपके उत्तर के लिए बहुत बहुत धन्यवाद, लेकिन (अगर मैं गलत हूं तो मुझे सही करें) मुझे नहीं लगता कि यह मुझे वर्तमान अनुरोध के लिए संस्करण प्राप्त करने में मदद करता है। मैंने सोचा कि मेरा सवाल इस बारे में थोड़ा अस्पष्ट हो सकता है इसलिए मैंने इसे संपादित किया। –

+0

मुझे लगता है कि मैंने तब गलत समझा। यदि आपको वर्तमान अनुरोध की आवश्यकता है तो प्रस्तुतकर्ता में HttpRequestMessage या HttpContext इंजेक्शन करना सबसे अधिक समझ में आता है; तो आपको अनुरोध यूआरआई के बाहर संस्करण को पार्स करना होगा। – Oppositional

+0

एसिंक कॉल के बाद मुझे HttpRequestMessage या HttpContext को इंजेक्ट करने के बारे में कोई विचार? अनुरोध से संस्करण को पार्स करना समस्या नहीं है जो मेरे पास है। –