Application_Error नहीं फायरिंग जब customerrors = "चालू"


124

मेरे पास global.asaxफ़ाइल के Application_Errorईवेंट में कोड है जो एक त्रुटि होने पर निष्पादित होता है और त्रुटि का विवरण खुद को ईमेल करता है।

void Application_Error(object sender, EventArgs e)
{
    var error = Server.GetLastError();

    if (error.Message != "Not Found")
    {
        // Send email here...
    }

}

यह ठीक काम करता है जब मैं इसे विजुअल स्टूडियो में चला रहा हूं, हालांकि जब मैं हमारे लाइव सर्वर पर प्रकाशित करता हूं तो Application_Errorघटना आग नहीं बनती।

कुछ परीक्षण के बाद , Application_Errorजब मैं सेट करता हूं तो मैं फायरिंग प्राप्त कर सकता हूं customErrors="Off", हालांकि customErrors="On"घटना को फिर से फायरिंग से रोकने के लिए इसे वापस सेट कर सकता हूं ।

क्या कोई सुझाव दे सकता है कि सक्षम Application_Errorहोने पर फायरिंग क्यों नहीं होगी ?customErrorsweb.config


मुझे भी बिलकुल यही समस्या हो रही है। मुझे यह SO प्रश्न भी मिला: stackoverflow.com/questions/3713939/… जो IIS7 सर्वर को क्लासिक मोड में डालने का सुझाव देता है। दुर्भाग्य से यह हमारे लिए कोई विकल्प नहीं है। किसी के पास कोई बेहतर उपाय है?
जेसी वेब

यहां एक और संबंधित प्रश्न है और यह उत्तर है (जिनमें से कोई भी स्वीकार नहीं किया गया है) Application_Error () का उपयोग नहीं करने का सुझाव देते हैं ... stackoverflow.com/questions/1194578/…
Jesse Webb

@Gweebz मैंने इस बारे में एक उत्तर पोस्ट किया है कि मुझे यह कैसे मिला, लेकिन मुझे अभी भी कोई ठोस दस्तावेज नहीं मिला है कि मुझे यह व्यवहार क्यों मिल रहा है।
डब्ल्यूडीफ्री

मैंने एक उत्तर जोड़ा, जिसमें बताया गया है कि Application_Error()विधि क्यों लागू नहीं हो रही थी मैंने अपना अंतिम समाधान भी बताया।
जेसी वेब

कस्टम त्रुटियों को काम करने के लिए वास्तव में इस लेख की सिफारिश करें
थॉमसआर्डल

जवाबों:


133

अद्यतन
जब से यह उत्तर एक समाधान प्रदान करता है, मैं इसे संपादित नहीं करूंगा, लेकिन मैंने इस समस्या को हल करने का एक बहुत ही स्वच्छ तरीका पाया है। देखें मेरे अन्य जवाब जानकारी के लिए ...

मूल उत्तर:
मुझे पता लगा कि Application_Error()विधि को क्यों लागू नहीं किया जा रहा है ...

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute()); // this line is the culprit
    }
...
}

डिफ़ॉल्ट रूप से (जब एक नई परियोजना उत्पन्न होती है), एक एमवीसी एप्लिकेशन में Global.asax.csफ़ाइल में कुछ तर्क होते हैं । इस तर्क का उपयोग मार्गों को मैप करने और फ़िल्टर को पंजीकृत करने के लिए किया जाता है। डिफ़ॉल्ट रूप से, यह केवल एक फ़िल्टर पंजीकृत करता है: एक HandleErrorAttributeफ़िल्टर। जब CustomErrors चालू होता है (या दूरस्थ अनुरोधों के माध्यम से जब इसे RemoteOnly पर सेट किया जाता है), HandleErrorAttribute MVC को त्रुटि दृश्य देखने के लिए कहता है और यह कभी भी Application_Error()विधि को नहीं कहता है । मुझे इसका दस्तावेज़ीकरण नहीं मिला लेकिन प्रोग्रामर.स्टैकएक्सचेंज डॉट कॉम पर इस उत्तर में बताया गया है

