ASP.NET Core Blazor uygulamalarında hataları işleme

Not

Bu, bu makalenin en son sürümü değildir. Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.

Önemli

Bu bilgiler, ticari olarak piyasaya sürülmeden önce önemli ölçüde değiştirilebilen bir yayın öncesi ürünle ilgilidir. Burada verilen bilgilerle ilgili olarak Microsoft açık veya zımni hiçbir garanti vermez.

Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.

Bu makalede, işlenmeyen özel durumları yönetme Blazor ve hataları algılayıp işleyen uygulamalar geliştirme açıklanmaktadır.

Geliştirme sırasında ayrıntılı hatalar

Uygulama Blazor geliştirme sırasında düzgün çalışmadığında, uygulamadan ayrıntılı hata bilgileri almak sorunu gidermeye ve düzeltmeye yardımcı olur. Bir hata oluştuğunda uygulamalar ekranın Blazor alt kısmında açık sarı bir çubuk görüntüler:

  • Geliştirme sırasında çubuk sizi özel durumu görebileceğiniz tarayıcı konsoluna yönlendirir.
  • Üretimde, çubuk kullanıcıya bir hata oluştuğu konusunda uyarır ve tarayıcının yenilenmesini önerir.

Bu hata işleme deneyiminin kullanıcı arabirimi, proje şablonlarının Blazorbir parçasıdır. Proje şablonlarının Blazor tüm sürümleri, tarayıcılara hata kullanıcı arabiriminin içeriğini önbelleğe almaması için sinyal vermek için özniteliğini kullanmazdata-nosnippet, ancak belgelerin tüm sürümleri Blazor özniteliğini uygular.

Blazor Web Uygulamasında, bileşendeki deneyimi özelleştirinMainLayout. Ortam Etiketi Yardımcısı (örneğin, <environment include="Production">...</environment>) bileşenlerde Razor desteklenmediğinden, aşağıdaki örnek farklı ortamlar IHostEnvironment için hata iletilerini yapılandırmak üzere ekler.

üst kısmında MainLayout.razor:

@inject IHostEnvironment HostEnvironment

Hata kullanıcı arabirimi işaretlemesini Blazor oluşturun veya değiştirin:

<div id="blazor-error-ui" data-nosnippet>
    @if (HostEnvironment.IsProduction())
    {
        <span>An error has occurred.</span>
    }
    else
    {
        <span>An unhandled exception occurred.</span>
    }
    <a href="" class="reload">Reload</a>
    <a class="dismiss">🗙</a>
</div>

Bir Blazor Server uygulamada, dosyadaki deneyimi özelleştirin Pages/_Host.cshtml . Aşağıdaki örnek, farklı ortamlar için hata iletilerini yapılandırmak için Ortam Etiketi Yardımcısı'nı kullanır.

Bir Blazor Server uygulamada, dosyadaki deneyimi özelleştirin Pages/_Layout.cshtml . Aşağıdaki örnek, farklı ortamlar için hata iletilerini yapılandırmak için Ortam Etiketi Yardımcısı'nı kullanır.

Bir Blazor Server uygulamada, dosyadaki deneyimi özelleştirin Pages/_Host.cshtml . Aşağıdaki örnek, farklı ortamlar için hata iletilerini yapılandırmak için Ortam Etiketi Yardımcısı'nı kullanır.

Hata kullanıcı arabirimi işaretlemesini Blazor oluşturun veya değiştirin:

<div id="blazor-error-ui" data-nosnippet>
    <environment include="Staging,Production">
        An error has occurred.
    </environment>
    <environment include="Development">
        An unhandled exception occurred.
    </environment>
    <a href="" class="reload">Reload</a>
    <a class="dismiss">🗙</a>
</div>

Bir Blazor WebAssembly uygulamada, dosyadaki deneyimi özelleştirin wwwroot/index.html :

<div id="blazor-error-ui" data-nosnippet>
    An unhandled error has occurred.
    <a href="" class="reload">Reload</a>
    <a class="dismiss">🗙</a>
</div>

blazor-error-ui öğesi, uygulamanın otomatik olarak oluşturulan stil sayfasında CSS sınıfının stilinin blazor-error-ui bulunması display: none nedeniyle normalde gizlenir. Bir hata oluştuğunda, çerçeve öğesine uygulanır display: block .

blazor-error-ui öğesi normalde sitenin klasördeki stil sayfasında CSS sınıfının stilinin blazor-error-ui bulunması display: none nedeniyle gizlenirwwwroot/css. Bir hata oluştuğunda, çerçeve öğesine uygulanır display: block .

Ayrıntılı bağlantı hattı hataları

Bu bölüm, bir bağlantı hattı üzerinde çalışan Web Apps için Blazor geçerlidir.

Bu bölüm uygulamalar için Blazor Server geçerlidir.

İstemci tarafı hataları çağrı yığınını içermez ve hatanın nedeni hakkında ayrıntılı bilgi sağlamaz, ancak sunucu günlükleri bu tür bilgileri içerir. Geliştirme amacıyla hassas bağlantı hattı hata bilgileri, ayrıntılı hatalar etkinleştirilerek istemcinin kullanımına sunulabiliyor.

CircuitOptions.DetailedErrors seçeneğini true olarak ayarlayın. Daha fazla bilgi ve örnek için bkz . ASP.NET Temel BlazorSignalR kılavuzu.

Ayarın CircuitOptions.DetailedErrors alternatifi, yapılandırma anahtarını true uygulamanın Development ortam ayarları dosyasında (appsettings.Development.json olarak ayarlamaktırDetailedErrors). Ayrıca, ayrıntılı SignalR günlük kaydı için sunucu tarafı günlüğünü (Microsoft.AspNetCore.SignalR) Hata Ayıklama veya İzleme olarak ayarlayınSignalR.

appsettings.Development.json:

{
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.AspNetCore.SignalR": "Debug"
    }
  }
}

Yapılandırma DetailedErrors anahtarı, ortam sunucularındaStagingDevelopment/veya yerel sisteminizde değeri olan ortam değişkenini true kullanmaya ASPNETCORE_DETAILEDERRORS da ayarlanabilir.true

Uyarı

Hata bilgilerini İnternet'te istemcilere ifşa etmekten her zaman kaçının; bu bir güvenlik riskidir.

