跳到内容

d

ESLint与代码检查

我们通常希望对存储在应用程序数据库中的数据应用一些约束。我们的应用程序不应接受缺少或空的content属性的笔记。在路由处理器中检查笔记的有效性:

app.post('/api/notes', (request, response) => {
  const body = request.body
  if (body.content === undefined) {    return response.status(400).json({ error: 'content missing' })  }
  // ...
})

如果笔记没有content属性,我们将以400 bad request的状态码响应请求。

在数据存储到数据库之前验证数据格式的一种更智能的方法是使用Mongoose提供的验证功能。

我们可以为模式中的每个字段定义特定的验证规则:

const noteSchema = new mongoose.Schema({
  content: {    type: String,    minLength: 5,    required: true  },  important: Boolean
})

现在,content字段要求至少为五个字符长,并且被设置为必需,意味着它不能缺失。我们没有对important字段添加任何约束,所以它在模式中的定义没有改变。

minLengthrequired验证器是Mongoose提供的内置验证器。如果没有一个内置的验证器能满足我们的需求,Mongoose的自定义验证器功能允许我们创建新的验证器。

如果我们试图在数据库中存储一个违反了某些约束的对象,操作将会抛出异常。让我们改变我们创建新笔记的处理器,使其将任何可能的异常传递给错误处理中间件:

app.post('/api/notes', (request, response, next) => {  const body = request.body

  const note = new Note({
    content: body.content,
    important: body.important || false,
  })

  note.save()
    .then(savedNote => {
      response.json(savedNote)
    })
    .catch(error => next(error))})

让我们扩展错误处理器以处理这些验证错误:

const errorHandler = (error, request, response, next) => {
  console.error(error.message)

  if (error.name === 'CastError') {
    return response.status(400).send({ error: 'malformatted id' })
  } else if (error.name === 'ValidationError') {    return response.status(400).json({ error: error.message })  }

  next(error)
}

当对象验证失败时,我们从Mongoose返回以下默认错误消息:

postman显示错误消息

我们注意到后端现在有一个问题:在编辑笔记时没有进行验证。 这个问题可以解决,update-validators 文档解释说,在执行findOneAndUpdate和相关方法时,默认不会运行验证。

但是要修复这个问题很简单。让我们也稍微改写一下路由代码:

app.put('/api/notes/:id', (request, response, next) => {
  const { content, important } = request.body
  Note.findByIdAndUpdate(
    request.params.id, 
    { content, important },    { new: true, runValidators: true, context: 'query' }  ) 
    .then(updatedNote => {
      response.json(updatedNote)
    })
    .catch(error => next(error))
})

Deploying the database backend to production

该应用程序应该能在Fly.io/Render上按原样工作。由于到目前为止我们只对后端进行了修改,所以我们不需要生成前端的新生产构建。

在dotenv中定义的环境变量只会在后端不处于生产模式时使用,即在Fly.io或Render中。

对于生产环境,我们需要在托管我们应用的服务中设置数据库URL。

在Fly.io中,可以通过fly secrets set命令来完成:

fly secrets set MONGODB_URI='mongodb+srv://fullstack:thepasswordishere@cluster0.o1opl.mongodb.net/noteApp?retryWrites=true&w=majority'

当应用正在开发过程中,很可能会出现一些失败的情况。例如,当我第一次部署带有数据库的应用时,一个笔记都没有看到:

浏览器显示没有出现任何笔记

浏览器控制台的网络标签页显示,获取笔记的请求并未成功,请求只是在pending状态下停留了很长时间,直到最后以502状态码失败。

浏览器控制台必须始终保持打开状态!

同时,持续关注服务器日志也非常重要。当我打开 fly logs 查看日志时,问题就显而易见了:

fly.io服务器日志显示连接到未定义

数据库URL是 undefined ,所以忘记了执行 fly secrets set MONGODB_URI 命令。

在使用Render时,可以通过在仪表板中定义适当的环境变量来提供数据库URL:

render仪表板显示MONGODB_URI环境变量

Render仪表板显示服务器日志:

render仪表板上有箭头指向在端口10000上运行的服务器

你可以在此GitHub仓库part3-6分支中找到我们当前应用的完整代码。

Lint

在我们进入下一部分之前,我们介绍一个重要的工具,叫做lint。维基百科对lint的描述如下:

一般来说,lint或者linter是任何检测和标记编程语言中错误的工具,包括样式错误。术语 lint-like behavior 有时用于标记可疑语言使用的过程。Lint类 的工具通常对源代码进行静态分析。

在编译的静态类型语言如Java中,像NetBeans这样的IDE可以指出代码中的错误,甚至是编译错误之外的错误。像checkstyle这样的用于执行静态分析的附加工具,可以用来扩展IDE的能力,也可以指出与样式相关的问题,如缩进。