ApplicationError () विधि को हर अखंडित अपवाद के लिए कहा जाता है, सरल लाइन को हटा दें जो HandleErrorAttribute फ़िल्टर को पंजीकृत करता है।

अब समस्या यह है कि आप जो चाहते हैं उसे प्राप्त करने के लिए customErrors को कैसे कॉन्फ़िगर करें ...

CustomErrors खंड के लिए चूक redirectMode="ResponseRedirect"। आप MVC मार्ग होने के लिए defaultRedirect विशेषता को भी निर्दिष्ट कर सकते हैं। मैंने एक ErrorController बनाया जो बहुत सरल था और इस तरह दिखने के लिए मैंने अपना web.config बदल दिया ...

web.config

<customErrors mode="RemoteOnly" redirectMode="ResponseRedirect" defaultRedirect="~/Error">
  <error statusCode="404" redirect="~/Error/PageNotFound" />
</customErrors>

इस समाधान के साथ समस्या यह है कि यह आपके त्रुटि URL में एक 302 रीडायरेक्ट करता है और फिर वे पृष्ठ 200 स्थिति कोड के साथ प्रतिक्रिया करते हैं। इससे Google उन त्रुटि पृष्ठों को अनुक्रमित करता है जो खराब हैं। यह HTTP युक्ति के बहुत अनुरूप नहीं है। मैं जो करना चाहता था वह पुनर्निर्देशित नहीं था, और अपने कस्टम त्रुटि विचारों के साथ मूल प्रतिक्रिया को ओवररिट कर दिया।

मैंने बदलने की कोशिश की redirectMode="ResponseRewrite"। दुर्भाग्य से, यह विकल्प MVC मार्गों का समर्थन नहीं करता है , केवल स्थैतिक HTML पृष्ठ या ASPX। मैंने पहली बार एक स्थिर HTML पृष्ठ का उपयोग करने की कोशिश की लेकिन प्रतिक्रिया कोड अभी भी 200 था लेकिन, कम से कम यह पुनर्निर्देशित नहीं हुआ। मुझे तब इस उत्तर से एक विचार आया ...

मैंने त्रुटि से निपटने के लिए एमवीसी को छोड़ने का फैसला किया। मैंने Error.aspxa और a बनाया PageNotFound.aspx। ये पृष्ठ बहुत सरल थे लेकिन इनमें एक जादू था ...

<script type="text/C#" runat="server">
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        Response.StatusCode = (int) System.Net.HttpStatusCode.InternalServerError;
    }
</script>

यह ब्लॉक पेज को सही स्थिति कोड के साथ परोसा जाना बताता है। मोटे तौर पर, PageNotFound.aspx पेज पर, मैंने HttpStatusCode.NotFoundइसके बजाय इस्तेमाल किया। मैंने अपने web.config को इस तरह से बदला ...

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite" defaultRedirect="~/Error.aspx">
  <error statusCode="404" redirect="~/PageNotFound.aspx" />
</customErrors>

यह सब पूरी तरह से काम किया!

सारांश:

  • लाइन निकालें: filters.Add(new HandleErrorAttribute());
  • Application_Error()अपवादों को लॉग करने के लिए विधि का उपयोग करें
  • ASPX पृष्ठों पर इंगित करते हुए, ResponseRewrite के साथ customErrors का उपयोग करें
  • ASPX पृष्ठों को अपनी प्रतिक्रिया स्थिति कोड के लिए जिम्मेदार बनाएं

इस समाधान के साथ मैंने कुछ युगल देखे हैं।

  • ASPX पेज रेज़र टेम्प्लेट के साथ कोई भी मार्कअप साझा नहीं कर सकते हैं, मुझे लगातार नज़र और महसूस के लिए हमारी वेबसाइट के मानक हेडर और फ़ूटर मार्कअप को फिर से लिखना पड़ा।
  • * .Aspx पृष्ठों को सीधे उनके URL को मारकर एक्सेस किया जा सकता है

इन समस्याओं के लिए काम के आस-पास हैं, लेकिन मैं उनके द्वारा किसी भी अतिरिक्त काम करने के लिए पर्याप्त चिंतित नहीं था।