Bileşen sunucu tarafı işleme için Razor ayrıntılı hatalar

Bu bölüm Web Apps için Blazor geçerlidir.

RazorComponentsServiceOptions.DetailedErrors Bileşen sunucu tarafı işleme hataları hakkında ayrıntılı bilgi oluşturmayı denetlemek için Razor seçeneğini kullanın. Varsayılan değer şudur: false.

Aşağıdaki örnek ayrıntılı hataları etkinleştirir:

builder.Services.AddRazorComponents(options => 
    options.DetailedErrors = builder.Environment.IsDevelopment());

Uyarı

Yalnızca ortamdaki ayrıntılı hataları Development etkinleştirin. Ayrıntılı hatalar, kötü amaçlı kullanıcıların bir saldırıda kullanabileceği uygulama hakkında hassas bilgiler içerebilir.

Yukarıdaki örnek, değerini tarafından IsDevelopmentdöndürülen değere DetailedErrors göre ayarlayarak bir güvenlik derecesi sağlar. Uygulama Development ortamdayken olarak DetailedErrors ayarlanır true. Üretim uygulamasını ortamdaki genel bir sunucuda Development barındırmak mümkün olduğundan bu yaklaşım kusursuz değildir.

Geliştirici kodunda işlenmeyen özel durumları yönetme

Bir uygulamanın hatadan sonra devam etmesi için uygulamanın hata işleme mantığı olmalıdır. Bu makalenin sonraki bölümlerinde işlenmeyen özel durumların olası kaynakları açıklanmaktadır.

Üretimde çerçeve özel durum iletilerini işlemeyin veya kullanıcı arabiriminde izlemeleri yığınlamayın. Özel durum iletileri veya yığın izlemeleri işlenebilir:

  • Hassas bilgileri son kullanıcılara ifşa etme.
  • Kötü amaçlı bir kullanıcının uygulama, sunucu veya ağ güvenliğini tehlikeye atabilecek zayıflıkları bulmasına yardımcı olun.

Devreler için işlenmeyen özel durumlar

Bu bölüm, bir bağlantı hattı üzerinde çalışan sunucu tarafı uygulamaları için geçerlidir.

Razor sunucu etkileşiminin etkinleştirildiği bileşenler sunucuda durum bilgisi vardır. Kullanıcılar sunucudaki bileşenle etkileşim kurarken, bağlantı hattı olarak bilinen sunucuyla bağlantı kurar. Devre, etkin bileşen örneklerinin yanı sıra aşağıdakiler gibi diğer birçok durum yönünü barındırıyor:

  • Bileşenlerin en son işlenen çıktısı.
  • İstemci tarafı olayları tarafından tetiklenebilen geçerli olay işleme temsilcileri kümesi.

Bir kullanıcı uygulamayı birden çok tarayıcı sekmesinde açarsa, kullanıcı birden çok bağımsız devre oluşturur.

Blazor çoğu işlenmeyen özel durumları, oluştukları bağlantı hattı için önemli olarak kabul eder. İşlenmeyen bir özel durum nedeniyle bir bağlantı hattı sonlandırılırsa, kullanıcı yalnızca sayfayı yeniden yükleyerek yeni bir bağlantı hattı oluşturarak uygulamayla etkileşim kurmaya devam edebilir. Sonlandırılan devrenin dışında kalan ve diğer kullanıcıların veya diğer tarayıcı sekmelerinin devreleri olan devreler etkilenmez. Bu senaryo, kilitlenen bir masaüstü uygulamasına benzer. Kilitlenen uygulamanın yeniden başlatılması gerekir, ancak diğer uygulamalar etkilenmez.

Aşağıdaki nedenlerle işlenmeyen bir özel durum oluştuğunda çerçeve bir bağlantı hattını sonlandırır:

  • İşlenmeyen bir özel durum genellikle devreyi tanımsız durumda bırakır.
  • İşlenmeyen bir özel durumdan sonra uygulamanın normal işlemi garanti edilemez.
  • Bağlantı hattı tanımlanmamış durumda devam ederse uygulamada güvenlik açıkları görüntülenebilir.

Genel özel durum işleme

Genel özel durum işleme için aşağıdaki bölümlere bakın:

Hata sınırları

Hata sınırları , özel durumları işlemek için kullanışlı bir yaklaşım sağlar. Bileşen ErrorBoundary :

  • Bir hata oluşmadığında alt içeriğini işler.
  • İşlenmeyen bir özel durum oluştuğunda hata kullanıcı arabirimini işler.

Hata sınırı tanımlamak için mevcut içeriği sarmalama bileşenini kullanın ErrorBoundary . Uygulama normal çalışmaya devam eder, ancak hata sınırı işlenmeyen özel durumları işler.

<ErrorBoundary>
    ...
</ErrorBoundary>

Genel bir şekilde hata sınırı uygulamak için, uygulamanın ana düzeninin gövde içeriğinin çevresine sınırı ekleyin.

MainLayout.razor içinde:

<article class="content px-4">
    <ErrorBoundary>
        @Body
    </ErrorBoundary>
</article>

Hata sınırının yalnızca statik MainLayout bir bileşene uygulandığı Web Apps'teBlazor, sınır yalnızca statik sunucu tarafı işleme (statik SSR) aşamasında etkindir. Sınır, yalnızca bileşen hiyerarşisinin daha aşağısındaki bir bileşen etkileşimli olduğundan etkinleştirilmez. Bileşenin ve bileşenlerin geri kalanının bileşen hiyerarşisinin MainLayout daha aşağısında etkileşime olanak tanımak için, bileşendekiComponents/App.razor () ve Routes bileşen örnekleri için HeadOutlet etkileşimli işlemeyi App etkinleştirin. Aşağıdaki örnek Etkileşimli Sunucu (InteractiveServer) işleme modunu benimser:

<HeadOutlet @rendermode="InteractiveServer" />

...

<Routes @rendermode="InteractiveServer" />

