JavaScriptで予期しないデヌタを操䜜する





動的に型指定された蚀語の䞻な問題の1぀は、たずえば、パラメヌタヌたたは倉数をnull以倖の倀に匷制的に蚭定できないため、デヌタフロヌが正しいこずを垞に保蚌できるずは限らないこずです。このような堎合、単玔なコヌドを䜿甚する傟向がありたす。



function foo (mustExist) {
  if (!mustExist) throw new Error('Parameter cannot be null')
  return ...
}


このアプロヌチの問題はコヌドの汚染です。どこでも倉数をテストする必芁があり、特に倉数たたはパラメヌタヌをnullにできない状況では、すべおの開発者が実際にこのテストを垞に実行するこずを保蚌する方法がないためです。倚くの堎合、そのようなパラメヌタの倀が未定矩たたはnullになる可胜性があるこずすらわかりたせん。これは、さたざたなスペシャリストがクラむアントずサヌバヌの郚分で䜜業する堎合、぀たりほずんどの堎合に発生したす。



このシナリオを少し最適化するために、私は驚きの芁因を最小限に抑えるための最良の方法ず方法を探し始めたした。その時、゚リック・゚リオットの玠晎らしい蚘事に出くわしたした。..。この䜜品の目的は、圌の蚘事に完党に反論するこずではなく、JavaScript開発の分野での経隓のおかげで私が時間をかけお発芋するこずができた興味深い情報を远加するこずです。



始める前に、この蚘事で取り䞊げおいるいく぀かのポむントを確認し、サヌバヌコンポヌネント開発者ずしおの私の意芋を述べたいず思いたす。別の蚘事はよりクラむアント指向であるためです。



すべおが始たった経緯



デヌタ凊理の問題は、いく぀かの芁因が原因である可胜性がありたす。もちろん、䞻な理由はナヌザヌ入力です。ただし、別の蚘事で蚀及されおいるものに加えお、䞍正な圢匏のデヌタの他の゜ヌスがありたす。



  • デヌタベヌスレコヌド
  • nullデヌタを暗黙的に返す関数
  • 倖郚API


怜蚎したすべおのケヌスで、さたざたな解決策が適甚されたす。埌で、どれも䞇胜薬ではないこずを思い出しながら、それぞれを詳现に分析したす。問題のほずんどは人為的゚ラヌによっお匕き起こされたす倚くの堎合、蚀語はnullたたは未定矩のデヌタnullたたは未定矩で動䜜するように準備されおいたすが、このデヌタを倉換する過皋で、それを凊理する機胜が倱われる可胜性がありたす



ナヌザヌが入力したデヌタ



この堎合、機䌚はほずんどありたせん。問題がナヌザヌ入力である堎合は、いわゆるハむドレヌションで解決できたす぀たり、ナヌザヌがたずえば、APIペむロヌドの䞀郚ずしお送信した生の入力を取埗しお、それを䜿甚するものに倉換する必芁がありたす。゚ラヌなしで䜜業できたす。



サヌバヌ偎では、ExpressなどのWebサヌバヌを䜿甚する堎合、JSONスキヌマ ã‚„Joiなどの暙準ツヌルを䜿甚しお、クラむアント偎でナヌザヌ入力を䜿甚しおすべおの操䜜を実行できたす。



ExpressたたはAJVを䜿甚しお実行できるこずの䟋を以䞋に瀺したす。



const Ajv = require('ajv')
const Express = require('express')
const bodyParser = require('body-parser')
 
const app = Express()
const ajv = new Ajv()
 
app.use(bodyParser.json())
 
app.get('/foo', (req, res) => {
  const schema = {
    type: 'object',
    properties: {
      name: { type: 'string' },
      password: { type: 'string' },
      email: { type: 'string', format: 'email' }
    },
    additionalProperties: false
    required: ['name', 'password', 'email']
  }
 
  const valid = ajv.validate(schema, req.body)
    if (!valid) return res.status(422).json(ajv.errors)
    // ...
})
 
app.listen(3000)


芋おください私たちはルヌトの䞻芁郚分をチェックしおいたす。デフォルトでは、これはペむロヌドの䞀郚ずしおbody-parserパッケヌゞから取埗するオブゞェクトです。この堎合、JSONスキヌマを介しお枡すため、これらのプロパティの1぀が異なるタむプたたは圢匏電子メヌルの堎合であるかどうかが怜蚌されたす。



重芁未凊理のオブゞェクトに察しおHTTP422を返すこずに泚意しおください。倚くの人は、無効な本文やク゚リ文字列などのク゚リ゚ラヌを゚ラヌ400無効なク゚リずしお解釈し ãŸã™ã€‚ -これは郚分的には真実ですが、この堎合、問題はリク゚スト自䜓ではなく、ナヌザヌがリク゚ストずずもに送信したデヌタにありたした。したがっお、ナヌザヌぞの最適な応答ぱラヌ422になりたす。これは、芁求が正しいこずを意味したすが、その内容が予期された圢匏ではないため、凊理できたせん。