मुझे आशा है कि यह सभी की मदद करता है!


2
+1! वास्तव में अच्छा वर्कअराउंड लेकिन, एमवीसी को एस्पक्स पन्नों के साथ मिलाने के क्या परिणाम हो सकते हैं?
डिएगो

2
हम कुछ महीनों के लिए PROD में यह कर चुके हैं और मुझे नकारात्मक प्रभाव नहीं मिला है। एकमात्र 'गेटा' यह था कि हमें अपने CI को बदलना था और एक AspCompile MSBUILD कार्य करने के लिए तैनात करना था क्योंकि MVC को इसकी आवश्यकता नहीं है, लेकिन जब हमने .as फ़ाइलों को जोड़ा , तो उन्हें इसकी आवश्यकता थी। अन्य समस्याएं हो सकती हैं लेकिन वे अभी तक सामने नहीं आए हैं ...
जेसी वेबब

मैं MVC4 में इस दृष्टिकोण का उपयोग करने की कोशिश कर रहा हूं, और जाहिर है कि यह केवल मेरे लिए काम करता है जब मुझे मिला <customErrors mode="Off" />filters.Add(new HandleErrorAttribute());हटाए जाने या न होने का कोई प्रभाव नहीं पड़ता है।
ग्रेज़गोरज़ सोलावेकी

Aspx पेज पर आप उस ऐजैक्स कॉल को जोड़ सकते हैं जिसे आप दिखाना चाहते हैं। डुप्लिकेट कोड होने से बचाता है
माइक

71

मैंने एक ExceptionFilter बनाकर और Application_Error के बजाय वहां त्रुटि लॉग करके इसे हल किया। आपको बस रजिस्टरग्लोबलफिल्टर में एक कॉल जोड़ना होगा

log4netExceptionFilter.cs

using System
using System.Web.Mvc;

public class log4netExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        Exception ex = context.Exception;
        if (!(ex is HttpException)) //ignore "file not found"
        {
            //Log error here
        }
    }
}

Global.asax.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new log4netExceptionFilter()); //must be before HandleErrorAttribute
    filters.Add(new HandleErrorAttribute());
}

6
+1 यदि यह उत्तर काम करता है, तो यह सुनिश्चित हो जाता है कि त्रुटियों को संभालने के लिए मुझे सबसे सुरुचिपूर्ण और संभवतः इच्छित तरीका (एमवीसी रूपरेखा द्वारा) प्रतीत होता है।
मैट हैस्मिथ

2
यह मेरे लिए पूरी तरह से काम करता है और यहाँ स्वीकार किए गए उत्तर की तुलना में बहुत अधिक है। अच्छा!
एलेक्स वॉरेन

3
यह लॉगिंग अपवादों की समस्या को हल करने के लिए काम करेगा। आपने कस्टम त्रुटि पृष्ठ दिखाने के साथ इसे कैसे संयोजित किया?
जेसी वेब

3
मैंने यह कोशिश की और इसने 404 के मामले को छोड़कर अच्छी तरह से काम किया। 404 के लिए, यह Errors.cshtml दृश्य नहीं दिखाएगा, यह केवल मुझे YSoD देगा। यदि कस्टम 404 की आवश्यकता नहीं है, तो यह समाधान निश्चित रूप से क्लीनर है!
जेसी वेब

1
मुझें यह पसंद है। 'CustomErrors = पर' के साथ काम करता है
Illidan

36

मुझे एक ऐसा लेख मिला, जो एमवीसी 3 वेब ऐप में कस्टम एरर पेज बनाने के बहुत साफ-सुथरे तरीके का वर्णन करता है, जो अपवादों को लॉग इन करने की क्षमता को रोकता नहीं है।

समाधान अनुभाग के <httpErrors>तत्व का उपयोग करना है <system.webServer>

मैंने अपना Web.config जैसे कॉन्फ़िगर किया है ...

