Megosztás a következőn keresztül:


ASP.NET alapvető ajánlott eljárások

Note

Ez nem a cikk legújabb verziója. Az aktuális kiadásról a cikk .NET 10-es verziójában olvashat.

Warning

A ASP.NET Core ezen verziója már nem támogatott. További információt a .NET és a .NET Core támogatási szabályzatában talál. A jelen cikk .NET 9-es verzióját lásd az aktuális kiadásért .

Készítette : Mike Rousos

Ez a cikk útmutatást nyújt a ASP.NET Core-alkalmazások teljesítményének és megbízhatóságának maximalizálásához.

Gyorsítótárazás agresszíven

A gyorsítótárazásról a cikk több részében tárgyalunk. További információ: A ASP.NET Core gyorsítótárazásának áttekintése.

A gyakori kód elérési útjainak ismertetése

Ebben a cikkben a gyakori kódot tartalmazó elérési út olyan kódútvonalként van definiálva, amelyet gyakran hívnak meg, és ahol a végrehajtási idő nagy része történik. Az intenzív használatú kódútvonalak általában korlátozzák az alkalmazások növelését és teljesítményét, ezt több részben tárgyaljuk a cikkben.

A hívások blokkolásának elkerülése

ASP.NET Core-alkalmazásokat úgy kell megtervezni, hogy egyszerre több kérést dolgozzanak fel. Az aszinkron API-k lehetővé teszik, hogy egy kis szálkészlet több ezer egyidejű kérést kezeljen azáltal, hogy nem várakozik a blokkoló hívásokra. Ahelyett, hogy egy hosszú ideig futó szinkron feladatra várna, a szál egy másik kéréssel is foglalkozhat.

Az ASP.NET Core-alkalmazások gyakori teljesítményproblémája az aszinkron hívások blokkolása. Számos szinkron blokkoló hívás a szálkészlet éhezéséhez és a válaszidő romlásához vezet.

Ne tiltsa le az aszinkron végrehajtást hívással Task.Wait vagy Task<TResult>.Result. Ne szerezzen zárolásokat a szokásos kódutakban. ASP.NET Core-alkalmazások akkor működnek a legjobban, ha a kódot párhuzamosan futtatják. Ne hívjon, Task.Run és azonnal várja meg. ASP.NET Core már futtatja az alkalmazás kódját normál szálkészlet szálakon, így a Task.Run hívása csak felesleges, szükségtelen ütemezést eredményez a szálkészletben. Még ha az ütemezett kód blokkolna is egy szálat, Task.Run ez nem akadályozza meg.

  • Végezze el a gyakori kódot aszinkron módon.
  • Az adathozzáférést, az I/O-t és a hosszan futó műveleti API-kat aszinkron módon hívhatja meg, ha elérhető aszinkron API.
  • Ne használjon Task.Run szinkron API-t aszinkronná tételére.
  • Végezze el a vezérlő- ésRazor oldalműveleteket aszinkron módon. A teljes hívásverem aszinkron az async/await minták kihasználása érdekében.
  • Fontolja meg az olyan üzenetközvetítők használatát, mint az Azure Service Bus, a megszakítás nélküli hívások átterhelésére.

A profilkészítő, például a PerfView, segítségével megtalálhatja a Thread Pool-hoz gyakran hozzáadott szálakat. Az Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start esemény a szálkészlethez hozzáadott szálat jelzi.

Nagyobb gyűjtemények visszaadása több kisebb oldalon

A weblapok egyszerre nem tölthetnek be nagy mennyiségű adatot. Objektumgyűjtemény visszaadásakor fontolja meg, hogy teljesítményproblémákhoz vezethet-e. Állapítsa meg, hogy a terv a következő rossz eredményeket hozhatja-e:

Adjon hozzá lapozást az előző forgatókönyvek enyhítése érdekében. Az oldalméret és oldalindex paraméterek használatával a fejlesztőknek előnyben kell részesíteniük a részleges eredmény visszaadását. Ha teljes eredményre van szükség, a kiszolgálói erőforrások zárolásának elkerülése érdekében lapozást kell használni az eredmények kötegeinek aszinkron feltöltéséhez.

További információ a lapozásról és a visszaadott rekordok számának korlátozásáról:

Visszatérés IEnumerable<T> vagy IAsyncEnumerable<T>