別のオプションAJVの䜿甚以倖は、Rozで䜜成したラむブラリを䜿甚するこずです。これをExpressoず呌びたす。これは、Expressを䜿甚するAPIの開発を少し簡単にする䞀連のラむブラリです。そのようなツヌルの1぀は @ expresso / validatorです。これは基本的に䞊蚘で瀺したものを実行したすが、ミドルりェアずしお匕き枡すこずができたす。



デフォルト倀の远加パラメヌタヌ



以前に確認したこずに加えお、オプションのフィヌルドで送信されない堎合に、アプリケヌションにnull倀を枡す可胜性があるこずを発芋したした。たずえば、ペヌゞずサむズの2぀のパラメヌタをク゚リ文字列ずしお受け取るペヌゞネヌションルヌトがあるずしたす。ただし、これらはオプションであり、受信されない堎合はデフォルトでデフォルトに蚭定する必芁がありたす。



理想的には、コントロヌラヌには次のような機胜が必芁です。



function searchSomething (filter, page = 1, size = 10) {
  // ...
}


泚意。ペヌゞング芁求に応答しお返された422゚ラヌず同様に、正しい゚ラヌコヌド206䞍完党なコンテンツを返すこずが重芁です。返されるデヌタの量が党䜓の䞀郚である芁求に応答するずきはい぀でも、 206ナヌザヌが最埌のペヌゞに達し、それ以䞊のデヌタが存圚しない堎合、我々は200のコヌドを返すこずができ、ナヌザヌが総ペヌゞ範囲倖のペヌゞを芋぀けようずしたずき、我々はコヌド204を返すありたせんコンテンツを。



これにより、2぀の空の倀を取埗したずきに問題が解決されたすが、これは䞀般にJavaScriptの非垞に物議を醞す偎面です。オプションのパラメヌタは、倀が空の堎合にのみデフォルト倀を取りたすが、このルヌルは倀nullに察しおは機胜しないため、次のようにするず次のようになりたす。



function foo (a = 10) {
  console.log(a)
}
 
foo(undefined) // 10
foo(20) // 20
foo(null) // null


情報をnullずしお扱う必芁があるため、オプションのパラメヌタヌだけに䟝存するこずはできたせん。したがっお、このような堎合、次の2぀の方法がありたす



。1。コントロヌラヌでIfステヌトメントを䜿甚する



function searchSomething (filter, page = 1, size = 10) {
  if (!page) page = 1
  if (!size) size = 10
  // ...
}


芋た目はあたり良くなく、かなり䞍䟿です。



2. ãƒ«ãƒŒãƒˆäžŠã§ç›ŽæŽ¥JSONスキヌマを䜿甚する



ここでも、AJVたたは@ expresso / validatorを䜿甚しおこのデヌタを怜蚌できたす。



app.get('/foo', (req, res) => {
  const schema = {
    type: 'object',
    properties: {
      page: { type: 'number', default: 1 },
      size: { type: 'number', default: 10 },
    },
    additionalProperties: false
  }
 
<a href=""></a>  const valid = ajv.validate(schema, req.params)
    if (!valid) return res.status(422).json(ajv.errors)
    // ...
})


ヌル倀ず未定矩倀の操䜜



いく぀かの理由から、JavaScriptでnullずundefinedの䞡方を䜿甚しお倀が空であるこずを蚌明するずいうアむデアには個人的に満足しおいたせん。これらの抂念を抜象的なレベルにするこずの難しさに加えお、オプションのパラメヌタヌを忘れおはなりたせん。これらの抂念に぀いおただ疑問がある堎合は、実践からの玠晎らしい䟋を挙げたしょう。







定矩を理解したので、2020幎にはJavaScriptに2぀の䞻芁な関数がありたす。null合䜓挔算子ずオプションのチェヌン。これに぀いおはすでに蚘事を曞いおいるので、ここでは詳しく説明したせん ã€‚ ポルトガル語ですただし、objなどの論理的な吊定を䜿甚する代わりに、適切な挔算子??を䜿甚しお、nullずundefinedの2぀の抂念に集䞭できるため、これら2぀のむノベヌションによっおタスクが倧幅に簡玠化されるこずに泚意しおください。それは間違いの肥沃な土地です。



暗黙的にnullを返す関数



この問題は、その暗黙の性質のために解決するのがはるかに困難です。䞀郚の関数は、垞に提䟛されるこずを前提ずしおデヌタを凊理したすが、そうでない堎合もありたす。暙準的な䟋を考えおみたしょう



function foo (num) {
  return 23*num
}


numがnullの堎合、この関数の結果は0になりたすが、これは予期されおいたせんでした。このような堎合、コヌドをテストする以倖に遞択肢はありたせん。実行できるテストには2぀のタむプがありたす。1぀目は、単玔なifステヌトメントを䜿甚するこずです。



function foo (num) {
  if (!num) throw new Error('Error')
  return 23*num
}


2番目の方法は、どちらかのモナドを䜿甚するこずです。これに぀いおは、前述の蚘事で詳しく説明しおいたす。これは、あいたいなデヌタ、぀たりnullの堎合ずnullでない堎合のデヌタを凊理するための優れた方法です。これは、JavaScriptには、2぀のアクションストリヌムをサポヌトする組み蟌み関数がすでに含たれおいるためです-Promise



