HTTP options 方法的作用是什么 ?
- 检测服务器所支持的请求方法
发送options方式请求在返回的请求头Allow属性中可以看到支持的请求方法 - CORS 中的预检请求
如果一个网站的其中的一部分接口的一部分方法支持跨域,我们可检测该方法是否可以跨域
allowedMethods 的作用
- 代码:
app.use(usersRouter.allowedMethods())
加上它该路由都将支持options方法 - 响应 options 方法, 告诉它所支持的请求方法
- 若没有使用此方法使用option方式请求就会报404错误
- 若没有使用此方法使用option方式请求就会报404错误
- 相应的返回405(不允许)和501(没实现)
405: 支持该请求方法但是该接口功能未实现未写的时候
501: 不支持该请求方法是返回
RESTful API 最佳实践–增删改查应该返回什么响应?
增加修改返回该对象
删除返回204状态码
实例:
usersRouter.delete('/:id', (ctx) => {
ctx.status = 204
})
错误处理
- 编程语言或计算机硬件里的一种机制
- 处理软件或信息系统中出现的异常状况
异常状况
- 运行时错误, 都返回 500
建立在语法没有错误的基础上, 在运行时出现的错误, 如请求undefined
时错误 - 逻辑错误, 如
找不到(404) .
先决条件失败 ( 412 ) : 请求的id
不存在
无法处理的实体 ( 参数格式不对 , 422 ) : 请求体参数格式不对
等…
为什么要用错误处理
- 防止程序挂掉
try…catch - 告诉用户错误信息
避免用户不知道错在哪里, 体验较差, 用户群体消失 - 便于开发者调试
操作步骤
- 制造 404 , 412 , 500 三种错误
- 404
- 412
findById(ctx) {
if(ctx.params.id * 1 >= db.length) { // 不存在id时
ctx.throw(412, '先决条件失败, id 大于等于数组长度了')
}
ctx.body = { name: 'lilei' }
}
3. 500
find(ctx) {
ctx.body = a.b
}
- 了解 Koa 自带的错误处理做了什么
自己编写错误处理中间件
操作步骤
- 自己编写错误处理中间件
app.use(async(ctx, next) => {
try {
await next()
} catch (err) {
// message:"先决条件失败, id 大于等于数组长度了"
// name:"PreconditionFailedError"
// stack:"PreconditionFailedError: 先决条件失败, id 大于等于数组长度了
// expose:true
// status:412
// statusCode:412
ctx.status = err.status || err.statusCode
ctx.body = {
message: err.message
}
}
})
- 500
ctx.status = err.status || err.statusCode || 500
- 制造 404 , 412 , 500 三种错误来测试
使用 koa-json-error
进行错误处理
使用 koa-parameter
校验参数(校验前端表单)
参数不正确, 返回422
app.use(parameter(app))
不仅仅是一个中间件, 可以在ctx加上方法校验, 可以全局使用,所以传入app
ctx.verifyParams({
name: { type: 'string', required: true},
age: { type: 'number', required: true}
})
补充:
409
: 如新建用户,发现用户已存在,抛出错误
字段过滤
设计schema默认隐藏部分字段
原返回
{
"gender": "male",
"locations": [
"上海",
"北京"
],
"_id": "5eadafda08d7180ecc1fb330",
"username": "izhaong",
"employments": [
{
"_id": "5eadb33dfb66404e84c9fa57",
"company": "biotree"
}
],
"educations": [
{
"_id": "5eadb33dfb66404e84c9fa58",
"school": "皇家机电",
"major": "钒钛资源利用技术",
"diploma": 3,
"entrance_year": 2014,
"graduation_year": 2017
}
],
"avatar": "http://localhost:3000/upload/upload_7fb20d056f3d693d4bde34a2b4289783.jpeg",
"business": "互联网",
"headline": "诚意 正心 明理 格物 致知 修身 齐家"
}
设计
const userSchema = new Schema({
__v: { type: Number, select: false },
username: { type: String, required: true },
password: { type: String, required: true, select: false },
avatar: { type: String },
gender: { type: String, enum: ['male', 'femele'], default: 'male', required: true },
headline: { type: String },
locations: { type: [{ type: String }], select: false },
business: { type: String, select: false },
employments: { type: [{ company: { type: String }, job: { type: String } }], select: false },
educations: {
type: [{
school: { type: String },
major: { type: String },
diploma: { type: Number, enum: [1, 2, 3, 4, 5] },
entrance_year: { type: Number },
graduation_year: { type: Number }
}],
select: false
}
})
设计之后
{
"gender": "male",
"_id": "5eadafda08d7180ecc1fb330",
"username": "izhaong",
"avatar": "http://localhost:3000/upload/upload_7fb20d056f3d693d4bde34a2b4289783.jpeg",
"headline": "诚意 正心 明理 格物 致知 修身 齐家"
}
通过查询字符串显示隐藏字段
mongoose
是支持const user = await User.findById(ctx.params.id).select('+educations+business')
这种查询的
所以我们不能写死
处理得
const { fields } = ctx.query
// f不能为空 过滤掉
const selectFields = fields.split(';').filter(f => f).map(f => ' +' + f).join('')
// const user = await User.findById(ctx.params.id)
// const user = await User.findById(ctx.params.id).select('+educations+business')
const user = await User.findById(ctx.params.id).select(selectFields)
查询{{api}}/users/5eadafda08d7180ecc1fb330?fields=educations;business
得
{
"gender": "male",
"_id": "5eadafda08d7180ecc1fb330",
"username": "izhaong",
"educations": [
{
"_id": "5eadca0728cdd1bf308632de",
"school": "皇家机电",
"major": "钒钛资源利用技术",
"diploma": 3,
"entrance_year": 2014,
"graduation_year": 2017
}
],
"avatar": "http://localhost:3000/upload/upload_7fb20d056f3d693d4bde34a2b4289783.jpeg",
"business": "互联网",
"headline": "诚意 正心 明理 格物 致知 修身 齐家"
}