Bileşenden Routes uygulamanın tamamında sunucu etkileşimini etkinleştirmeyi tercih ediyorsanız, hata sınırını bileşen hiyerarşisinin daha aşağısına yerleştirin. Örneğin, hata sınırını uygulamanın ana düzeninde değil, etkileşime olanak tanıyan tek tek bileşenlerde işaretlemenin çevresine yerleştirin. Akılda tutulması gereken önemli kavramlar, hata sınırının nereye yerleştirildiğidir:

  • Hata sınırı etkileşimli değilse, yalnızca statik işleme sırasında sunucuda etkinleştirme yapabilir. Örneğin, bir bileşen yaşam döngüsü yönteminde hata oluştuğunda sınır etkinleştirilebilir.
  • Hata sınırı etkileşimliyse, sarmalandığı Etkileşimli Sunucu tarafından işlenen bileşenler için etkinleştirebilme özelliğine sahiptir.

Sayım beşi geçerse bileşenin Counter bir özel durum oluşturacağı aşağıdaki örneği göz önünde bulundurun.

Counter.razor içinde:

private void IncrementCount()
{
    currentCount++;

    if (currentCount > 5)
    {
        throw new InvalidOperationException("Current count is too big!");
    }
}

İşlenmeyen özel durum beşin üzerinde bir currentCount süre için oluşturulursa:

  • Hata normal olarak günlüğe kaydedilir (System.InvalidOperationException: Current count is too big!).
  • Özel durum hata sınırı tarafından işlenir.
  • Hata kullanıcı arabirimi, hata sınırı tarafından aşağıdaki varsayılan hata iletisiyle işlenir: An error has occurred.

Varsayılan olarak, ErrorBoundary bileşen hata içeriği için CSS sınıfıyla blazor-error-boundary boş <div> bir öğe işler. Varsayılan kullanıcı arabiriminin renkleri, metni ve simgesi, uygulamanın klasördeki stil sayfasında wwwroot CSS kullanılarak tanımlanır, bu nedenle hata kullanıcı arabirimini özelleştirebilirsiniz.

Özelliğini ayarlayarak ErrorContent varsayılan hata içeriğini değiştirin:

<ErrorBoundary>
    <ChildContent>
        @Body
    </ChildContent>
    <ErrorContent>
        <p class="errorUI">😈 A rotten gremlin got us. Sorry!</p>
    </ErrorContent>
</ErrorBoundary>

Önceki örneklerde hata sınırı düzende tanımlandığından, hata oluştuktan sonra kullanıcının hangi sayfaya gidildiğine bakılmaksızın hata kullanıcı arabirimi görülür. Çoğu senaryoda hata sınırlarının kapsamını daraltmanızı öneririz. Bir hata sınırının kapsamını geniş kapsamlı olarak tanımlarsanız, hata sınırının Recover yöntemini çağırarak sonraki sayfa gezinti olaylarında hata olmayan bir duruma sıfırlayabilirsiniz.

MainLayout.razor içinde:

...

<ErrorBoundary @ref="errorBoundary">
    @Body
</ErrorBoundary>

...

@code {
    private ErrorBoundary? errorBoundary;

    protected override void OnParametersSet()
    {
        errorBoundary?.Recover();
    }
}

Kurtarma işleminin yalnızca hatayı yeniden oluşturan bir bileşeni yeniden oluşturduğu sonsuz döngüden kaçınmak için işleme mantığını çağırmayın Recover . Yalnızca şu durumlarda arama Recover :

  • Kullanıcı, bir yordamı yeniden denemek istediğini veya kullanıcı yeni bir bileşene gittiği zaman düğme seçme gibi bir kullanıcı arabirimi hareketi gerçekleştirir.
  • Ek mantık da özel durumu temizler. Bileşen yeniden başlatıldığında hata yinelenmez.

Alternatif genel özel durum işleme

Hata sınırlarını (ErrorBoundary) kullanmanın bir alternatifi, özel bir hata bileşenini alt bileşenlere geçirmektir.CascadingValue Eklenen hizmeti veya özel günlükçü uygulamasını kullanmak yerine bir bileşeni kullanmanın avantajlarından biri, basamaklı bir bileşenin bir hata oluştuğunda içeriği işleyip CSS stilleri uygulayabilmesidir.

Aşağıdaki Error bileşen örneği yalnızca hataları günlüğe kaydeder, ancak bileşenin yöntemleri, birden çok hata işleme yönteminin kullanılması da dahil olmak üzere uygulama tarafından gerekli olan herhangi bir şekilde hataları işleyebilir.

Error.razor:

@inject ILogger<Error> Logger

<CascadingValue Value="this">
    @ChildContent
</CascadingValue>

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    public void ProcessError(Exception ex)
    {
        Logger.LogError("Error:ProcessError - Type: {Type} Message: {Message}", 
            ex.GetType(), ex.Message);

        // Call StateHasChanged if ProcessError directly participates in 
        // rendering. If ProcessError only logs or records the error,
        // there's no need to call StateHasChanged.
        //StateHasChanged();
    }
}

Not

hakkında RenderFragmentdaha fazla bilgi için bkz . ASP.NET Core Razor bileşenleri.

BileşendeRoutes, bileşeni (<Router>...</Router>) bileşenle sarmalar RouterError. Bu, bileşenin, bileşenin Error olarak alındığı uygulamanın herhangi bir CascadingParameterbileşenine art arda inmesine Error izin verir.

Routes.razor içinde:

<Error>
    <Router ...>
        ...
    </Router>
</Error>

BileşendeApp, bileşeni (<Router>...</Router>) bileşenle sarmalar RouterError. Bu, bileşenin, bileşenin Error olarak alındığı uygulamanın herhangi bir CascadingParameterbileşenine art arda inmesine Error izin verir.

App.razor içinde:

<Error>
    <Router ...>
        ...
    </Router>
</Error>