在JavaScript领域,目前主导的静态分析(又名"linting")工具是ESlint

让我们使用以下命令将ESlint作为开发依赖项安装到notes后端项目中:

npm install eslint --save-dev

之后我们可以用以下命令初始化一个默认的ESlint配置:

npx eslint --init

我们要回答所有的问题:

ESlint初始化的终端输出

配置将会保存在 .eslintrc.js 文件中。我们将在 env 配置中将 browser 改为 node

module.exports = {
    "env": {
        "commonjs": true,
        "es2021": true,
        "node": true    },
    "overrides": [
        {
            "env": {
                "node": true
            },
            "files": [
                ".eslintrc.{js,cjs}"
            ],
            "parserOptions": {
                "sourceType": "script"
            }
        }
    ],
    "parserOptions": {
        "ecmaVersion": "latest"
    },
    "rules": {
    }
}

让我们稍微修改一下配置。安装一个插件,该插件定义了一套与代码风格相关的规则:

npm install --save-dev @stylistic/eslint-plugin-js

启用插件并添加一个扩展定义和四个代码风格规则:

module.exports = {
    // ...
    'plugins': [
        '@stylistic/js'
    ],
    'extends': 'eslint:recommended',
    'rules': {
        '@stylistic/js/indent': [
            'error',
            2
        ],
        '@stylistic/js/linebreak-style': [
            'error',
            'unix'
        ],
        '@stylistic/js/quotes': [
            'error',
            'single'
        ],
        '@stylistic/js/semi': [
            'error',
            'never'
        ],
    }
}

扩展 eslint:recommended 将一套推荐的规则添加到项目中。此外,还添加了关于缩进、换行、连字符和分号的规则。这四条规则都在Eslint样式插件中定义了。

可以使用以下命令检查和验证像 index.js 这样的文件:

npx eslint index.js

我们建议为linting创建一个单独的 npm script

{
  // ...
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    // ...
    "lint": "eslint ."  },
  // ...
}

现在,npm run lint 命令将检查项目中的每个文件。

当运行命令时,dist 目录中的文件也会被检查,我们不希望这种情况发生、我们可以通过在项目的根目录中创建一个.eslintignore 文件来实现这一点,文件的内容如下:

dist

这将导致整个dist目录不被ESlint检查。

Lint对我们的代码有很多意见:

ESlint错误的终端输出

我们暂时不去修复这些问题。

从命令行执行linter的更好替代方案是将eslint-plugin配置到编辑器中,这将连续运行linter。通过使用插件,你将立即在代码中看到错误。你可以在这里找到更多关于Visual Studio ESLint插件的信息。

VS Code的ESlint插件会用红线下划出风格违规:

VScode ESLint插件显示错误的截图

这使得错误很容易被发现并立即修复。

ESlint有大量的规则,这些规则通过编辑 .eslintrc.js 文件就可以很容易地使用。

让我们添加eqeqeq规则,如果用非三等号运算符检查等式,它会发出警告。该规则是在配置文件的rules字段下添加的。

{
  // ...
  'rules': {
    // ...
   'eqeqeq': 'error',
  },
}

在我们进行这项工作的同时,让我们对规则进行一些其他更改。

让我们阻止行尾的不必要的尾随空格,要求大括号前后始终有一个空格,并且也要求箭头函数的函数参数中一致使用空格。

{
  // ...
  'rules': {
    // ...
    'eqeqeq': 'error',
    'no-trailing-spaces': 'error',
    'object-curly-spacing': [
        'error', 'always'
    ],
    'arrow-spacing': [
        'error', { 'before': true, 'after': true }
    ]
  },
}

我们的默认配置从eslint:recommended中使用了一堆预定的规则:

'extends': 'eslint:recommended',

这包括一个关于 console.log 命令的警告规则。可以通过在配置文件中将其"值"定义为0来禁用一条规则。我们暂时为no-console规则这样做。

{
  // ...
  'rules': {
    // ...
    'eqeqeq': 'error',
    'no-trailing-spaces': 'error',
    'object-curly-spacing': [
        'error', 'always'
    ],
    'arrow-spacing': [
        'error', { 'before': true, 'after': true }
    ],
    'no-console': 0  },
}

注意 当你对.eslintrc.js文件进行更改时,建议从命令行运行linter。这将验证配置文件是否正确格式化:

如果你的配置文件中有什么错误,lint插件可能会表现得相当不稳定。

许多公司定义编码标准,这些标准通过ESlint配置文件在整个组织中强制执行。不建议反复重新发明轮子,采用别人项目中的现成配置可能是个好主意。最近,许多项目通过采用Airbnb的ESlint配置,采纳了Airbnb的Javascript风格指南

你可以在 part3-7 分支的这个GitHub仓库中找到我们当前应用程序的全部代码。