A műveletből való IEnumerable<T> visszatérés szinkronizált gyűjteményiterációt eredményez a szerializáló által. Ennek eredménye a hívások blokkolása és a szálkészlet éhezésének lehetősége. A szinkron számbavétel elkerülése érdekében használja ToListAsync a számbavétel visszaadása előtt.

Az ASP.NET Core 3.0-val kezdődően a IAsyncEnumerable<T> az aszinkron enumeráció alternatívájaként IEnumerable<T> is használható. További információ: Vezérlőművelet visszatérési típusai.

Nagy méretű objektumfoglalások minimalizálása

A .NET szemétgyűjtő automatikusan kezeli a memória lefoglalását és felszabadítását ASP.NET Core-alkalmazásokban. Az automatikus szemétgyűjtés általában azt jelenti, hogy a fejlesztőknek nem kell aggódniuk a memória felszabadításának módja vagy időzítése miatt. A nem hivatkozott objektumok megtisztítása azonban processzoridőt igényel, ezért a fejlesztőknek minimálisra kell csökkenteni az objektumok gyakori kódot tartalmazó elérési utakon való elosztását. A szemétgyűjtés különösen költséges nagy objektumok esetén (>= 85 000 bájt). A nagyméretű objektumok a nagy méretű objektum halomán vannak tárolva, és teljes (2. generációs) szemétgyűjtést igényelnek a tisztításhoz. A 0. és az 1. generációs gyűjteményektől eltérően a 2. generációs gyűjteményekhez az alkalmazás végrehajtásának ideiglenes felfüggesztése szükséges. A nagy objektumok gyakori lefoglalása és lefoglalásának megszüntetése inkonzisztens teljesítményt okozhat.

Recommendations:

  • Fontolja meg a gyakran használt nagy objektumok gyorsítótárazását. A nagy objektumok gyorsítótárazása megakadályozza, hogy költséges lefoglalásokat kelljen végezni.
  • Nagyméretű tömbök tárolására használjon ArrayPool<T> készletpuffereket.
  • Ne foglal le sok, rövid élettartamú nagy objektumot a gyakori kódot tartalmazó útvonalakon.

Az előzőhez hasonló memóriaproblémák diagnosztizálhatók a PerfView szemétgyűjtési (GC) statisztikáinak áttekintésével és a következők vizsgálatával:

  • A szemétgyűjtés szüneteltetési ideje.
  • A processzoridő hány százaléka kerül szemétgyűjtésbe.
  • Hány szemétgyűjtési művelet van a 0., 1. és 2. generációban?

További információ: Szemétgyűjtés és teljesítmény.

Adathozzáférés és I/O optimalizálása

Az adattárral és más távoli szolgáltatásokkal folytatott interakciók gyakran a ASP.NET Core-alkalmazások leglassabb részei. Az adatok hatékony olvasása és írása kritikus fontosságú a jó teljesítmény érdekében.

Recommendations:

  • Az összes adatelérési API-t aszinkron módon kell meghívni.
  • Ne kérjen le több adatot, mint amennyi szükséges. A lekérdezések írásával csak az aktuális HTTP-kéréshez szükséges adatokat adja vissza.
  • Fontolja meg az adatbázisból vagy távoli szolgáltatásból lekért gyakran használt adatok gyorsítótárazását, ha a kissé elavult adatok elfogadhatók. A forgatókönyvtől függően használja a MemoryCache-t vagy az DistributedCache-t. További információért lásd: Válasz gyorsítótárazása az ASP.NET Core-ban.
  • Minimalizálja a hálózati körutakat. A cél az, hogy több hívás helyett egyetlen hívásban kérje le a szükséges adatokat.
  • Használjonnyomkövetési nélküli lekérdezéseket az Entity Framework Core-ban, amikor adatokhoz csak olvasásra van szükség. EF Core hatékonyabban tudja visszaadni a nyomkövetés nélküli lekérdezések eredményeit.
  • Végezze el a LINQ-lekérdezések szűrését .Whereés összesítését (például , .Selectvagy .Sum utasításokkal), hogy a szűrést az adatbázis végezze el.
  • Fontolja meg, hogy az EF Core bizonyos lekérdezési operátorokat ügyféloldalon old fel, ami nem hatékony lekérdezés-végrehajtáshoz vezethet. További információ: Ügyfélértékelési teljesítményproblémák.
  • Ne használjon leképezési lekérdezéseket gyűjteményeken, ami "N + 1" SQL-lekérdezések futtatását eredményezheti. További információ: Korrelált al lekérdezések optimalizálása.