Bir bileşendeki hataları işlemek için:

  • Bileşeni blokta @code olarak CascadingParameter belirleyinError. Proje şablonunu temel alan Blazor bir uygulamadaki örnek Counter bileşene aşağıdaki Error özelliği ekleyin:

    [CascadingParameter]
    public Error? Error { get; set; }
    
  • Uygun bir özel durum türüne sahip herhangi bir catch blokta hata işleme yöntemini çağır. Örnek Error bileşen yalnızca tek ProcessError bir yöntem sunar, ancak hata işleme bileşeni uygulama genelinde alternatif hata işleme gereksinimlerini karşılamak için herhangi bir sayıda hata işleme yöntemi sağlayabilir. Aşağıdaki Counter bileşen örneğinde, sayı beşten büyük olduğunda bir özel durum oluşturulur ve kapana kısılır:

    @code {
        private int currentCount = 0;
    
        [CascadingParameter]
        public Error? Error { get; set; }
    
        private void IncrementCount()
        {
            try
            {
                currentCount++;
    
                if (currentCount > 5)
                {
                    throw new InvalidOperationException("Current count is over five!");
                }
            }
            catch (Exception ex)
            {
                Error?.ProcessError(ex);
            }
        }
    }
    

Önceki Error bileşeni bir Counter bileşende yapılan önceki değişikliklerle birlikte kullanarak, tarayıcının geliştirici araçları konsolu kapana kısılmış, günlüğe kaydedilen hatayı gösterir:

fail: {COMPONENT NAMESPACE}.Error[0]
Error:ProcessError - Type: System.InvalidOperationException Message: Current count is over five!

ProcessError Yöntem, özel bir hata iletisi çubuğu gösterme veya işlenen öğelerin CSS stillerini değiştirme gibi işlemeye doğrudan katılıyorsa, kullanıcı arabirimini yeniden oluşturmak için yöntemin ProcessErrors sonunda çağrısı StateHasChanged yapın.

Bu bölümdeki yaklaşımlar bir try-catch deyimle ilgili hataları işlediğinden, bir hata oluştuğunda ve bağlantı hattı canlı kaldığında uygulamanın SignalR istemci ve sunucu arasındaki bağlantısı kopmuyor. İşlenmeyen diğer özel durumlar bir bağlantı hattı için önemli olmaya devam eder. Daha fazla bilgi için, devrenin işlenmeyen özel durumlara nasıl tepki vermesine ilişkin bölüme bakın.

Uygulama, hataları merkezi bir şekilde işlemek için bir hata işleme bileşenini basamaklı değer olarak kullanabilir.

Aşağıdaki Error bileşen kendisini alt bileşenlere geçirir CascadingValue . Aşağıdaki örnek yalnızca hatayı günlüğe kaydeder, ancak bileşenin yöntemleri, birden çok hata işleme yönteminin kullanılması da dahil olmak üzere uygulamanın gerektirdiği herhangi bir şekilde hataları işleyebilir. Eklenen hizmeti veya özel günlükçü uygulamasını kullanmak yerine bir bileşeni kullanmanın avantajlarından biri, basamaklı bir bileşenin bir hata oluştuğunda içeriği işleyip CSS stilleri uygulayabilmesidir.

Error.razor:

@using Microsoft.Extensions.Logging
@inject ILogger<Error> Logger

<CascadingValue Value="this">
    @ChildContent
</CascadingValue>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    public void ProcessError(Exception ex)
    {
        Logger.LogError("Error:ProcessError - Type: {Type} Message: {Message}", 
            ex.GetType(), ex.Message);
    }
}

Not

hakkında RenderFragmentdaha fazla bilgi için bkz . ASP.NET Core Razor bileşenleri.

BileşendeApp, bileşeni bileşenle birlikte sarmalar ErrorRouter. Bu, bileşenin, bileşenin Error olarak alındığı uygulamanın herhangi bir CascadingParameterbileşenine art arda inmesine Error izin verir.

App.razor:

<Error>
    <Router ...>
        ...
    </Router>
</Error>

Bir bileşendeki hataları işlemek için:

  • Error Bileşeni bloğunda @code olarak CascadingParameter belirleyin:

    [CascadingParameter]
    public Error Error { get; set; }
    
  • Uygun bir özel durum türüne sahip herhangi bir catch blokta hata işleme yöntemini çağır. Örnek Error bileşen yalnızca tek ProcessError bir yöntem sunar, ancak hata işleme bileşeni uygulama genelinde alternatif hata işleme gereksinimlerini karşılamak için herhangi bir sayıda hata işleme yöntemi sağlayabilir.

    try
    {
        ...
    }
    catch (Exception ex)
    {
        Error.ProcessError(ex);
    }
    

Yukarıdaki örnek Error bileşeni ve ProcessError yöntemi kullanarak tarayıcının geliştirici araçları konsolu kapana kısılmış, günlüğe kaydedilen hatayı gösterir:

fail: BlazorSample.Shared.Error[0] Error:ProcessError - Type: System.NullReferenceException message: Nesne başvurusu bir nesnenin örneğine ayarlanmadı.

ProcessError Yöntem, özel bir hata iletisi çubuğu gösterme veya işlenen öğelerin CSS stillerini değiştirme gibi işlemeye doğrudan katılıyorsa, kullanıcı arabirimini yeniden oluşturmak için yöntemin ProcessErrors sonunda çağrısı StateHasChanged yapın.

Bu bölümdeki yaklaşımlar bir try-catch deyimle ilgili hataları işlediğinden, bir Blazor hata oluştuğunda ve bağlantı hattı canlı kaldığında uygulamanın SignalR istemci ile sunucu arasındaki bağlantısı kopmuyor. İşlenmeyen özel durumlar bir bağlantı hattı için önemlidir. Daha fazla bilgi için, devrenin işlenmeyen özel durumlara nasıl tepki vermesine ilişkin bölüme bakın.

Kalıcı sağlayıcıyla hataları günlüğe kaydetme

