NodeJS'de Hata Sınıflarıyla Daha İyi Hata İşleme
Yayınlanan: 2022-03-10error
sınıfı modelini ve uygulamalarınızdaki hataları daha iyi, daha verimli bir şekilde ele almak için nasıl kullanılacağını açıklar.Hata işleme, yazılım geliştirmenin gerçekten hak ettiği ilgiyi görmeyen kısımlarından biridir. Bununla birlikte, sağlam uygulamalar oluşturmak, hatalarla düzgün bir şekilde ilgilenmeyi gerektirir.
NodeJS'de hataları düzgün bir şekilde ele almadan yapabilirsiniz, ancak NodeJS'nin asenkron doğası nedeniyle, yanlış kullanım veya hatalar, özellikle uygulamalarda hata ayıklarken, çok yakında acı çekmenize neden olabilir.
Devam etmeden önce, hata sınıflarının nasıl kullanılacağını tartışacağımız hata türlerine dikkat çekmek istiyorum.
Operasyonel Hatalar
Bunlar, bir programın çalışma süresi sırasında keşfedilen hatalardır. Operasyonel hatalar hata değildir ve çoğunlukla bir veritabanı sunucusunun zaman aşımına uğraması veya bir kullanıcının bir girdi alanına SQL sorguları girerek SQL enjeksiyonu denemeye karar vermesi gibi bir veya birkaç harici faktörün bir kombinasyonu nedeniyle zaman zaman meydana gelebilir.
Aşağıda daha fazla operasyonel hata örneği verilmiştir:
- Bir veritabanı sunucusuna bağlanılamadı;
- Kullanıcı tarafından geçersiz girişler (sunucu
400
yanıt koduyla yanıt verir); - İstek zaman aşımına uğradı;
- Kaynak bulunamadı (sunucu bir 404 yanıt koduyla yanıt verir);
- Sunucu
500
yanıtla döner.
Ayrıca, Operasyonel Hataların karşılığına kısaca değinmeye değer.
Programcı Hataları
Bunlar, kod değiştirilerek çözülebilecek programdaki hatalardır. Bu tür hatalar, kodun kırılması sonucu meydana geldikleri için ele alınamaz. Bu hatalara örnek:
- Tanımlanmamış bir nesne üzerindeki bir özelliği okumaya çalışmak.
const user = { firstName: 'Kelvin', lastName: 'Omereshone', } console.log(user.fullName) // throws 'undefined' because the property fullName is not defined
- Geri arama olmadan zaman uyumsuz bir işlevi çağırma veya çağırma.
- Bir sayının beklendiği yerde bir dize geçirme.
Bu makale, NodeJS'de İşlemsel Hata işleme hakkındadır. NodeJS'deki hata işleme, diğer dillerdeki hata işlemeden önemli ölçüde farklıdır. Bunun nedeni JavaScript'in eşzamansız doğası ve JavaScript'in hatalara açık olmasıdır. Açıklamama izin ver:
JavaScript'te, atabileceğiniz tek şey error
sınıfının örnekleri değildir. Kelimenin tam anlamıyla herhangi bir veri türünü atabilirsiniz, bu açıklığa diğer diller tarafından izin verilmez.
Örneğin, bir JavaScript geliştiricisi, aşağıdaki gibi bir hata nesnesi örneği yerine bir sayı atmaya karar verebilir:
// bad throw 'Whoops :)'; // good throw new Error('Whoops :)')
Diğer veri türlerini atarken sorunu göremeyebilirsiniz, ancak bunu yapmak hata ayıklamayı daha zor hale getirecektir çünkü bir yığın izlemesi ve hata ayıklama için gereken Error nesnesinin gösterdiği diğer özellikleri alamazsınız.
Error sınıfı modeline ve NodeJS'de hata işleme için nasıl daha iyi bir yol olduğuna bakmadan önce, hata işlemedeki bazı yanlış kalıplara bakalım.
Kötü Hata İşleme Modeli #1: Geri Aramaların Yanlış Kullanımı
Gerçek dünya senaryosu : Kodunuz, döndürmesini beklediğiniz sonucu almak için geri arama gerektiren harici bir API'ye bağlıdır.
Aşağıdaki kod parçasını alalım:
'use strict'; const fs = require('fs'); const write = function () { fs.mkdir('./writeFolder'); fs.writeFile('./writeFolder/foobar.txt', 'Hello World'); } write();
NodeJS 8 ve yukarısına kadar, yukarıdaki kod meşruydu ve geliştiriciler komutları başlatıp unutacaktı. Bu, geliştiricilerin bu tür işlev çağrılarına bir geri arama sağlamalarının gerekmediği ve bu nedenle hata işlemeyi dışarıda bırakabileceği anlamına gelir. writeFolder
oluşturulmadığında ne olur? writeFile
çağrısı yapılmayacak ve bu konuda hiçbir şey bilemeyiz. Bu aynı zamanda yarış durumuna da neden olabilir, çünkü ikinci komut tekrar başladığında ilk komut bitmemiş olabilir, bilemezsiniz.
Yarış koşulunu çözerek bu sorunu çözmeye başlayalım. Bunu, ikinci komutla yazmadan önce dizinin gerçekten var olduğundan emin olmak için ilk mkdir
komutuna bir geri çağrı vererek yapardık. Yani kodumuz aşağıdaki gibi görünecek:
'use strict'; const fs = require('fs'); const write = function () { fs.mkdir('./writeFolder', () => { fs.writeFile('./writeFolder/foobar.txt', 'Hello World!'); }); } write();
Yarış koşulunu çözmüş olsak da, henüz işimiz bitmedi. Kodumuz hala sorunlu çünkü ilk komut için bir geri arama kullanmamıza rağmen, writeFolder
klasörünün oluşturulup oluşturulmadığını bilmemizin bir yolu yok. Klasör oluşturulmamışsa, ikinci çağrı tekrar başarısız olur, ancak yine de hatayı yine yok saydık. Bunu şu şekilde çözüyoruz…
Geri Aramalarda Hata İşleme
Geri aramalarda hatayı düzgün bir şekilde ele almak için, her zaman önce hata yaklaşımını kullandığınızdan emin olmalısınız. Bunun anlamı, döndürülen verileri (varsa) kullanmaya devam etmeden önce, işlevden döndürülen bir hata olup olmadığını kontrol etmeniz gerektiğidir. Bunu yapmanın yanlış yolunu görelim:
'use strict'; // Wrong const fs = require('fs'); const write = function (callback) { fs.mkdir('./writeFolder', (err, data) => { if (data) fs.writeFile('./writeFolder/foobar.txt', 'Hello World!'); else callback(err) }); } write(console.log);
Yukarıdaki model yanlıştır, çünkü bazen aradığınız API herhangi bir değer döndürmeyebilir veya geçerli bir dönüş değeri olarak sahte bir değer döndürebilir. Görünüşe göre işlev veya API'yi başarılı bir şekilde çağırmış olsanız bile, bu sizi bir hata durumunda bırakır.
Yukarıdaki model de kötü çünkü kullanımı hatanızı tüketecek (hatalarınız gerçekleşmiş olsa bile çağrılmayacak). Ayrıca, bu tür bir hata işleme modelinin bir sonucu olarak kodunuzda neler olduğu hakkında hiçbir fikriniz olmayacak. Yani yukarıdaki kod için doğru yol şudur:
'use strict'; // Right const fs = require('fs'); const write = function (callback) { fs.mkdir('./writeFolder', (err, data) => { if (err) return callback(err) fs.writeFile('./writeFolder/foobar.txt', 'Hello World!'); }); } write(console.log);
Yanlış Hata İşleme Modeli #2: Sözlerin Yanlış Kullanımı
Gerçek dünya senaryosu : Yani Sözleri keşfettiniz ve geri arama cehennemi nedeniyle geri aramalardan çok daha iyi olduklarını düşünüyorsunuz ve kod tabanınızın bağlı olduğu bazı harici API'ler vaat etmeye karar verdiniz. Veya harici bir API'den veya fetch() işlevi gibi bir tarayıcı API'sinden bir söz kullanıyorsunuz.
Bu günlerde NodeJS kod tabanlarımızda gerçekten geri arama kullanmıyoruz, sözler kullanıyoruz. Öyleyse örnek kodumuzu bir söz vererek yeniden uygulayalım:
'use strict'; const fs = require('fs').promises; const write = function () { return fs.mkdir('./writeFolder').then(() => { fs.writeFile('./writeFolder/foobar.txt', 'Hello world!') }).catch((err) => { // catch all potential errors console.error(err) }) }
Yukarıdaki kodu bir mikroskop altına koyalım - fs.mkdir
sözünü başka bir söz zincirine (fs.writeFile çağrısı) bu söz çağrısını bile işlemeden dalladığımızı görebiliriz. Bunu yapmanın daha iyi bir yolunun şöyle olacağını düşünebilirsiniz:
'use strict'; const fs = require('fs').promises; const write = function () { return fs.mkdir('./writeFolder').then(() => { fs.writeFile('./writeFolder/foobar.txt', 'Hello world!').then(() => { // do something }).catch((err) => { console.error(err); }) }).catch((err) => { // catch all potential errors console.error(err) }) }
Ancak yukarıdakiler ölçeklenmez. Bunun nedeni, arayacak daha fazla söz zincirimiz varsa, çözmek için vaat edilen geri arama cehennemine benzer bir şeyle sonuçlanacak olmamızdır. Bu, kodumuzun sağa girintilenmeye devam edeceği anlamına gelir. Elimizde bir söz cehennemi olurdu.
Geri Aramaya Dayalı Bir API Vaat Etmek
Çoğu zaman, o API'deki hataları daha iyi ele almak için kendi başınıza bir geri arama tabanlı API'ye söz vermek istersiniz. Ancak, bunu yapmak gerçekten kolay değil. Nedenini açıklamak için aşağıda bir örnek alalım.
function doesWillNotAlwaysSettle(arg) { return new Promise((resolve, reject) => { doATask(foo, (err) => { if (err) { return reject(err); } if (arg === true) { resolve('I am Done') } }); }); }
Yukarıdakilerden, eğer arg
true
değilse ve doATask
işlevine yapılan çağrıdan bir hatamız yoksa, bu söz uygulamanızda bir bellek sızıntısı olan yalnızca takılacaktır.
Sözlerde Yutulan Senkronizasyon Hataları
Promise yapıcısını kullanmanın çeşitli zorlukları vardır, bu zorluklardan biri; çözülür veya reddedilir çözülmez başka bir duruma geçemez. Bunun nedeni, bir sözün yalnızca tek bir durum alabilmesidir - ya beklemededir ya da çözümlenmiştir/reddedilmiştir. Bu, vaatlerimizde ölü bölgelere sahip olabileceğimiz anlamına gelir. Bunu kodda görelim:
function deadZonePromise(arg) { return new Promise((resolve, reject) => { doATask(foo, (err) => { resolve('I'm all Done'); throw new Error('I am never reached') // Dead Zone }); }); }
Yukarıdakilerden, söz çözülür çözülmez, bir sonraki çizginin ölü bir bölge olduğunu ve asla ulaşılamayacağını görüyoruz. Bu, vaatlerinizde gerçekleştirilen aşağıdaki herhangi bir eşzamanlı hata işleme işleminin yutulacağı ve asla atılmayacağı anlamına gelir.
Gerçek Dünya Örnekleri
Yukarıdaki örnekler, zayıf hata işleme modellerini açıklamaya yardımcı olur, gerçek hayatta görebileceğiniz sorun türlerine bir göz atalım.
Gerçek Dünya Örneği #1 — Hatayı Dizeye Dönüştürme
Senaryo : Bir API'den döndürülen hatanın sizin için yeterince iyi olmadığına karar verdiniz ve buna kendi mesajınızı eklemeye karar verdiniz.
'use strict'; function readTemplate() { return new Promise(() => { databaseGet('query', function(err, data) { if (err) { reject('Template not found. Error: ', + err); } else { resolve(data); } }); }); } readTemplate();
Şimdi yukarıdaki kodda neyin yanlış olduğuna bakalım. Yukarıdan, geliştiricinin, döndürülen hatayı “Şablon bulunamadı” dizesiyle birleştirerek databaseGet
API'si tarafından atılan hatayı iyileştirmeye çalıştığını görüyoruz. Bu yaklaşımın birçok dezavantajı vardır, çünkü birleştirme yapıldığında geliştirici, döndürülen hata nesnesinde dolaylı olarak toString
çalıştırır. Bu şekilde, hatanın döndürdüğü ekstra bilgileri kaybeder (yığın izlemesine veda edin). Yani geliştiricinin şu anda sahip olduğu şey, yalnızca hata ayıklama sırasında kullanışlı olmayan bir dizedir.
Daha iyi bir yol, hatayı olduğu gibi tutmak veya oluşturduğunuz başka bir hataya sarmak ve databaseGet çağrısından atılan hatayı bir özellik olarak eklemektir.
Gerçek Dünya Örneği #2: Hatayı Tamamen Yoksaymak
Senaryo : Belki bir kullanıcı uygulamanıza kaydolurken, bir hata meydana gelirse, sadece hatayı yakalamak ve özel bir mesaj göstermek istersiniz, ancak yakalanan hatayı hata ayıklama amacıyla bile kaydetmeden tamamen yok sayarsınız.
router.get('/:id', function (req, res, next) { database.getData(req.params.userId) .then(function (data) { if (data.length) { res.status(200).json(data); } else { res.status(404).end(); } }) .catch(() => { log.error('db.rest/get: could not get data: ', req.params.userId); res.status(500).json({error: 'Internal server error'}); }) });
Yukarıdan, hatanın tamamen yok sayıldığını ve veritabanına yapılan çağrı başarısız olursa kodun kullanıcıya 500 gönderdiğini görebiliriz. Ancak gerçekte, veritabanı hatasının nedeni, kullanıcı tarafından gönderilen ve 400 durum kodundaki bir hata olan hatalı biçimlendirilmiş veriler olabilir.

Yukarıdaki durumda, geliştirici olarak neyin yanlış gittiğini bilemeyeceğiniz için hata ayıklama dehşetine düşeriz. Her zaman 500 dahili sunucu hatası atıldığı için kullanıcı düzgün bir rapor veremeyecektir. İşvereninizin zamanını ve parasını boşa harcamakla eşdeğer olan sorunu bulmak için saatlerce harcarsınız.
Gerçek Dünya Örneği 3: Bir API'den Atılan Hatayı Kabul Etmemek
Senaryo : Kullanmakta olduğunuz bir API'den bir hata atıldı, ancak bu hatayı kabul etmiyorsunuz, bunun yerine hatayı, hata ayıklama amaçları için işe yaramaz hale getirecek şekilde sıralar ve dönüştürürsünüz.
Aşağıdaki kod örneğini alın:
async function doThings(input) { try { validate(input); try { await db.create(input); } catch (error) { error.message = `Inner error: ${error.message}` if (error instanceof Klass) { error.isKlass = true; } throw error } } catch (error) { error.message = `Could not do things: ${error.message}`; await rollback(input); throw error; } }
Yukarıdaki kodda hata ayıklamaya yol açacak çok şey oluyor. Hadi bir bakalım:
-
try/catch
bloklarını sarmak: Yukarıdan anlayacağınız üzeretry/catch
bloğunu sarıyoruz ki bu çok kötü bir fikir. Normalde hatamızı işlememiz gereken yüzeyi küçültmek içintry/catch
(bunu KURU hata işleme olarak düşünün); - Ayrıca iyileştirme girişiminde hata mesajını da değiştiriyoruz ki bu da iyi bir fikir değil;
- Hatanın
Klass
türünde bir örnek olup olmadığını kontrol ediyoruz ve bu durumda isKlass hatasının boolean özelliğiniisKlass
olarak ayarlıyoruz (ancak bu kontrol geçerse hataKlass
türündedir); - Ayrıca veritabanını çok erken geri alıyoruz çünkü kod yapısından, hata atıldığında veritabanına bile ulaşmamış olma ihtimalimiz yüksek.
Yukarıdaki kodu yazmanın daha iyi bir yolu aşağıdadır:
async function doThings(input) { validate(input); try { await db.create(input); } catch (error) { try { await rollback(); } catch (error) { logger.log('Rollback failed', error, 'input:', input); } throw error; } }
Yukarıdaki pasajda ne yaptığımızı analiz edelim:
- Bir
try/catch
bloğu kullanıyoruz ve yalnızca catch bloğunda, bu geri alma işleviyle ilgili bir şeyler olması durumunda koruma görevi görecek başka birtry/catch
bloğunu kullanıyoruz ve bunu günlüğe kaydediyoruz; - Son olarak, orijinal alınan hatamızı atıyoruz, yani bu hatanın içerdiği mesajı kaybetmeyiz.
Test yapmak
Çoğunlukla kodumuzu (manuel veya otomatik olarak) test etmek istiyoruz. Ancak çoğu zaman sadece olumlu şeyleri test ediyoruz. Sağlam bir test için, hataları ve uç durumları da test etmeniz gerekir. Bu ihmal, hataların üretime girme yolunu bulmasından sorumludur ve bu da daha fazla hata ayıklama süresine mal olur.
İpucu : Daima yalnızca olumlu şeyleri değil (bir uç noktadan 200 durum kodu almak) değil, aynı zamanda tüm hata durumlarını ve tüm uç durumları da test ettiğinizden emin olun.
Gerçek Dünya Örneği 4: İşlenmeyen Redler
Daha önce vaatler kullandıysanız, muhtemelen unhandled rejections
karşılaşmışsınızdır.
İşte işlenmeyen reddetmeler hakkında hızlı bir başlangıç. İşlenmeyen retler, işlenmeyen taahhüt retleridir. Bu, sözün reddedildiği, ancak kodunuzun çalışmaya devam edeceği anlamına gelir.
İşlenmemiş reddedilmelere yol açan yaygın bir gerçek dünya örneğine bakalım.
'use strict'; async function foobar() { throw new Error('foobar'); } async function baz() { throw new Error('baz') } (async function doThings() { const a = foobar(); const b = baz(); try { await a; await b; } catch (error) { // ignore all errors! } })();
İlk bakışta yukarıdaki kod hataya açık görünmeyebilir. Ancak daha yakından baktığımızda, bir kusur görmeye başlıyoruz. Açıklayayım: a
reddedilirse ne olur? Bu, await b
asla ulaşılmadığı anlamına gelir ve bu, işlenmemiş bir reddetme anlamına gelir. Olası bir çözüm, her iki vaatte de Promise.all
kullanmaktır. Yani kod şöyle okurdu:
'use strict'; async function foobar() { throw new Error('foobar'); } async function baz() { throw new Error('baz') } (async function doThings() { const a = foobar(); const b = baz(); try { await Promise.all([a, b]); } catch (error) { // ignore all errors! } })();
İşlenmemiş bir söz reddetme hatasına yol açabilecek başka bir gerçek dünya senaryosu:
'use strict'; async function foobar() { throw new Error('foobar'); } async function doThings() { try { return foobar() } catch { // ignoring errors again ! } } doThings();
Yukarıdaki kod parçacığını çalıştırırsanız, işlenmeyen bir söz reddi alırsınız ve işte nedeni: Açık olmasa da, try/catch
ile işlemeden önce bir söz (foobar) döndürüyoruz. Yapmamız gereken, try/catch
ile yapacağımız sözü beklemektir, böylece kod şöyle okunur:
'use strict'; async function foobar() { throw new Error('foobar'); } async function doThings() { try { return await foobar() } catch { // ignoring errors again ! } } doThings();
Olumsuz Şeyleri Kapatmak
Artık yanlış hata işleme kalıplarını ve olası düzeltmeleri gördüğünüze göre, şimdi Error sınıfı kalıbına ve bunun NodeJS'de yanlış hata işleme sorununu nasıl çözdüğüne geçelim.
Hata Sınıfları
Bu modelde, uygulamamıza bir ApplicationError
sınıfı ile başlayacağız, bu şekilde uygulamalarımızda açıkça attığımız tüm hataların ondan miras alacağını biliyoruz. Bu nedenle, aşağıdaki hata sınıflarıyla başlayacağız:
-
ApplicationError
Bu, diğer tüm hata sınıflarının atasıdır, yani diğer tüm hata sınıfları ondan miras alır. -
DatabaseError
Veritabanı işlemleriyle ilgili herhangi bir hata bu sınıftan miras alacaktır. -
UserFacingError
Bir kullanıcının uygulamayla etkileşime girmesi sonucunda oluşan herhangi bir hata bu sınıftan devralınır.
error
sınıfı dosyamız şöyle görünür:
'use strict'; // Here is the base error classes to extend from class ApplicationError extends Error { get name() { return this.constructor.name; } } class DatabaseError extends ApplicationError { } class UserFacingError extends ApplicationError { } module.exports = { ApplicationError, DatabaseError, UserFacingError }
Bu yaklaşım, uygulamamız tarafından atılan hataları ayırt etmemizi sağlar. Şimdi, eğer hatalı bir istek hatasını (geçersiz kullanıcı girişi) veya bulunamadı hatasını (kaynak bulunamadı) ele almak istiyorsak, UserFacingError
olan temel sınıftan (aşağıdaki kodda olduğu gibi) miras alabiliriz.
const { UserFacingError } = require('./baseErrors') class BadRequestError extends UserFacingError { constructor(message, options = {}) { super(message); // You can attach relevant information to the error instance // (eg. the username) for (const [key, value] of Object.entries(options)) { this[key] = value; } } get statusCode() { return 400; } } class NotFoundError extends UserFacingError { constructor(message, options = {}) { super(message); // You can attach relevant information to the error instance // (eg. the username) for (const [key, value] of Object.entries(options)) { this[key] = value; } } get statusCode() { return 404 } } module.exports = { BadRequestError, NotFoundError }
error
sınıfı yaklaşımının faydalarından biri, bu hatalardan birini, örneğin bir NotFoundError
, bu kod tabanını okuyan her geliştirici, zamanın bu noktasında neler olduğunu anlayabilecektir (eğer kodu okurlarsa). ).
Her bir hata sınıfına özgü birden çok özelliği, o hatanın somutlaştırılması sırasında da iletebileceksiniz.
Diğer bir önemli fayda ise, her zaman bir hata sınıfının parçası olan özelliklere sahip olabilmenizdir, örneğin, bir UserFacing hatası alırsanız, bir statusCode'un her zaman bu hata sınıfının bir parçası olduğunu bilirsiniz, şimdi onu direkt olarak kullanabilirsiniz. kodu daha sonra.
Hata Sınıflarını Kullanmaya İlişkin İpuçları
- Her hata sınıfı için kendi modülünüzü (muhtemelen özel bir modül) yapın, böylece bunu uygulamanıza kolayca aktarabilir ve her yerde kullanabilirsiniz.
- Yalnızca önemsediğiniz hataları atın (hata sınıflarınızın örnekleri olan hatalar). Bu şekilde, hata sınıflarınızın tek Gerçeğin Kaynağınız olduğunu bilirsiniz ve uygulamanızda hata ayıklamak için gerekli tüm bilgileri içerir.
- Soyut bir hata modülüne sahip olmak oldukça faydalıdır çünkü artık uygulamalarımızın verebileceği hatalarla ilgili tüm gerekli bilgilerin tek bir yerde olduğunu biliyoruz.
- Katmanlardaki hataları işleyin. Her yerde hataları ele alıyorsanız, takip etmesi zor olan hata işleme konusunda tutarsız bir yaklaşımınız olur. Katmanlar derken, veritabanı, ekspres/fastify/HTTP katmanları vb. gibi şeyleri kastediyorum.
Hata sınıflarının kodda nasıl göründüğüne bakalım. İşte ekspres bir örnek:
const { DatabaseError } = require('./error') const { NotFoundError } = require('./userFacingErrors') const { UserFacingError } = require('./error') // Express app.get('/:id', async function (req, res, next) { let data try { data = await database.getData(req.params.userId) } catch (err) { return next(err); } if (!data.length) { return next(new NotFoundError('Dataset not found')); } res.status(200).json(data) }) app.use(function (err, req, res, next) { if (err instanceof UserFacingError) { res.sendStatus(err.statusCode); // or res.status(err.statusCode).send(err.errorCode) } else { res.sendStatus(500) } // do your logic logger.error(err, 'Parameters: ', req.params, 'User data: ', req.user) });
Yukarıdakilerden yola çıkarak, Express'in tüm hatalarınızı tek bir yerde işlemenize olanak tanıyan küresel bir hata işleyici ortaya çıkarmasından yararlanıyoruz. Hataları ele aldığımız yerlerde next()
çağrısını görebilirsiniz. Bu çağrı, hataları app.use
bölümünde tanımlanan işleyiciye iletir. Express, async/await'i desteklemediğinden, try/catch
bloklarını kullanıyoruz.
Bu nedenle, yukarıdaki koddan, hatalarımızı işlemek için, atılan hatanın bir UserFacingError
örneği olup olmadığını kontrol etmemiz yeterlidir ve otomatik olarak hata nesnesinde bir statusCode olacağını biliyoruz ve bunu kullanıcıya gönderiyoruz (isteyebilirsiniz). müşteriye iletebileceğiniz belirli bir hata koduna sahip olmak) ve bu hemen hemen bu kadar.
Ayrıca, bu modelde ( error
sınıfı deseni) açıkça atmadığınız diğer her hatanın 500
hata olduğunu fark edeceksiniz, çünkü bu beklenmedik bir şey, yani bu hatayı uygulamanıza açıkça atmadınız. Bu sayede uygulamalarımızda meydana gelen hata türlerini ayırt edebiliyoruz.
Çözüm
Uygulamanızda doğru hata işleme, geceleri daha iyi uyumanızı ve hata ayıklama süresinden tasarruf etmenizi sağlayabilir. İşte bu makaleden almanız gereken bazı önemli noktalar:
- Uygulamanız için özel olarak ayarlanmış hata sınıflarını kullanın;
- Soyut hata işleyicileri uygulayın;
- Her zaman async/await'i kullanın;
- Hataları anlamlı hale getirin;
- Kullanıcı gerekirse söz verir;
- Uygun hata durumlarını ve kodlarını döndürün;
- Söz kancalarından yararlanın.
Haftada bir kez teslim edilen kullanışlı ön uç ve UX bitleri.
İşinizi daha iyi yapmanıza yardımcı olacak araçlarla. Abone olun ve Vitaly'nin Akıllı Arayüz Tasarım Kontrol Listeleri PDF'sini e-posta ile alın.
Ön uç ve UX üzerinde. 190.000 kişi tarafından güvenilen.