A következő megközelítések növelhetik a teljesítményt a nagy léptékű alkalmazásokban:

Javasoljuk, hogy a kódbázis véglegesítése előtt mérje fel az előző nagy teljesítményű megközelítések hatását. Előfordulhat, hogy a lefordított lekérdezések további összetettsége nem indokolja a teljesítmény javulását.

A lekérdezési problémák az Application Insights vagy profilkészítési eszközök használatával történő adathozzáféréssel töltött idő áttekintésével észlelhetők. A legtöbb adatbázis a gyakran végrehajtott lekérdezésekkel kapcsolatos statisztikákat is elérhetővé teszi.

HTTP-kapcsolatok készlete a HttpClientFactory használatával

Bár HttpClient implementálja az IDisposable interfészt, újra felhasználható. A bezárt HttpClient példányok rövid ideig nyitva hagyják az TIME_WAIT állapotban lévő aljzatokat. Ha gyakran használ objektumokat létrehozó és megsemmisítő HttpClient kódútvonalat, az alkalmazás kimerítheti a rendelkezésre álló szoftvercsatornákat. HttpClientFactory ASP.NET Core 2.1-ben került bevezetésre a probléma megoldásaként. Kezeli a HTTP-kapcsolatok készletezését a teljesítmény és a megbízhatóság optimalizálása érdekében. További információ: Rugalmas HttpClientFactory HTTP-kérések implementálása.

Recommendations:

A gyakori kódútvonalak gyors megőrzése

Azt szeretné, hogy az összes kód gyors legyen. Az optimalizálás szempontjából a leggyakrabban használt kódútvonalak a legkritikusabbak. Ezek a következők:

  • Az alkalmazás kérésfeldolgozási folyamatának köztes szoftver összetevői, különösen a köztes szoftverek a folyamat korai szakaszában futnak. Ezek az összetevők nagy hatással vannak a teljesítményre.
  • Minden kéréshez vagy kérésenként többször végrehajtott kód. Ilyenek például az egyéni naplózás, az engedélyezési kezelők vagy az átmeneti szolgáltatások inicializálása.

Recommendations:

Hosszan futó feladatok végrehajtása HTTP-kérelmeken kívül

A ASP.NET Core-alkalmazásra irányuló kérelmek többségét egy vezérlő vagy oldalmodell kezelheti, amely meghívja a szükséges szolgáltatásokat, és HTTP-választ ad vissza. Bizonyos, hosszú ideig futó feladatokat tartalmazó kérések esetében jobb, ha a teljes kérés-válasz folyamatot aszinkron módon hajtja végre.

Recommendations:

  • Ne várja meg, amíg a hosszú ideig futó feladatok befejeződnek a szokásos HTTP-kérésfeldolgozás részeként.
  • Fontolja meg a hosszú ideig futó kérések kezelését háttérfolyamatokkal, vagy esetleg egy Azure Function-nel és/vagy üzenetközvetítő, például az Azure Service Bus használatával. A folyamaton kívüli munka elvégzése különösen hasznos a processzorigényes feladatok esetében.
  • Használjon valós idejű kommunikációs lehetőségeket, például SignalRaz ügyfelekkel való aszinkron kommunikációhoz.

Ügyfél eszközök kicsinyítése

ASP.NET Összetett előtérrendszerekkel rendelkező Core-alkalmazások gyakran számos JavaScript-, CSS- vagy képfájlt szolgálnak ki. A kezdeti terheléskérések teljesítménye a következőkkel javítható:

  • Bundling, amely több fájlt egyesít egybe.
  • Minifying, amely csökkenti a fájlok méretét azáltal, hogy eltávolítja a térközt és a megjegyzéseket.

Recommendations:

  • Használja a csomagkézbesítési és minifikációs irányelveket, amelyek a kompatibilis eszközöket említik, és bemutatják, hogyan használható ASP.NET Core címkéje environment mind a Development környezetek, mind Production a környezetek kezelésére.
  • Fontolja meg más külső eszközök, például a Webpack használatát az összetett ügyféleszköz-kezeléshez.

Válaszok tömörítése

