作者:凯伦·布尔格(Kellen Bolger)
了解如何通过路由中间件设置默认的“填充”选项。可以通过后端中的基于路由的中间件来处理此功能,而不是从前端传递每个请求的“填充”。这将使您能够保持前端请求精简和井井有条。
您也可以使用它来控制默认情况下将返回的数据,并且不允许用户在前端添加其他填充选项。
什么是路由中间件?
在Strapi中,路由中间件的范围更有限,并在路线级别配置并用作中间件。您可以learn more in the Strapi documentation。现在,让我们跳入并学习如何设置。
样本内容结构
在此示例中,我将使用一个简单的博客文章类型,该类型由标题,身体,英雄图像,slug和作者组成,这是与用户的一对一关系。每个博客文章都有许多作者。
在进入如何实现自定义中间件之前,让我们看一下为什么首先要添加此用例中的中间件。
问题
默认情况下,需要在每个client-side
请求上定义并发送总体结构,否则该请求将仅返回顶级父级内容。
GET
请求localhost:1337/api/blog-posts
返回以下内容:
// localhost:1337/api/blog-posts
{
"data": [
{
"id": 1,
"attributes": {
"title": "Test Blog Post",
"body": "Test blog content",
"slug": "test-blog-post",
"createdAt": "2022-08-09T18:45:19.012Z",
"updatedAt": "2022-08-09T18:45:21.710Z",
"publishedAt": "2022-08-09T18:45:21.707Z"
}
}
],
"meta": {
"pagination": {
"page": 1,
"pageSize": 25,
"pageCount": 1,
"total": 1
}
}
}
这不是理想的,因为已经排除了重要数据,例如heroImage
和authors
'信息。
填充= *
对上述问题的一种简单解决方案涉及将populate=*
添加到初始查询中。
localhost:1337/api/blog-posts?populate=*
返回以下内容:
// localhost:1337/api/blog-posts?populate=*
{
"data": [
{
"id": 1,
"attributes": {
"title": "Test Blog Post",
"body": "Test blog content",
"slug": "test-blog-post",
"createdAt": "2022-08-09T18:45:19.012Z",
"updatedAt": "2022-08-09T19:22:39.637Z",
"publishedAt": "2022-08-09T18:45:21.707Z",
"heroImage": {
"data": {
"id": 1,
"attributes": {
"name": "test_cat.jpeg",
"alternativeText": "test_cat.jpeg",
"caption": "test_cat.jpeg",
"width": 500,
"height": 500,
"formats": {
"thumbnail": {
"name": "thumbnail_test_cat.jpeg",
"hash": "thumbnail_test_cat_2bdaa9fbe9",
"ext": ".jpeg",
"mime": "image/jpeg",
"path": null,
"width": 156,
"height": 156,
"size": 5.01,
"url": "/uploads/thumbnail_test_cat_2bdaa9fbe9.jpeg"
}
},
"hash": "test_cat_2bdaa9fbe9",
"ext": ".jpeg",
"mime": "image/jpeg",
"size": 21.78,
"url": "/uploads/test_cat_2bdaa9fbe9.jpeg",
"previewUrl": null,
"provider": "local",
"provider_metadata": null,
"createdAt": "2022-08-09T19:06:25.220Z",
"updatedAt": "2022-08-09T19:06:25.220Z"
}
}
},
"authors": {
"data": {
"id": 1,
"attributes": {
"username": "testUser",
"email": "test@test.com",
"provider": "local",
"confirmed": true,
"blocked": false,
"createdAt": "2022-08-09T19:07:03.325Z",
"updatedAt": "2022-08-09T19:07:03.325Z"
}
}
}
}
}
],
"meta": {
"pagination": {
"page": 1,
"pageSize": 25,
"pageCount": 1,
"total": 1
}
}
}
虽然这确实返回更多数据,但这种方法的主要缺陷是您无法控制返回数据。您仍未收到有价值的信息,例如作者的role
,同时也接收您可能不在乎的数据。
变细
而不是使用populate=*
,您可以使用LHS Bracket syntax过滤查询。
查询看起来像这样:
localhost:1337/api/blog-posts?populate[heroImage][fields]
[0]=name&populate[heroImage][fields]
[1]=alternativeText&populate[heroImage][fields]
[2]=caption&populate[heroImage][fields]
[3]=url&populate[authors][fields]
[0]=username&populate[authors][populate][role][fields]
[0]=name
虽然正确返回指定的数据,但使用不可行。此查询非常不守规矩,当然不是您在整个应用程序中都需要始终使用的内容。
输入...查询弦
使用query-string,我们可以以更具可读性和可重复使用的方式实现与上述相同的查询。查询可以轻松直接在我们的应用程序的前端中使用。
例如:
const qs = require('qs')
const query = qs.stringify(
{
populate: {
heroImage: {
fields: ['name', 'alternativeText', 'caption', 'url'],
},
authors: {
fields: ['username'],
populate: {
role: {
fields: ['name'],
},
},
},
},
},
{
encodeValuesOnly: true, // prettify URL
}
)
// `localhost:1337/api/blog-posts?${query}`
它成功返回与上述查询相同的结果,我们使用括号语法:
{
"data": [
{
"id": 1,
"attributes": {
"title": "Test Blog Post",
"body": "Test blog content",
"slug": "test-blog-post",
"createdAt": "2022-08-09T18:45:19.012Z",
"updatedAt": "2022-08-09T19:22:39.637Z",
"publishedAt": "2022-08-09T18:45:21.707Z",
"heroImage": {
"data": {
"id": 1,
"attributes": {
"name": "test_cat.jpeg",
"alternativeText": "test_cat.jpeg",
"caption": "test_cat.jpeg",
"url": "/uploads/test_cat_2bdaa9fbe9.jpeg"
}
}
},
"authors": {
"data": {
"id": 1,
"attributes": {
"username": "testUser"
}
}
}
}
}
],
"meta": {
"pagination": {
"page": 1,
"pageSize": 25,
"pageCount": 1,
"total": 1
}
}
}
在许多用例中,这将是逻辑结束。但是,如果您发现自己一遍又一遍地重复使用相同的查询,请继续阅读。
中间件中的查询逻辑
现在您知道了如何构建有用的查询,可以通过将查询直接添加到基于路由的中间件中来进一步优化过程。
初始化新中间件
在Strapi中,您可以直接从CLI生成样板代码。在您的终端中,运行命令:
yarn strapi generate
从那里导航到middleware
。
您将提示您命名中间件。然后,您需要选择要添加此中间件的位置。
在此示例中,选择Add middleware to an existing API
,因为您只希望它在博客柱路由上运行。
现在在Strapi项目中,如果您导航到src > api > blog-post > middlewares > test.js
,您将看到以下样板:
'use strict'
/**
* `test` middleware.
*/
module.exports = (config, { strapi }) => {
// Add your own logic here.
return async (ctx, next) => {
strapi.log.info('In test middleware.')
await next()
}
}
在路线上启用中间件
在使用中间件之前,您首先需要在路线上启用它。
如果您前往
src > api > blog-post > routes > blog-post.js
,您将看到默认路由配置:
'use strict'
/**
* blog-post router.
*/
const { createCoreRouter } = require('@strapi/strapi').factories
module.exports = createCoreRouter('api::blog-post.blog-post')
编辑此文件如下:
'use strict'
/**
* blog-post router.
*/
const { createCoreRouter } = require('@strapi/strapi').factories
module.exports = createCoreRouter('api::blog-post.blog-post', {
config: {
find: {
middlewares: ['api::blog-post.test'],
},
},
})
Pro提示:如果您不记得中间件的内部UID,即
api::blog-post.test
,请运行以下命令:
yarn strapi middlewares:list
这将为您提供项目中所有内部中间件UID的列表
要查看核心路线的所有可用自定义,check out the docs。
将逻辑添加到中间件
现在,中间件已在您的项目中初始化并添加到blog-post
路线中,是时候添加一些逻辑了。
此中间件的目的是这样,因此您无需在前端构建查询即可返回要获取的数据。
通过将逻辑直接添加到中间件中,当您前往localhost:1337/api/blog-post
路线时,所有查询都会自动发生。
而不是将查询写在前端,而是将其直接添加到中间件中,因此:
// src > api > blog-post > middlewares > test.js
module.exports = (config, { strapi }) => {
// This is where we add our middleware logic
return async (ctx, next) => {
ctx.query.populate = {
heroImage: {
fields: ['name', 'alternativeText', 'caption', 'url'],
},
authors: {
fields: ['username'],
populate: {
role: {
fields: ['name'],
},
},
},
}
await next()
}
}
现在,停止Strapi服务器并运行yarn strapi build
以重建您的Strapi实例。构建完成后,运行yarn develop
重新启动Strapi服务器。
如果您转到路由localhost:1337/api/blog-posts
响应返回:
{
"data": [
{
"id": 1,
"attributes": {
"title": "Test Blog Post",
"body": "Test blog content",
"slug": "test-blog-post",
"createdAt": "2022-08-09T18:45:19.012Z",
"updatedAt": "2022-08-09T19:22:39.637Z",
"publishedAt": "2022-08-09T18:45:21.707Z",
"heroImage": {
"data": {
"id": 1,
"attributes": {
"name": "test_cat.jpeg",
"alternativeText": "test_cat.jpeg",
"caption": "test_cat.jpeg",
"url": "/uploads/test_cat_2bdaa9fbe9.jpeg"
}
}
},
"authors": {
"data": {
"id": 1,
"attributes": {
"username": "testUser"
}
}
}
}
}
],
"meta": {
"pagination": {
"page": 1,
"pageSize": 25,
"pageCount": 1,
"total": 1
}
}
}
不需要查询字符串!
恭喜您结束了!
这是您刚刚学到的内容的回顾:
- 如何使用
populate=*
。 - 如何使用LHS Bracket syntax查询和过滤。
- 如何使用query-string构建自定义客户端查询以更好地使用。
- 如何将自定义中间件添加到您的项目中。
- 如何在API路线上实现MiddleWares。