<httpErrors errorMode="DetailedLocalOnly" existingResponse="Replace">
  <remove statusCode="404" subStatusCode="-1" />
  <remove statusCode="500" subStatusCode="-1" />
  <error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" />
  <error statusCode="500" path="/Error" responseMode="ExecuteURL" />
</httpErrors>

मैंने भी कॉन्फ़िगर customErrorsकिया है mode="Off"(जैसा कि लेख द्वारा सुझाया गया है)।

यह एक ErrorController के कार्यों द्वारा प्रतिक्रियाओं को ओवरराइड करता है। यह है कि नियंत्रक:

public class ErrorController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult NotFound()
    {
        return View();
    }
}

विचार बहुत सीधे हैं, मैंने पृष्ठों को बनाने के लिए सिर्फ मानक रेजर सिंटैक्स का उपयोग किया है।

अकेले ही आपको MVC के साथ कस्टम त्रुटि पृष्ठों का उपयोग करने के लिए पर्याप्त होना चाहिए।

मुझे अपवादों के लॉगिंग की भी आवश्यकता थी इसलिए मैंने एक कस्टम अपवादक का उपयोग करके मार्क के समाधान को चुरा लिया ...

public class ExceptionPublisherExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext exceptionContext)
    {
        var exception = exceptionContext.Exception;
        var request = exceptionContext.HttpContext.Request;
        // log stuff
    }
}

आखिरी चीज़ जो आपको करने की ज़रूरत है, वह है अपनी Global.asax.cs फ़ाइल में अपवाद फ़िल्टर को पंजीकृत करना :

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new ExceptionPublisherExceptionFilter());
    filters.Add(new HandleErrorAttribute());
}

यह मेरे पिछले उत्तर की तुलना में बहुत अधिक क्लीनर समाधान की तरह लगता है और जहां तक ​​मैं बता सकता हूं, बस काम करता है। मुझे यह विशेष रूप से पसंद है क्योंकि ऐसा नहीं लगता था कि मैं एमवीसी ढांचे के खिलाफ लड़ रहा था; यह समाधान वास्तव में इसका लाभ उठाता है!


6
यह उल्लेखनीय है कि मुझे लगता है कि यह समाधान केवल IIS 7 और नए में काम करता है; httpErrors तत्व को हाल ही में जोड़ा गया था।
जेसी वेब

यह उत्तर मेरे काम नहीं आया, यह मेरे पास है, यह मुझे एक भयानक नीला खेल देता है: HTTP Error 500.0 - Internal Server Error The page cannot be displayed because an internal server error has occurred. त्रुटि स्क्रीन, मेरा अनुरोधित /Error/Indexपृष्ठ नहीं
सर्ज सगन

@SerjSagan मैंने अभी एक नए प्रोजेक्ट में कदमों का परीक्षण करने की कोशिश की और यह ठीक काम करता है। क्या आप IIS, IIS एक्सप्रेस या VS देव सर्वर का उपयोग कर रहे हैं? यह वीएस देव सर्वर में काम नहीं करेगा। जब मैंने अपना प्रोजेक्ट वीएस देव सर्वर का उपयोग करने के लिए सेट किया था, तो आईआईएस नहीं, मैंने कस्टम त्रुटि पृष्ठों के बजाय येलो-स्क्रीन त्रुटियों को देखा।
जेसी वेब वेब

1
@SerjSagan लेकिन ऐसा लगता है कि आप क्लासिक ब्लू-स्क्रीन त्रुटियों के विपरीत IIS ब्लू-स्क्रीन त्रुटियों को प्राप्त कर रहे हैं। इससे मुझे लगता है कि आप IIS के कुछ रूप का उपयोग कर रहे हैं। यदि ऐसा है, तो पहले वाक्य में विशेष रूप से अंतिम अनुभाग शीर्षक से जुड़े लेख के माध्यम से पढ़ें, जिसका शीर्षक है: "कुछ महत्वपूर्ण नोट्स"। यह कहता है:To able to overwrite global settings in global IIS settings (file: C:\Windows\System32\inetsrv\config \applicationHost.config) should be: <section name="httpErrors" overrideModeDefault="Allow" />
जेसी वेब वेब