A válasz méretének csökkentése általában növeli az alkalmazások válaszkészségét, gyakran drámai módon. A hasznos adatok méretének csökkentésének egyik módja az alkalmazás válaszainak tömörítése. További információ: Választömörítés.

A legújabb ASP.NET Core-kiadás használata

A ASP.NET Core minden új kiadása teljesítménybeli fejlesztéseket tartalmaz. A .NET és a ASP.NET Core optimalizálása azt jelenti, hogy az újabb verziók általában felülmúlják a régebbi verziókat. A .NET Core 2.1 például támogatja a lefordított reguláris kifejezéseket, és kihasználta a Span<T> előnyeit. ASP.NET Core 2.2 támogatja a HTTP/2-t. ASP.NET Core 3.0 számos olyan fejlesztést tartalmaz, amelyek csökkentik a memóriahasználatot és javítják az átviteli sebességet. Ha a teljesítmény prioritást élvez, érdemes lehet a ASP.NET Core aktuális verziójára frissíteni.

Kivételek minimalizálása

A kivételeknek ritkanak kell lenniük. A kivételek kivetése és elfogása lassú a többi kódfolyamat-mintához képest. Emiatt nem szabad kivételeket használni a normál programfolyamat szabályozásához.

Recommendations:

  • Ne használja a kivételek dobását vagy elkapását a normál programfolyamat részeként, különösen kritikus kódútvonalakon.
  • Használja az alkalmazás logikáját a kivételt okozó feltételek észlelésére és kezelésére.
  • Dobja ki vagy fogja el a kivételeket szokatlan vagy váratlan körülmények esetén.

Az alkalmazásdiagnosztikai eszközök, például az Application Insights segíthetnek azonosítani a teljesítményt befolyásoló gyakori kivételeket az alkalmazásokban.

Kerülje a szinkron olvasást vagy írást a HttpRequest/HttpResponse törzsben

A ASP.NET Core-ban minden I/O aszinkron. A kiszolgálók implementálják az Stream interfészt, amely szinkron és aszinkron túlterheléssel is rendelkezik. Az aszinkron elemeket előnyben kell részesíteni a szálkészlet szálainak blokkolásának elkerülése érdekében. A szálak blokkolása a szálkészlet éhezéséhez vezethet.

Ne csinálja ezt: Az alábbi példa a ReadToEnd-t használja. Az aktuális szálat letiltja, hogy megvárja az eredményt. Ez egy példa a aszinkron-fölött-szinkron működésre.

public class BadStreamReaderController : Controller
{
    [HttpGet("/contoso")]
    public ActionResult<ContosoData> Get()
    {
        var json = new StreamReader(Request.Body).ReadToEnd();

        return JsonSerializer.Deserialize<ContosoData>(json);
    }
}

Az előző kódban Get szinkron módon beolvassa a teljes HTTP-kérelem törzsét a memóriába. Ha az ügyfél lassan tölt fel, az alkalmazás szinkronizálást végez aszinkron módon. Az alkalmazás aszinkron módon szinkronizál, mert Kestrelnem támogatja a szinkron olvasást.

Tegye a következőt: Az alábbi példa olvasás közben használja ReadToEndAsync és nem blokkolja a szálat.

public class GoodStreamReaderController : Controller
{
    [HttpGet("/contoso")]
    public async Task<ActionResult<ContosoData>> Get()
    {
        var json = await new StreamReader(Request.Body).ReadToEndAsync();

        return JsonSerializer.Deserialize<ContosoData>(json);
    }

}

Az előző kód aszinkron módon beolvassa a teljes HTTP-kérelem törzsét a memóriába.

Warning

Ha a kérés nagy, a teljes HTTP-kérelem törzsének a memóriába való beolvasása memóriahiányhoz (OOM) vezethet. Az OOM szolgáltatásmegtagadást eredményezhet. További információért lásd az ebben a cikkben található Nagyméretű kérelemtörzsek vagy választestek memóriába olvasásának elkerülése részt.

Tegye a következőt: Az alábbi példa teljesen aszinkron módon működik, nem pufferelt kérelemtörzset használva:

public class GoodStreamReaderController : Controller
{
    [HttpGet("/contoso")]
    public async Task<ActionResult<ContosoData>> Get()
    {
        return await JsonSerializer.DeserializeAsync<ContosoData>(Request.Body);
    }
}

Az előző kód aszinkron módon de-szerializálja a kérelem törzsét egy C# objektummá.