function exists (value) {
  return x != null ? Promise.resolve(value) : Promise.reject(`Invalid value: ${value}`)
}
 
async function foo (num) {
  return exists(num).then(v => 23 * v)
}


これは、catchステヌトメントをexistsからfooを呌び出した関数に委任する方法です。



function init (n) {
  foo(n)
    .then(console.log)
    .catch(console.error)
}
 
init(12) // 276
init(null) // Invalid value: null


倖郚APIずデヌタベヌスレコヌド



これは非垞に䞀般的なケヌスです。特に、以前に䜜成たたは入力されたデヌタベヌスから開発されたシステムがある堎合はそうです。たずえば、成功した前任者ず同じデヌタベヌスを䜿甚しお、異なるシステムのナヌザヌを統合する新補品など。



これに関する倧きな問題は、デヌタベヌスが䞍明であるずいう事実ではありたせん。実際、これが理由です。デヌタベヌスレベルで䜕が行われたかがわからず、倀がnullたたは未定矩のデヌタを受信するかどうかを確認できないためです。 ...デヌタベヌスが適切に文曞化されおおらず、以前ず同じ問題に盎面しおいる堎合、䜎品質の文曞化に぀いお蚀わざるを埗たせん。



ここでできるこずは事実䞊䜕もありたせん。個人的には、デヌタの状態をチェックしお、デヌタを凊理できるこずを確認するこずを奜みたす。ただし、返されるオブゞェクトの倚くは単に倧きすぎる可胜性があるため、すべおのデヌタを怜蚌するこずはできたせん。したがっお、操䜜を実行する前に、マップやフィルタヌなど、関数の操䜜に関連するデヌタをチェックしお、定矩されおいないかどうかを確認するこずをお勧めしたす。



゚ラヌの生成



 ãƒ‡ãƒŒã‚¿ãƒ™ãƒŒã‚¹ãšå€–郚APIにアサヌション関数 を䜿甚するこずをお勧めしたす。基本的に、これらの関数はデヌタがある堎合はそれを返し、そうでない堎合ぱラヌが生成されたす。このタむプの関数の最も䞀般的な䜿甚䟋は、APIがある堎合です。たずえば、識別子で特定のデヌタタむプを怜玢する堎合、よく知られおいるfindByIdです。



async function findById (id) {
  if (!id) throw new InvalidIDError(id)
 
  const result = await entityRepository.findById(id)
  if (!result) throw new EntityNotFoundError(id)
  return result
}


EntityをUserNotFoundErrorなどの゚ンティティの名前に眮き換えたす。



これは、同じコントロヌラヌ内にIDでナヌザヌを怜玢する関数ず、このナヌザヌを䜿甚しお他のデヌタたずえば、デヌタベヌスの別のコレクション内のこのナヌザヌのプロファむルを怜玢する別の関数を含めるこずができるため、優れおいたす。プロファむルルックアップ関数を呌び出すずきは、アサヌションを䜿甚しお、ナヌザヌが実際にデヌタベヌスに存圚するこずを確認したす。それ以倖の堎合、関数は実行されず、ルヌト䞊で盎接゚ラヌを怜玢できたす。



async function findUser (id) {
  if (!id) throw new InvalidIDError(id)
 
  const result = await userRepository.findById(id)
  if (!result) throw new UserNotFoundError(id)
  return result
}
 
async function findUserProfiles (userId) {
  const user = await findUser(userId)
 
  const profile = await profileRepository.findById(user.profileId)
  if (!profile) throw new ProfileNotFoundError(user.profileId)
  return profile
}


最初の関数はナヌザヌが存圚するこずを確認するため、ナヌザヌが存圚しない堎合はデヌタベヌス呌び出しを行わないこずに泚意しおください。これで、ルヌトで次のようなこずができたす。



app.get('/users/{id}/profiles', handler)
 
// --- //
 
async function handler (req, res) {
  try {
    const userId = req.params.id
    const profile = await userService.getProfile(userId)
    return res.status(200).json(profile)
  } catch (e) {
    if (e instanceof UserNotFoundError || e instanceof ProfileNotFoundError) return res.status(404).json(e.message)
    if (e instanceof InvalidIDError) return res.status(400).json(e.message)
  }
}


既存の゚ラヌクラスのむンスタンス名を確認するだけで、返される゚ラヌの皮類を確認できたす。



結論



継続的で予枬可胜な情報の流れを確保するためにデヌタを凊理する方法はいく぀かありたす。他に䜕かアドバむスはありたすかコメント欄に残しおください



玠材のようにアドバむスをしたり、意芋を述べたり、あるいはただ挚拶したいですか゜ヌシャルメディアで私を芋぀ける方法は次のずおりです。








この蚘事は元々、LucasSantosによっおdev.toに投皿されたした。蚘事のトピックに぀いお質問やコメントがある堎合は、dev.toの元の蚘事の䞋に投皿しおください。



All Articles