İşlenmeyen bir özel durum oluşursa, özel durum hizmet kapsayıcısında yapılandırılan örneklere ILogger kaydedilir. Varsayılan olarak, Blazor uygulamalar Konsol Günlüğü Sağlayıcısı ile konsol çıkışında oturum açar. Günlük boyutunu ve günlük döndürmeyi yöneten bir sağlayıcıyla sunucudaki bir konuma (veya istemci tarafı uygulamalar için arka uç web API'sine) günlük kaydı yapmayı göz önünde bulundurun. Alternatif olarak, uygulama Azure Uygulaması lication Analizler (Azure İzleyici) gibi bir Uygulama Performansı Yönetimi (APM) hizmeti kullanabilir.

Not

İstemci tarafı uygulamaları ve Google Analytics için yerel çerçeve desteğini desteklemek için yerel BlazorUygulama Analizler özellikleri, bu teknolojilerin gelecek sürümlerinde kullanılabilir hale gelebilir. Daha fazla bilgi için bkz. WASM İstemci Tarafında (microsoft/Application Analizler-dotnet #2143) ve Web analizi ve tanılamalarında (topluluk uygulamalarına bağlantılar içerir) (dotnet/aspnetcore #5461) Destek Uygulaması Blazor Analizler. Bu arada, istemci tarafı bir uygulama, hataları doğrudan istemci tarafı uygulamasından Application Analizler'a günlüğe kaydetmek için birlikte çalışma ile JS Application Analizler JavaScript SDK'sını kullanabilir.

Bir bağlantı hattı üzerinden çalışan bir Blazor uygulamada geliştirme sırasında uygulama genellikle hata ayıklamaya yardımcı olmak için özel durumların tüm ayrıntılarını tarayıcının konsoluna gönderir. Üretimde, ayrıntılı hatalar istemcilere gönderilmez, ancak özel durumun tüm ayrıntıları sunucuda günlüğe kaydedilir.

Hangi olayların günlüğe kaydedileceğine ve günlüğe kaydedilen olayların önem düzeyine karar vermeniz gerekir. Saldırgan kullanıcılar hataları kasıtlı olarak tetikleyebilir. Örneğin, ürün ayrıntılarını görüntüleyen bir bileşenin URL'sinde bilinmeyen ProductId bir hatanın bulunduğu bir hatayı günlüğe kaydetmeyin. Tüm hatalar günlüğe kaydetme olayı olarak değerlendirilmemelidir.

Daha fazla bilgi için aşağıdaki makaleleri inceleyin:

‡için web API arka uç uygulamaları olan sunucu tarafı Blazor uygulamalar ve diğer sunucu tarafı ASP.NET Core uygulamaları için Blazorgeçerlidir. İstemci tarafı uygulamaları, istemcideki hata bilgilerini yakalayıp bir web API'sine gönderebilir ve bu da hata bilgilerini kalıcı bir günlük sağlayıcısına günlüğe kaydeder.

İşlenmeyen bir özel durum oluşursa, özel durum hizmet kapsayıcısında yapılandırılan örneklere ILogger kaydedilir. Varsayılan olarak, Blazor uygulamalar Konsol Günlüğü Sağlayıcısı ile konsol çıkışında oturum açar. Günlük boyutu yönetimi ve günlük döndürmesi olan bir günlük sağlayıcısı kullanan bir arka uç web API'sine hata bilgileri göndererek sunucuda daha kalıcı bir konuma oturum açmayı göz önünde bulundurun. Alternatif olarak, arka uç web API'si uygulaması istemcilerden aldığı hata bilgilerini kaydetmek için Azure Uygulaması lication Analizler (Azure İzleyici)† gibi bir Uygulama Performansı Yönetimi (APM) hizmeti kullanabilir.

Hangi olayların günlüğe kaydedileceğine ve günlüğe kaydedilen olayların önem düzeyine karar vermeniz gerekir. Saldırgan kullanıcılar hataları kasıtlı olarak tetikleyebilir. Örneğin, ürün ayrıntılarını görüntüleyen bir bileşenin URL'sinde bilinmeyen ProductId bir hatanın bulunduğu bir hatayı günlüğe kaydetmeyin. Tüm hatalar günlüğe kaydetme olayı olarak değerlendirilmemelidir.

Daha fazla bilgi için aşağıdaki makaleleri inceleyin:

†İstek tarafı uygulamaları ve Google Analytics için yerel Blazor çerçeve desteğini destekleyen Analizleruygulama özellikleri, bu teknolojilerin gelecek sürümlerinde kullanılabilir hale gelebilir. Daha fazla bilgi için bkz. WASM İstemci Tarafında (microsoft/Application Analizler-dotnet #2143) ve Web analizi ve tanılamalarında (topluluk uygulamalarına bağlantılar içerir) (dotnet/aspnetcore #5461) Destek Uygulaması Blazor Analizler. Bu arada, istemci tarafı bir uygulama, hataları doğrudan istemci tarafı uygulamasından Application Analizler'a günlüğe kaydetmek için birlikte çalışma ile JS Application Analizler JavaScript SDK'sını kullanabilir.

‡Uygulamalar için web API arka uç uygulamaları olan sunucu tarafı ASP.NET Core uygulamaları için Blazor geçerlidir. İstemci tarafı uygulamalar yakalanıp hata bilgilerini bir web API'sine gönderir ve hata bilgilerini kalıcı bir günlük sağlayıcısına kaydeder.

Hataların oluşabileceği yerler

Çerçeve ve uygulama kodu, bu makalenin aşağıdaki bölümlerinde daha ayrıntılı olarak açıklanan aşağıdaki konumlardan herhangi birinde işlenmeyen özel durumlar tetikleyebilir:

Bileşen örneği oluşturma

Bir bileşenin örneğini oluşturduğunda Blazor :

  • Bileşenin oluşturucusunun çağrılır.
  • yönergesi veya özniteliği aracılığıyla @inject bileşenin oluşturucusunun sağladığı DI hizmetlerinin [Inject] oluşturucuları çağrılır.

Yürütülen bir oluşturucuda veya herhangi [Inject] bir özellik için ayarlayıcıda oluşan bir hata işlenmeyen bir özel durumla sonuçlanır ve çerçevenin bileşenin örneğini oluşturmasını durdurur. Uygulama bir bağlantı hattı üzerinden çalışıyorsa devre başarısız olur. Oluşturucu mantığı özel durumlar oluşturabilecekse, uygulama hata işleme ve günlüğe kaydetme ile bir try-catch deyimi kullanarak özel durumları yakalamalıdır.

Yaşam döngüsü yöntemleri

Bir bileşenin ömrü boyunca yaşam Blazor döngüsü yöntemlerini çağırır. Herhangi bir yaşam döngüsü yöntemi zaman uyumlu veya zaman uyumsuz bir özel durum oluşturursa, özel durum bir bağlantı hattı için önemli olur. Bileşenlerin yaşam döngüsü yöntemlerindeki hatalarla başa çıkabilmek için hata işleme mantığı ekleyin.

Aşağıdaki örnekte OnParametersSetAsync bir ürünü almak için bir yöntemi çağırır:

  • yönteminde ProductRepository.GetProductByIdAsync oluşan bir özel durum bir try-catch deyimi tarafından işlenir.
  • catch Blok yürütülürken:
    • loadFailed olarak ayarlanır trueve kullanıcıya bir hata iletisi görüntülemek için kullanılır.
    • Hata günlüğe kaydedilir.
@page "/product-details/{ProductId:int?}"
@inject ILogger<ProductDetails> Logger
@inject IProductRepository Product

<PageTitle>Product Details</PageTitle>

<h1>Product Details Example</h1>

@if (details != null)
{
    <h2>@details.ProductName</h2>
    <p>
        @details.Description
        <a href="@details.Url">Company Link</a>
    </p>
    
}
else if (loadFailed)
{
    <h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
    <h1>Loading...</h1>
}

@code {
    private ProductDetail? details;
    private bool loadFailed;

    [Parameter]
    public int ProductId { get; set; }

    protected override async Task OnParametersSetAsync()
    {
        try
        {
            loadFailed = false;

            // Reset details to null to display the loading indicator
            details = null;

            details = await Product.GetProductByIdAsync(ProductId);
        }
        catch (Exception ex)
        {
            loadFailed = true;
            Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
        }
    }

    public class ProductDetail
    {
        public string? ProductName { get; set; }
        public string? Description { get; set; }
        public string? Url { get; set; }
    }

    /*
    * Register the service in Program.cs:
    * using static BlazorSample.Components.Pages.ProductDetails;
    * builder.Services.AddScoped<IProductRepository, ProductRepository>();
    */

    public interface IProductRepository
    {
        public Task<ProductDetail> GetProductByIdAsync(int id);
    }

    public class ProductRepository : IProductRepository
    {
        public Task<ProductDetail> GetProductByIdAsync(int id)
        {
            return Task.FromResult(
                new ProductDetail()
                {
                    ProductName = "Flowbee ",
                    Description = "The Revolutionary Haircutting System You've Come to Love!",
                    Url = "https://flowbee.com/"
                });
        }
    }
}
@page "/product-details/{ProductId:int}"
@using Microsoft.Extensions.Logging
@inject ILogger<ProductDetails> Logger
@inject IProductRepository ProductRepository

@if (details != null)
{
    <h1>@details.ProductName</h1>
    <p>@details.Description</p>
}
else if (loadFailed)
{
    <h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
    <h1>Loading...</h1>
}

@code {
    private ProductDetail? details;
    private bool loadFailed;

    [Parameter]
    public int ProductId { get; set; }

    protected override async Task OnParametersSetAsync()
    {
        try
        {
            loadFailed = false;

            // Reset details to null to display the loading indicator
            details = null;

            details = await ProductRepository.GetProductByIdAsync(ProductId);
        }
        catch (Exception ex)
        {
            loadFailed = true;
            Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
        }
    }

    public class ProductDetail
    {
        public string? ProductName { get; set; }
        public string? Description { get; set; }
    }

    public interface IProductRepository
    {
        public Task<ProductDetail> GetProductByIdAsync(int id);
    }
}
@page "/product-details/{ProductId:int}"
@using Microsoft.Extensions.Logging
@inject ILogger<ProductDetails> Logger
@inject IProductRepository ProductRepository

@if (details != null)
{
    <h1>@details.ProductName</h1>
    <p>@details.Description</p>
}
else if (loadFailed)
{
    <h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
    <h1>Loading...</h1>
}

@code {
    private ProductDetail? details;
    private bool loadFailed;

    [Parameter]
    public int ProductId { get; set; }

    protected override async Task OnParametersSetAsync()
    {
        try
        {
            loadFailed = false;

            // Reset details to null to display the loading indicator
            details = null;

            details = await ProductRepository.GetProductByIdAsync(ProductId);
        }
        catch (Exception ex)
        {
            loadFailed = true;
            Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
        }
    }

    public class ProductDetail
    {
        public string? ProductName { get; set; }
        public string? Description { get; set; }
    }

    public interface IProductRepository
    {
        public Task<ProductDetail> GetProductByIdAsync(int id);
    }
}
@page "/product-details/{ProductId:int}"
@using Microsoft.Extensions.Logging
@inject ILogger<ProductDetails> Logger
@inject IProductRepository ProductRepository

@if (details != null)
{
    <h1>@details.ProductName</h1>
    <p>@details.Description</p>
}
else if (loadFailed)
{
    <h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
    <h1>Loading...</h1>
}

@code {
    private ProductDetail details;
    private bool loadFailed;

    [Parameter]
    public int ProductId { get; set; }

    protected override async Task OnParametersSetAsync()
    {
        try
        {
            loadFailed = false;

            // Reset details to null to display the loading indicator
            details = null;

            details = await ProductRepository.GetProductByIdAsync(ProductId);
        }
        catch (Exception ex)
        {
            loadFailed = true;
            Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
        }
    }

    public class ProductDetail
    {
        public string ProductName { get; set; }
        public string Description { get; set; }
    }

    public interface IProductRepository
    {
        public Task<ProductDetail> GetProductByIdAsync(int id);
    }
}
@page "/product-details/{ProductId:int}"
@using Microsoft.Extensions.Logging
@inject ILogger<ProductDetails> Logger
@inject IProductRepository ProductRepository

@if (details != null)
{
    <h1>@details.ProductName</h1>
    <p>@details.Description</p>
}
else if (loadFailed)
{
    <h1>Sorry, we could not load this product due to an error.</h1>
}
else
{
    <h1>Loading...</h1>
}

@code {
    private ProductDetail details;
    private bool loadFailed;

    [Parameter]
    public int ProductId { get; set; }

    protected override async Task OnParametersSetAsync()
    {
        try
        {
            loadFailed = false;

            // Reset details to null to display the loading indicator
            details = null;

            details = await ProductRepository.GetProductByIdAsync(ProductId);
        }
        catch (Exception ex)
        {
            loadFailed = true;
            Logger.LogWarning(ex, "Failed to load product {ProductId}", ProductId);
        }
    }

    public class ProductDetail
    {
        public string ProductName { get; set; }
        public string Description { get; set; }
    }

    public interface IProductRepository
    {
        public Task<ProductDetail> GetProductByIdAsync(int id);
    }
}

İşleme mantığı

Bir Razor bileşen dosyasındaki (.razor) bildirim temelli işaretleme, adlı BuildRenderTreebir C# yönteminde derlenmiştir. Bir bileşen işlendiğinde, BuildRenderTree işlenen bileşenin öğelerini, metnini ve alt bileşenlerini açıklayan bir veri yapısı yürütür ve oluşturur.

İşleme mantığı özel durum oluşturabilir. Bu senaryonun bir örneği, değerlendirildiğinde @someObject.PropertyName ancak @someObject olduğunda nulloluşur. Bir devre üzerinde çalışan uygulamalar için Blazor , işleme mantığı tarafından oluşan işlenmeyen bir özel durum, uygulamanın devresinde önemli olur.

İşleme mantığını önlemek NullReferenceException için, üyelerine erişmeden önce bir null nesneyi denetleyin. Aşağıdaki örnekte ise person.Address özelliklere person.Addressnullerişilenmez:

@if (person.Address != null)
{
    <div>@person.Address.Line1</div>
    <div>@person.Address.Line2</div>
    <div>@person.Address.City</div>
    <div>@person.Address.Country</div>
}

Yukarıdaki kod bunun person olmadığını nullvarsayar. Genellikle kodun yapısı, bileşenin işlendiği sırada bir nesnenin var olduğunu garanti eder. Bu gibi durumlarda, işleme mantığını denetlemek null gerekmez. Önceki örnekte, person aşağıdaki örnekte gösterildiği gibi bileşen örneği oluşturulurken oluşturulduğu için var person olacağı garanti edilebilir:

@code {
    private Person person = new();

    ...
}

Olay işleyicileri

İstemci tarafı kodu, aşağıdakiler kullanılarak olay işleyicileri oluşturulduğunda C# kodunun çağrılarını tetikler:

  • @onclick
  • @onchange
  • Diğer @on... öznitelikler
  • @bind

Olay işleyici kodu bu senaryolarda işlenmeyen bir özel durum oluşturabilir.

Uygulama dış nedenlerle başarısız olabilecek kodu çağırırsa, hata işleme ve günlüğe kaydetme özelliğine sahip bir try-catch deyimi kullanarak özel durumları yakalayın.

Bir olay işleyicisi, geliştirici kodu tarafından tuzağa düşürülmeyen ve işlenmeyen bir özel durum oluşturursa (örneğin, veritabanı sorgusu başarısız oluyorsa):

  • Çerçeve özel durumu günlüğe kaydeder.
  • Bir bağlantı hattı üzerinden çalışan bir Blazor uygulamada, özel durum uygulamanın bağlantı hattı için önemli olur.

Bileşen elden çıkarma

Kullanıcı başka bir sayfaya gitmesinden dolayı bir bileşen kullanıcı arabiriminden kaldırılabilir. Uygulayan System.IDisposable bir bileşen kullanıcı arabiriminden kaldırıldığında, çerçeve bileşenin Dispose yöntemini çağırır.

Bileşenin Dispose yöntemi bir bağlantı hattı üzerinde çalışan bir Blazor uygulamada işlenmeyen bir özel durum oluşturursa, özel durum uygulamanın bağlantı hattı için önemli olur.

Atma mantığı özel durumlar oluşturabilecekse, uygulamanın hata işleme ve günlüğe kaydetme ile bir try-catch deyimi kullanarak özel durumları yakalaması gerekir.

Bileşenin elden çıkarılması hakkında daha fazla bilgi için bkz . ASP.NET Core Razor bileşen yaşam döngüsü.

JavaScript ile birlikte çalışma

IJSRuntime , çerçeve tarafından Blazor kaydedilir. IJSRuntime.InvokeAsync .NET kodunun kullanıcının tarayıcısında JavaScript (JS) çalışma zamanına zaman uyumsuz çağrılar yapmasına izin verir.

aşağıdaki koşullar ile InvokeAsynchata işleme için geçerlidir:

  • Çağrısı InvokeAsync zaman uyumlu olarak başarısız olursa bir .NET özel durumu oluşur. InvokeAsync Örneğin, sağlanan bağımsız değişkenler serileştirilemediğinden çağrısı başarısız olabilir. Geliştirici kodu özel durumu yakalamalıdır. Bir olay işleyicisindeki veya bileşen yaşam döngüsü yöntemindeki uygulama kodu bir bağlantı hattı üzerinde çalışan bir Blazor uygulamada özel durumu işlemezse, sonuçta elde edilen özel durum uygulamanın devresinde önemli olur.
  • Çağrısı InvokeAsync zaman uyumsuz olarak başarısız olursa , .NET Task başarısız olur. InvokeAsync Örneğin, -side kodu bir özel durum oluşturacağından veya olarak rejectedtamamlanmış bir Promise döndürdüğünden JSçağrısı başarısız olabilir. Geliştirici kodu özel durumu yakalamalıdır. işlecini await kullanıyorsanız, yöntem çağrısını hata işleme ve günlüğe kaydetme ile bir try-catch deyimde sarmalamanız gerekir. Aksi takdirde, bir bağlantı hattı üzerinden çalışan bir Blazor uygulamada başarısız olan kod, uygulamanın bağlantı hattında önemli olan işlenmemiş bir özel durumla sonuçlanabilir.
  • Varsayılan olarak, çağrısının InvokeAsync belirli bir süre içinde tamamlanması gerekir, aksi halde arama zaman aşımına uysa. Varsayılan zaman aşımı süresi bir dakikadır. Zaman aşımı, kodu hiçbir zaman tamamlama iletisi göndermeden ağ bağlantısı veya JS kod kaybına karşı korur. Arama zaman aşımına ularsa, sonuç System.Threading.Tasks bir OperationCanceledExceptionile başarısız olur. Özel durumu günlüğe kaydetme ile yakalayın ve işleyin.

Benzer şekilde, JS kod özniteliği tarafından belirtilen .NET yöntemlerine [JSInvokable] çağrı başlatabilir. Bu .NET yöntemleri işlenmeyen bir özel durum oluşturursa:

  • Bir bağlantı hattı üzerinden çalışan bir Blazor uygulamada, özel durum uygulamanın bağlantı hattı için önemli olarak kabul edilmez .
  • JS-side Promise reddedilir.

.NET tarafında veya JS yöntem çağrısının tarafında hata işleme kodunu kullanma seçeneğiniz vardır.

Daha fazla bilgi için aşağıdaki makaleleri inceleyin:

Ön Kayıt

Razor bileşenleri varsayılan olarak önceden oluşturulur, böylece işlenen HTML işaretlemeleri kullanıcının ilk HTTP isteğinin bir parçası olarak döndürülür.

Devre üzerinden çalışan bir Blazor uygulamada, ön kayıt şu şekilde çalışır:

  • Aynı sayfanın parçası olan önceden oluşturulmuş tüm bileşenler için yeni bir bağlantı hattı oluşturma.
  • İlk HTML oluşturuluyor.
  • Kullanıcının tarayıcısı aynı sunucuya yeniden bağlantı kurana SignalR kadar bağlantı hattını olarak disconnected ele alır. Bağlantı kurulduğunda, bağlantı hattındaki etkileşim sürdürülür ve bileşenlerin HTML işaretlemesi güncelleştirilir.

Önceden oluşturulmuş istemci tarafı bileşenleri için, ön kayıt şu şekilde çalışır:

  • Aynı sayfanın parçası olan önceden oluşturulmuş tüm bileşenler için sunucuda ilk HTML oluşturuluyor.
  • Tarayıcı, uygulamanın derlenmiş kodunu ve .NET çalışma zamanını (henüz yüklenmemişse) arka plana yükledikten sonra bileşeni istemcide etkileşimli hale getirme.

Bir bileşen, örneğin bir yaşam döngüsü yöntemi veya işleme mantığı sırasında ön kayıt sırasında işlenmeyen bir özel durum oluşturursa:

  • Bir bağlantı hattı üzerinde çalışan bir Blazor uygulamada, özel durum devre için önemlidir. Önceden oluşturulmuş istemci tarafı bileşenleri için özel durum, bileşenin işlenmesini engeller.
  • Özel durum çağrısı yığınından ComponentTagHelperoluşturulur.

Prerendering başarısız olduğunda normal koşullarda, çalışan bir bileşen işlenemediğinden bileşeni derlemeye ve işlemeye devam etmek mantıklı değildir.

Ön kayıt sırasında oluşabilecek hataları tolere etmek için hata işleme mantığı özel durumlar oluşturabilecek bir bileşenin içine yerleştirilmelidir. Hata işleme ve günlüğe kaydetme ile deyimleri kullanın try-catch . deyimini ComponentTagHelper sarmalamak try-catch yerine hata işleme mantığını tarafından işlenen bileşene ComponentTagHelperyerleştirin.

Gelişmiş senaryolar

Özyinelemeli işleme

Bileşenler özyinelemeli olarak iç içe yerleştirilebilir. Bu, özyinelemeli veri yapılarını temsil etmek için kullanışlıdır. Örneğin, bir TreeNode bileşen düğümün alt öğelerinin her biri için daha fazla TreeNode bileşen işleyebilir.

Özyinelemeli olarak işlenirken, sonsuz özyinelemeyle sonuçlayan kodlama desenlerinden kaçının:

  • Döngü içeren bir veri yapısını yinelemeli olarak işlemeyin. Örneğin, alt öğeleri kendisini içeren bir ağaç düğümü oluşturmayın.
  • Döngü içeren bir düzen zinciri oluşturmayın. Örneğin, düzeni kendisinde olan bir düzen oluşturmayın.
  • Son kullanıcının kötü amaçlı veri girişi veya JavaScript birlikte çalışma çağrıları aracılığıyla özyineleme sabitlerini (kuralları) ihlal etmelerine izin verme.

İşleme sırasında sonsuz döngüler:

  • İşleme işleminin sonsuza kadar devam etmesine neden olur.
  • Sonlandırılmamış döngü oluşturmaya eşdeğerdir.

Bu senaryolarda başarısız Blazor olur ve genellikle şunları yapmaya çalışır:

  • İşletim sisteminin izin verdiği kadar CPU süresini süresiz olarak tüketin.
  • Sınırsız miktarda bellek kullanın. Sınırsız bellek kullanmak, sonlandırılmamış bir döngünün her yinelemedeki bir koleksiyona girişler eklediği senaryoya eşdeğerdir.

Sonsuz özyineleme desenlerini önlemek için özyinelemeli işleme kodunun uygun durdurma koşulları içerdiğinden emin olun.

Özel işleme ağacı mantığı

Çoğu Razor bileşen bileşen dosyası (.razor) olarak Razor uygulanır ve kendi çıkışını işlemek için üzerinde RenderTreeBuilder çalışan mantık üretmek için çerçeve tarafından derlenir. Ancak, bir geliştirici yordam C# kodunu kullanarak mantığını el ile uygulayabilir RenderTreeBuilder . Daha fazla bilgi için bkz . ASP.NET Çekirdek Blazor gelişmiş senaryoları (işleme ağacı oluşturma).

Uyarı

El ile işleme ağacı oluşturucu mantığının kullanılması gelişmiş ve güvenli olmayan bir senaryo olarak kabul edilir, genel bileşen geliştirme için önerilmez.

Kod yazılırsa RenderTreeBuilder geliştiricinin kodun doğruluğunu garanti etmesi gerekir. Örneğin, geliştirici şunları sağlamalıdır:

  • ve CloseElement çağrıları OpenElement doğru şekilde dengelenir.
  • Öznitelikler yalnızca doğru yerlere eklenir.

Yanlış el ile işleme ağacı oluşturucu mantığı kilitlenmeler, uygulama veya sunucu kilitlenmeleri ve güvenlik açıkları gibi rastgele tanımsız davranışlara neden olabilir.

El ile işleme ağacı oluşturucusu mantığını aynı karmaşıklık düzeyinde ve derleme kodu veya Microsoft Ara Dil (MSIL) yönergelerini el ile yazmayla aynı tehlike düzeyinde kullanmayı göz önünde bulundurun.

Ek kaynaklar

†Uygulama tarafı Blazor uygulamalarının günlüğe kaydetme için kullandığı arka uç ASP.NET Core web API'sine uygulama.