A ReadFormAsync előnyben részesítése a Request.Form-hez

HttpContext.Request.ReadFormAsynchelyett használja HttpContext.Request.Form. HttpContext.Request.Form csak a következő feltételekkel olvasható biztonságosan:

  • Az űrlap beolvasását egy ReadFormAsync hívása végezte el.
  • A gyorsítótárban tárolt űrlapértéket a következő használatával olvassuk be: HttpContext.Request.Form

Ne tegye a következőt: Az alábbi példa a következőt használja HttpContext.Request.Form: HttpContext.Request.Forma szinkron az aszinkron felett használatával a szálkészlet éhezéséhez vezethet.

public class BadReadController : Controller
{
    [HttpPost("/form-body")]
    public IActionResult Post()
    {
        var form =  HttpContext.Request.Form;

        Process(form["id"], form["name"]);

        return Accepted();
    }

Tegye a következőt: Az alábbi példa az űrlap törzsének aszinkron olvasására használja HttpContext.Request.ReadFormAsync .

public class GoodReadController : Controller
{
    [HttpPost("/form-body")]
    public async Task<IActionResult> Post()
    {
       var form = await HttpContext.Request.ReadFormAsync();

        Process(form["id"], form["name"]);

        return Accepted();
    }

Kerülje a nagyméretű kérelemtestek vagy választestek memóriába való beolvasását

A .NET-ben minden 85 000 bájtnál nagyobb vagy egyenlő objektumfoglalás a nagy objektum halomba (LOH) kerül. A nagy objektumok kétféleképpen drágák:

  • A foglalási költség magas, mert egy újonnan foglalású nagy objektum memóriáját fel kell szabadítani. A CLR garantálja, hogy az összes újonnan lefoglalt objektum memóriája törlődik.
  • A LOH-t a halom többi részével gyűjtjük össze. A LOH teljes szemétgyűjtést vagy Gen2-gyűjteményt igényel.

Ez a blogbejegyzés tömören ismerteti a problémát:

Egy nagy objektum lefoglalásakor Gen 2 objektumként jelölik meg. Nem Gen 0, mint a kisebb objektumok esetében. A következmény az, hogy ha elfogy a memória a LOH-ban, a GC nem csak a LOH-ot, hanem az egész felügyelt halmot megtisztítja. Így megtisztítja Gen 0, Gen 1 és Gen 2 beleértve LOH. Ezt nevezik teljes szemétgyűjtésnek, és ez a leginkább időigényes szemétgyűjtés. Számos alkalmazás esetében elfogadható. De határozottan nem a nagy teljesítményű webkiszolgálók esetében, ahol kevés nagy memóriapufferre van szükség egy átlagos webes kérés kezeléséhez (olvasás szoftvercsatornából, dekompresszió, JSON dekódolása stb.).

Nagy kérés- vagy választörzs tárolása egyetlen byte[] vagy string:

  • Előfordulhat, hogy a LOH-ban gyorsan elfogy a hely.
  • Teljesítményproblémákat okozhat az alkalmazás számára, mert teljes memóriagyűjtések futnak.

Szinkron adatfeldolgozási API használata

Amikor egy olyan szerializálót/deszerializálót használ, amely csak a szinkron olvasásokat és írásokat támogatja (például Json.NET):

  • Aszinkron módon tárolja az adatokat a memóriában, mielőtt átadná őket a szerializálónak/deszerializálónak.

Warning

Ha a kérés nagy, az memóriahiányhoz (OOM) vezethet. Az OOM szolgáltatásmegtagadást eredményezhet. További információért lásd az ebben a cikkben található Nagyméretű kérelemtörzsek vagy választestek memóriába olvasásának elkerülése részt.

ASP.NET Core 3.0 alapértelmezés szerint JSON-szerializálást használ System.Text.Json . System.Text.Json:

  • JSON-t olvas és ír aszinkron módon.
  • UTF-8 szövegre van optimalizálva.
  • Általában nagyobb teljesítményt nyújt, mint Newtonsoft.Json.

Ne tárolja az IHttpContextAccessor.HttpContext mezőt

Az IHttpContextAccessor.HttpContext visszaadja az HttpContext aktív kérést, amikor a kérelemszálból éri el. A IHttpContextAccessor.HttpContext mezőt nem szabad egy mezőben vagy változóban tárolni.

Ne tegye a következőt: Az alábbi példa egy mezőben tárolja a HttpContext mezőt, majd később megpróbálja használni.

public class MyBadType
{
    private readonly HttpContext _context;
    public MyBadType(IHttpContextAccessor accessor)
    {
        _context = accessor.HttpContext;
    }

