基于路由的中间件来处理默认总体查询逻辑
#javascript #strapi #headlesscms #route

作者:凯伦·布尔格(Kellen Bolger)

了解如何通过路由中间件设置默认的“填充”选项。可以通过后端中的基于路由的中间件来处理此功能,而不是从前端传递每个请求的“填充”。这将使您能够保持前端请求精简和井井有条。

您也可以使用它来控制默认情况下将返回的数据,并且不允许用户在前端添加其他填充选项。

什么是路由中间件?

在Strapi中,路由中间件的范围更有限,并在路线级别配置并用作中间件。您可以learn more in the Strapi documentation。现在,让我们跳入并学习如何设置。

样本内容结构

在此示例中,我将使用一个简单的博客文章类型,该类型由标题,身体,英雄图像,slug和作者组成,这是与用户的一对一关系。每个博客文章都有许多作者。

sample content structure

在进入如何实现自定义中间件之前,让我们看一下为什么首先要添加此用例中的中间件。

问题

默认情况下,需要在每个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
   }
 }
}

这不是理想的,因为已经排除了重要数据,例如heroImageauthors'信息。

填充= *

对上述问题的一种简单解决方案涉及将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

middleware

您将提示您命名中间件。然后,您需要选择要添加此中间件的位置。

run middleware

在此示例中,选择Add middleware to an existing API,因为您只希望它在博客柱路由上运行。

select route

现在在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的列表

list of middleware

要查看核心路线的所有可用自定义,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。