3
@SerjSagan को बदलने के लिए त्रुटि। विस्तार से या कस्टम आपके पृष्ठ को दिखाएगा।
डैनियल पी

8

ASP.NET MVC5 उपयोग के मामले में

public class ExceptionPublisherExceptionFilter : IExceptionFilter
    {
        private static Logger _logger = LogManager.GetCurrentClassLogger();
        public void OnException(ExceptionContext exceptionContext)
        {
            var exception = exceptionContext.Exception;
            var request = exceptionContext.HttpContext.Request;
            // HttpException httpException = exception as HttpException;
            // Log this exception with your logger
            _logger.Error(exception.Message);
        }
    }

आप में पा सकते हैं FilterConfig.csके App_Startफ़ोल्डर।


1

मुझे ExceptionFilter के साथ मार्क का जवाब पसंद है, लेकिन एक अन्य विकल्प, यदि आपके पास आपके सभी नियंत्रक एक ही आधार नियंत्रक से प्राप्त होते हैं, तो बस आपके आधार नियंत्रक में OnException को ओवरराइड करना है। आप वहां अपनी लॉगिंग और ईमेल कर सकते हैं। यह आपके IoC कंटेनर के साथ आपके आधार नियंत्रक में पहले से ही जो भी निर्भरताएँ हैं, उनका उपयोग करने में सक्षम होने का लाभ है।

आप अभी भी IExceptionFilter के साथ अपने IoC का उपयोग कर सकते हैं, लेकिन यह आपके बाइंडिंग को कॉन्फ़िगर करने के लिए थोड़ा अधिक मुश्किल है।


0

जहाँ तक मुझे पता है, आप url पैरामीटर में निर्दिष्ट पृष्ठ पर नियंत्रण दे रहे हैं और आपका ईवेंट नोटीफ़िकेशन के बजाय यहाँ बैठेगा

<customErrors defaultRedirect="myErrorPage.aspx"
              mode="On">
</customErrors>

बहुत सारी जानकारी यहाँ मिल सकती है: http://support.microsoft.com/kb/306355


धन्यवाद क्रिस, मैंने दस्तावेज़ीकरण पढ़ा और यह बताता है कि जब कोई अनहेल्ड त्रुटि होती है, तो Application_Error कहा जाएगा (जो मुझे उम्मीद है) और जो Server.ClearError () को web.config नहीं कहा जाना चाहिए। कस्टम अनुभाग अंतिम हैंडल बिंदु होगा। हालाँकि, customErrors को सक्षम करना वह है जो Application_Error को फायरिंग से रोक रहा है।
डब्ल्यूडीएफ

आपके द्वारा जोड़ा गया दस्तावेज़ ASP.NET अनुप्रयोग के बारे में बात कर रहा है और ऐसा प्रतीत होता है कि MVC3 वेब अनुप्रयोग भिन्न व्यवहार कर रहे हैं।
जेसी वेब

0

इसके आस-पास जाने के लिए मैंने कस्टमाइज़र्स को अक्षम छोड़ दिया और Global.asax में Application_Error इवेंट से सभी त्रुटियों को हैंडल किया। यह MVC के साथ थोड़ा मुश्किल है क्योंकि मैं 301 रीडायरेक्ट वापस नहीं करना चाहता, मैं उपयुक्त त्रुटि कोड वापस करना चाहता था। अधिक जानकारी मेरे ब्लॉग पर http://www.wduffy.co.uk/blog/use-application_error-in-asp-net-mvcs-global-asax-to-handle-errors/ पर देखी जा सकती है लेकिन अंतिम कोड है निचे सूचीबद्ध...

void Application_Error(object sender, EventArgs e)
{
    var error = Server.GetLastError();
    var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500;

    if (code != 404)
    {
            // Generate email with error details and send to administrator
    }

    Response.Clear();
    Server.ClearError();

    string path = Request.Path;
    Context.RewritePath(string.Format("~/Errors/Http{0}", code), false);
    IHttpHandler httpHandler = new MvcHttpHandler();
    httpHandler.ProcessRequest(Context);
    Context.RewritePath(path, false);
}