    public void CheckAdmin()
    {
        if (!_context.User.IsInRole("admin"))
        {
            throw new UnauthorizedAccessException("The current user isn't an admin");
        }
    }
}

Az előző kód gyakran rögzít null értéket vagy helytelent HttpContext a konstruktorban.

Tegye a következőt: A következő példa:

  • IHttpContextAccessor-t egy mezőben tárolja.
  • A mezőt a HttpContext megfelelő időpontban használja, és ellenőrzi a null mezőt.
public class MyGoodType
{
    private readonly IHttpContextAccessor _accessor;
    public MyGoodType(IHttpContextAccessor accessor)
    {
        _accessor = accessor;
    }

    public void CheckAdmin()
    {
        var context = _accessor.HttpContext;
        if (context != null && !context.User.IsInRole("admin"))
        {
            throw new UnauthorizedAccessException("The current user isn't an admin");
        }
    }
}

Ne érje el a HttpContextet több szálból

HttpContext nincs szálbiztos. HttpContext A több szálból való párhuzamos hozzáférés váratlan viselkedést eredményezhet, például a kiszolgáló leállítja a válaszadást, összeomlik és adatsérülést okoz.

Ne tegye a következőt: Az alábbi példa három párhuzamos kérést készít, és naplózza a bejövő kérelem elérési útját a kimenő HTTP-kérés előtt és után. A kérelem elérési útja több szálból érhető el, akár párhuzamosan is.

public class AsyncBadSearchController : Controller
{       
    [HttpGet("/search")]
    public async Task<SearchResults> Get(string query)
    {
        var query1 = SearchAsync(SearchEngine.Google, query);
        var query2 = SearchAsync(SearchEngine.Bing, query);
        var query3 = SearchAsync(SearchEngine.DuckDuckGo, query);

        await Task.WhenAll(query1, query2, query3);

        var results1 = await query1;
        var results2 = await query2;
        var results3 = await query3;

        return SearchResults.Combine(results1, results2, results3);
    }       

    private async Task<SearchResults> SearchAsync(SearchEngine engine, string query)
    {
        var searchResults = _searchService.Empty();
        try
        {
            _logger.LogInformation("Starting search query from {path}.", 
                                    HttpContext.Request.Path);
            searchResults = _searchService.Search(engine, query);
            _logger.LogInformation("Finishing search query from {path}.", 
                                    HttpContext.Request.Path);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed query from {path}", 
                             HttpContext.Request.Path);
        }

        return await searchResults;
    }

Tegye a következőt: Az alábbi példa az összes adatot átmásolja a bejövő kérésből a három párhuzamos kérés végrehajtása előtt.

public class AsyncGoodSearchController : Controller
{       
    [HttpGet("/search")]
    public async Task<SearchResults> Get(string query)
    {
        string path = HttpContext.Request.Path;
        var query1 = SearchAsync(SearchEngine.Google, query,
                                 path);
        var query2 = SearchAsync(SearchEngine.Bing, query, path);
        var query3 = SearchAsync(SearchEngine.DuckDuckGo, query, path);

        await Task.WhenAll(query1, query2, query3);

        var results1 = await query1;
        var results2 = await query2;
        var results3 = await query3;

        return SearchResults.Combine(results1, results2, results3);
    }

    private async Task<SearchResults> SearchAsync(SearchEngine engine, string query,
                                                  string path)
    {
        var searchResults = _searchService.Empty();
        try
        {
            _logger.LogInformation("Starting search query from {path}.",
                                   path);
            searchResults = await _searchService.SearchAsync(engine, query);
            _logger.LogInformation("Finishing search query from {path}.", path);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed query from {path}", path);
        }

        return await searchResults;
    }

Ne használja a HttpContextet a kérés befejezése után

HttpContext csak akkor érvényes, ha aktív HTTP-kérés található a ASP.NET Core-folyamatban. A teljes ASP.NET Core-folyamat a meghatalmazottak aszinkron lánca, amely minden kérést végrehajt. Amikor a Task láncból visszaadott érték befejeződik, az HttpContext újrafeldolgozódik.

Ne tegye a következőt: Az alábbi példa async void a HTTP-kérést az első await elérésekor teszi teljessé:

  • A async void használata MINDIG rossz gyakorlat az ASP.NET Core-alkalmazásokban.
  • A példakód a HTTP-kérés befejezése után fér hozzá HttpResponse .
  • A késői hozzáférés összeomlást okoz a folyamatban.
public class AsyncBadVoidController : Controller
{
    [HttpGet("/async")]
    public async void Get()
    {
        await Task.Delay(1000);

        // The following line will crash the process because of writing after the 
        // response has completed on a background thread. Notice async void Get()

        await Response.WriteAsync("Hello World");
    }
}

Tegye a következőt: Az alábbi példa egy Task értéket ad vissza a keretrendszernek, így a HTTP-kérés nem fejeződik be, amíg a művelet be nem fejeződik.

public class AsyncGoodTaskController : Controller
{
    [HttpGet("/async")]
    public async Task Get()
    {
        await Task.Delay(1000);

        await Response.WriteAsync("Hello World");
    }
}

Ne rögzítse a HttpContextet háttérszálakban

Ne tegye ezt: Az alábbi példa megmutatja, hogy egy lezárás rögzíti a HttpContext értéket a Controller tulajdonságból. Ez rossz gyakorlat, mert a munkaelem a következőt teheti:

  • Végezze a kérelem területén kívül.
  • Próbálja meg a hibás HttpContext beolvasását.
[HttpGet("/fire-and-forget-1")]
public IActionResult BadFireAndForget()
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        var path = HttpContext.Request.Path;
        Log(path);
    });

    return Accepted();
}

Tegye a következőt: A következő példa:

  • Másolja a háttérfeladatban szükséges adatokat a kérés során.
  • A vezérlő nem hivatkozik semmire.
[HttpGet("/fire-and-forget-3")]
public IActionResult GoodFireAndForget()
{
    string path = HttpContext.Request.Path;
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        Log(path);
    });

    return Accepted();
}

A háttérfeladatokat üzemeltetett szolgáltatásként kell végrehajtani. További információ: Háttérfeladatok üzemeltetett szolgáltatásokkal.

Ne rögzítse a háttérszálak vezérlőibe injektált szolgáltatásokat

Ne tegyük ezt: Az alábbi példa egy closure-t mutat be, amely megragadja a DbContextController műveletparamétert. Ez egy rossz gyakorlat. A munkaelem a kérelem hatókörén kívül is futtatható. A ContosoDbContext a kérelemhez van rendelve, ami ObjectDisposedException-et eredményez.

[HttpGet("/fire-and-forget-1")]
public IActionResult FireAndForget1([FromServices]ContosoDbContext context)
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        context.Contoso.Add(new Contoso());
        await context.SaveChangesAsync();
    });

    return Accepted();
}

Tegye a következőt: A következő példa:

  • Egy IServiceScopeFactory elemet szúr be annak érdekében, hogy hatókört hozzon létre a háttérmunkaelemben. IServiceScopeFactory egy singleton.
  • Létrehoz egy új függőséginjektálási hatókört a háttérszálban.
  • A vezérlő nem hivatkozik semmire.
  • Nem rögzíti a ContosoDbContext bejövő kérést.
[HttpGet("/fire-and-forget-3")]
public IActionResult FireAndForget3([FromServices]IServiceScopeFactory 
                                    serviceScopeFactory)
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        await using (var scope = serviceScopeFactory.CreateAsyncScope())
        {
            var context = scope.ServiceProvider.GetRequiredService<ContosoDbContext>();

            context.Contoso.Add(new Contoso());

            await context.SaveChangesAsync();                                        
        }
    });

    return Accepted();
}

A következő kiemelt kód:

  • Létrehoz egy hatókört a háttérművelet tartamára, és szolgáltatásokat old fel belőle.
  • A megfelelő hatókörből használja ContosoDbContext .
[HttpGet("/fire-and-forget-3")]
public IActionResult FireAndForget3([FromServices]IServiceScopeFactory 
                                    serviceScopeFactory)
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        await using (var scope = serviceScopeFactory.CreateAsyncScope())
        {
            var context = scope.ServiceProvider.GetRequiredService<ContosoDbContext>();

            context.Contoso.Add(new Contoso());

            await context.SaveChangesAsync();                                        
        }
    });

    return Accepted();
}