और यहाँ नियंत्रक है

public class ErrorsController : Controller
{

    [HttpGet]
    public ActionResult Http404(string source)
    {
            Response.StatusCode = 404;
            return View();
    }

    [HttpGet]
    public ActionResult Http500(string source)
    {
            Response.StatusCode = 500;
            return View();
    }

}

मैंने आपके समाधान की कोशिश की लेकिन यह मेरे काम नहीं आया। लाइनों के साथ Response.StatusCode = ###;, यह बिल्ट-इन MVC त्रुटि पृष्ठों को दिखा रहा था C:\inetpub\custerr\en-US। मुझे अपने Application_Error () पद्धति से HttpHandlers या नियंत्रकों को मैन्युअल रूप से आमंत्रित करने का विचार भी पसंद नहीं आया। मुझे खुशी है कि आपको अपनी समस्या का हल मिल गया, हालांकि, मुझे पता है कि इसने मुझे किस तरह के सिरदर्द दिए हैं।
जेसी वेब

0

इस ब्लॉग प्रविष्टि ने मेरी मदद की:

http://asp-net.vexedlogic.com/2011/04/23/asp-net-maximum-request-length-exceeded/

यदि आप IIS 7.0 या उच्चतर का उपयोग कर रहे हैं, तो आप अनुरोधों को संभालने के लिए अपनी Web.config फ़ाइल को बदल सकते हैं जो बहुत बड़ी है। कुछ चेतावनी हैं, लेकिन यहाँ एक उदाहरण है:

<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="1048576" />
    </requestFiltering>
  </security>
  <httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="404" subStatusCode="13" />
    <error statusCode="404" subStatusCode="13" prefixLanguageFilePath="" path="/UploadTooLarge.htm" responseMode="Redirect" />
  </httpErrors>
</system.webServer>

इन विन्यास फाइल तत्वों के बारे में यहाँ अतिरिक्त विवरण हैं:

http://www.iis.net/ConfigReference/system.webServer/security/requestFiltering/requestLimits

स्थिति कोड 404.13 को "सामग्री की लंबाई बहुत बड़ी" के रूप में परिभाषित किया गया है। ध्यान देने वाली एक महत्वपूर्ण बात यह है कि maxAllowedContentLengthबाइट्स में निर्दिष्ट है। यह maxRequestLengthआपके द्वारा <system.web>अनुभाग में पाई गई सेटिंग से अलग है , जो किलोबाइट में निर्दिष्ट है।

<system.web>
  <httpRuntime maxRequestLength="10240" />
</system.web>

यह भी ध्यान रखें कि pathविशेषता एक निरपेक्ष पथ जब होना चाहिए responseModeहै Redirect, इसलिए, आभासी निर्देशिका नाम पहले जोड़ें अगर प्रासंगिक। जेसी वेब के जानकारीपूर्ण उत्तर बताते हैं कि यह कैसे करना है responseMode="ExecuteURL", और मुझे लगता है कि दृष्टिकोण भी अच्छा काम करेगा।

यदि आप विजुअल स्टूडियो डेवलपमेंट सर्वर (कैसिनी, वेब सर्वर विजुअल स्टूडियो में एकीकृत) का उपयोग कर विकसित कर रहे हैं तो यह दृष्टिकोण काम नहीं करता है। मैं मान रहा हूँ कि यह IIS एक्सप्रेस में काम करेगा, लेकिन मैंने इसका परीक्षण नहीं किया है।


0

मुझे वही समस्या आ Application_Error()रही थी जहाँ मारा नहीं जा रहा था। मैंने सब कुछ करने की कोशिश की, जब तक कि मैं क्या हो रहा था के माध्यम से कदम रखा। मेरे पास ईएलएमएएच ईवेंट में कुछ कस्टम कोड था जो उस ईमेल को भेजने वाले JSON को जोड़ रहा था, और वहां एक अशक्त त्रुटि थी!

आंतरिक त्रुटि को ठीक करने से कोड को Application_Error()उम्मीद के मुताबिक घटना को जारी रखने की अनुमति मिली ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.