A válasz törzsének elindítása után ne módosítsa az állapotkódot vagy a fejléceket

ASP.NET Core nem puffereli a HTTP-válasz törzsét. A válasz első megírása:

  • A fejléceket a rendszer a törzs adattömbjével együtt elküldi az ügyfélnek.
  • A válaszfejlécek már nem módosíthatók.

Ne tegye a következőt: A következő kód megpróbálja hozzáadni a válaszfejléceket, miután a válasz már elindult:

app.Use(async (context, next) =>
{
    await next();

    context.Response.Headers["test"] = "test value";
});

Az előző kód kivételt fog eredményezni, context.Response.Headers["test"] = "test value"; ha next() a válaszra íródott.

Tegye a következőt: Az alábbi példa ellenőrzi, hogy a HTTP-válasz elindult-e a fejlécek módosítása előtt.

app.Use(async (context, next) =>
{
    await next();

    if (!context.Response.HasStarted)
    {
        context.Response.Headers["test"] = "test value";
    }
});

Tegye a következőt: Az alábbi példa a fejlécek beállítása céljából használható HttpResponse.OnStarting, mielőtt a válaszfejlécek ki lesznek küldve az ügyfélnek.

Annak ellenőrzése, hogy a válasz nem kezdődött-e el, lehetővé teszi egy visszahívás regisztrálását, amelyet közvetlenül a válaszfejlécek írása előtt hív meg a rendszer. Annak ellenőrzése, hogy a válasz nem indult-e el:

  • Lehetővé teszi a fejlécek éppen időbeni hozzáfűzését vagy felülbírálását.
  • Nincs szükség a folyamat következő köztes szoftverének ismeretére.
app.Use(async (context, next) =>
{
    context.Response.OnStarting(() =>
    {
        context.Response.Headers["someheader"] = "somevalue";
        return Task.CompletedTask;
    });

    await next();
});

Ne hívja meg a next() függvényt, ha már megkezdte az írást a válasz törzsébe.

Csak akkor hívják meg az összetevőket, ha lehetséges számukra, hogy kezeljék és módosítsák a választ.

Folyamatközi üzemeltetés használata az IIS-vel

A folyamaton belüli üzemeltetés használatával egy ASP.NET Core-alkalmazás ugyanabban a folyamatban fut, mint az IIS feldolgozói folyamata. A folyamaton belüli üzemeltetés jobb teljesítményt nyújt a folyamaton kívüli üzemeltetéshez képest, mivel a kérések nem a visszacsatolási adapteren keresztül futnak. A visszacsatolási adapter egy olyan hálózati adapter, amely a kimenő hálózati forgalmat ugyanahhoz a géphez adja vissza. Az IIS a folyamatkezelést a Windows Folyamataktiválási Szolgáltatás (WAS)segítségével kezeli.

A projektek alapértelmezés szerint a ASP.NET Core 3.0-s vagy újabb verziójában a folyamaton belüli üzemeltetési modellre kerülnek.

További információ: Host ASP.NET Core on Windows with IIS

Ne feltételezze, hogy a HttpRequest.ContentLength értéke nem null

HttpRequest.ContentLength null értékű, ha a Content-Length fejléc nem érkezik meg. Ebben az esetben a null érték azt jelenti, hogy a kérelem törzsének hossza nem ismert; ez nem jelenti azt, hogy a hossz nulla. Mivel minden nullával történő összehasonlítás (kivéve ==) hamis értéket ad vissza, például az Request.ContentLength > 1024 összehasonlítás akkor térhet vissza false értékben, ha a kérés törzsének mérete meghaladja az 1024 bájtot. Az ennek az ismeretének hiányában biztonsági rések alakulhatnak ki az alkalmazásokban. Előfordulhat, hogy úgy gondolja, védelmet nyújt a túl nagy kérések ellen, pedig valójában nem így van.

További információkért tekintse meg ezt a StackOverflow-választ.

Vállalati webalkalmazás-minták

A megbízható, biztonságos, teljesíthető, tesztelhető és méretezhető ASP.NET Core-alkalmazás létrehozásával kapcsolatos útmutatásért tekintse meg Vállalati webalkalmazás-mintákcímű témakört. Rendelkezésre áll egy teljesen felhasználásra kész példawebalkalmazás, amely implementálja a mintákat.