当前位置: 首页 > Web前端 > HTML

翻译-《JavaScript Everywhere》第9章详细资料(^_^)

时间:2023-04-02 21:40:03 HTML

翻译|《JavaScript Everywhere》第9章详细资料(^_^)写在置顶前端开发工程师。翻译一本英文技术书籍。为了提高大家的阅读体验,对句子的结构和内容进行了微调。如果大家发现本文有什么不妥之处,或者有什么意见和建议,可以在评论区留言,或者加我的微信:code\_maomao,欢迎交流,互相学习。(σ???)σ..:\*☆哎哟还不错第9话详情当现在无处不在的空气清新剂纺必适刚上市的时候,它就像一个哑弹。最初的广告只展示了人们使用该产品去除特定的难闻气味,例如香烟烟雾,导致销量不佳。面对令人失望的结果,营销团队将注意力转移到使用Febreze作为完美细节。这些广告现在描绘的是有人打扫房间、弄松枕头并用纺必适(Febreze)兴奋地整理房间。产品的重新设计导致销量激增。这是细节重要性的一个很好的例子。现在我们有了一个可用的API,但它缺少使我们投入生产的收尾工作。在本章中,我们将实施一些Web和GraphQL应用程序安全和用户体验最佳实践。这些细节远远超出了空气清新剂的喷雾范围,对我们应用程序的安全性、保障性和可用性至关重要。Web应用程序和Express.js的最佳实践Express.js是为我们的API提供支持的底层Web应用程序框架。我们可以对Express.js代码做一些小的调整,为我们的应用程序提供坚实的基础。ExpressHelmetExpressHelmet中间件是小型安全中间件功能的集合。这些将调整我们应用程序的HTTP标头以提高安全性。虽然其中许多是基于浏览器的应用程序,但启用Helmet是保护我们的应用程序免受常见Web漏洞攻击的简单步骤。要启用Helmet,我们需要在我们的应用程序中使用中间件,并指示Express在我们的中间件堆栈中尽早使用它。在./src/index.js文件中,添加如下内容://首先require文件顶部的包consthelmet=require('helmet')//添加中间件在栈顶,constapp之后=express()app.use(头盔());通过添加Helmet中间件,我们快速为我们的应用程序启用了常见的Web安全最佳实践。跨源资源共享跨源资源共享(CORS)是我们允许从另一个域请求资源的方法。由于我们的API和UI代码将分开存在,因此我们希望启用来自其他来源的使用。如果您有兴趣了解CORS的来龙去脉,我强烈推荐MozillaCORS指南。要启用CORS,我们将使用.src/index.js文件中的Express.jsCORS中间件包://首先需要文件顶部的包constcors=require('cors');//在app.use(helmet());app.use(cors());之后添加中间件通过这种方式添加中间件,我们可以实现来自所有域的跨域请求。目前这对我们来说很好用,因为我们处于开发模式,很可能会使用我们的托管服务提供商生成的域,但通过使用中间件,我们还可以将请求限制到特定来源。分页目前,我们的笔记查询和用户查询都返回数据库中笔记和用户的完整列表。这对于本地开发来说效果很好,但随着我们的应用程序的增长变得不可持续,因为可以返回许多(或数千)条笔记的查询非常昂贵,并且会减慢数据库、服务器和网络速度。相反,我们可以对这些查询进行分页以仅返回一定数量的结果。我们可以实现两种常见的分页类型。第一种类型的偏移分页通过客户端传递偏移量并返回有限数量的数据来工作。例如,如果每页数据限制为10条记录并且我们想要请求第三页数据,我们可以传递20的偏移量。虽然从概念上讲这是最直接的方法,但它可能会遇到缩放和性能问题。第二种类型的分页是基于游标的分页,其中传递基于时间的游标或唯一标识符作为起点。然后,我们要求在该记录之后提供一定数量的数据。这种方法使我们能够最大程度地控制分页。此外,由于Mongo的对象ID是有序的(它们以4字节时间值开头),我们可以轻松地将其用作游标。要了解有关Mongo对象ID的更多信息,建议阅读相应的MongoDB文档。如果你不理解这个概念,没关系。让我们将笔记的分页实现为GraphQL查询。首先,让我们定义将要创建的内容,然后是更新模式,最后是解析器代码。出于我们的需要,我们希望查询我们的API,同时可选择将游标作为参数传递。然后,API应该返回有限数量的数据,一个布尔值,表示数据集中最后一个的光标点,以及是否有另一页数据要查询。有了这个描述,我们可以更新src/schema.js文件来定义这个新查询。首先,我们需要在文件中添加一个NoteFeed类型:typeNoteFeed{notes:[Note]!光标:字符串!hasNextPage:Boolean!}接下来,我们将添加noteFeed查询:typeQuery{#addnoteFeedtoourexistingqueriesnoteFeed(cursor:String):NoteFeed}更新结构后,我们可以为查询编写解析器代码。在./src/resolvers/query.js中,将以下内容添加到导出的对象中:noteFeed:async(parent,{cursor},{models})=>{//hardcodethelimitto10itemsconstlimit=10;//将默认的hasNextPage值设置为falselethasNextPage=false;//如果没有传递游标,则默认查询将为空//这将从数据库中提取最新的注释letcursorQuery={};//如果有游标//我们的查询将查找ObjectId小于游标的注释if(cursor){cursorQuery={_id:{$lt:cursor}};}//在我们的数据库中找到limit+1个笔记,从最新到最旧排序letnotes=awaitmodels.Note.find(cursorQuery).sort({_id:-1}).limit(limit+1);//如果我们找到的笔记数量超过限制//将hasNextPage设置为true并将笔记修剪到限制if(notes.length>limit){hasNextPage=true;notes=notes.slice(0,-1);}//新光标将是feed数组中最后一项的Mongo对象IDconstnewCursor=notes[notes.length-1]._id;return{notes,cursor:newCursor,hasNextPage};}在使用这个解析器之后,我们可以查询我们的noteFeed并返回最多10个结果。在GraphQLPlayground中,我们可以编写以下查询来接收笔记列表、它们的对象ID、它们的“创建时间”时间戳、游标和下一页布尔值:query{noteFeed{notes{idcreatedAt}cursorhasNextPage}}由于我们的数据库中有超过10条笔记,所以它返回一个hasNextPage值为true的游标。使用该游标,我们可以查询提要的第二页:query{noteFeed(cursor:""){notes{idcreatedAt}cursorhasNextPage}}我们可以继续执行此操作。通过这个实现,我们创建了一个分页的笔记块。这不仅允许我们的UI请求特定的数据块,而且还减少了服务器和数据库的负载。数据限制除了设置分页,我们还想限制可以通过我们的API请求的数据量。这可以防止查询使我们的服务器或数据库过载。此过程中的一个简单的第一步是限制查询可以返回的数据量。我们的两个查询,user和notes,从数据库返回所有匹配的数据。我们可以通过在数据库查询上设置limit()方法来做到这一点。例如,在我们的.src/resolvers/query.js文件中,我们可以更新notes查询如下:notes:async(parent,args,{models})=>{returnawaitmodels.Note.find()。limit(100);}虽然限制数据是一个好的开始,但目前我们的查询可以写入无限深度。这意味着可以编写单个查询来检索笔记列表、每个笔记的作者信息、每个作者的收藏夹列表、每个收藏夹的作者信息等等。一次查询就这么多数据了,继续写吧!为了防止这些类型的过度查询,我们可以根据API限制查询的深度。此外,我们可能有没有嵌套太多但仍需要大量计算才能返回数据的复杂查询。我们可以通过限制查询的复杂性来防止此类请求。我们可以通过使用./src/index.js文件中的graphql-depth-limit和graphql-validation-complexity包来实现这些限制://importthemodulesatthetopofthefileconstdepthLimit=require('graphql-深度限制');const{createComplexityLimitRule}=require('graphql-validation-complexity');//更新我们的ApolloServer代码以包含validationRulesconstserver=newApolloServer({typeDefs,resolvers,validationRules:[depthLimit(5),createComplexityLimitRule(1000)],context:async({req})=>{//从标头中获取用户令牌consttoken=req.headers.authorization;//尝试使用令牌检索用户constuser=awaitgetUser(token);//将数据库模型和用户添加到上下文中return{models,user};}});通过添加这些包,我们为API添加了额外的查询保护。有关保护GraphQLAPI免受恶意查询侵害的更多信息,请查看SpectrumCTOMaxStoiber撰写的这篇精彩文章。其他注意事项在构建我们的API之后,您应该对GraphQL开发的基础知识有扎实的了解。如果你想深入这些话题,那么你可以去测试一下,GraphQL订阅和ApolloEngine是一些不错的选择。测验好吧,我承认:我没有为本书编写测验而感到内疚。测试我们的代码很重要,因为它使我们能够轻松地进行更改并改善与其他开发人员的协作。GraphQL设置的一大优点是解析器只是接受一些参数并返回数据的函数。这使得我们的GraphQL逻辑易于测试。订阅订阅是GraphQL的一项强大功能,它提供了一种将发布-订阅模式集成到我们的应用程序中的直接方法。这意味着UI可以在服务器上发布数据时订阅通知或更新。这使得GraphQL服务器成为处理实时数据的应用程序的理想解决方案。有关GraphQL订阅的更多信息,请查看ApolloServer文档。ApolloGraphQL平台在API的整个开发过程中,我们一直在使用ApolloGraphQL库。在以后的章节中,我们还将使用Apollo客户端库与API进行交互。我之所以选择这些库,是因为它们是行业标准,并且为使用GraphQL提供了出色的开发人员体验。如果您将应用程序投入生产,维护这些库的公司Apollo还将提供一个平台,为GraphQLAPI提供监控和工具。您可以在阿波罗(Apollo)网站上了解更多信息。结论在本章中,我们对我们的应用程序进行了一些最后的润色。虽然我们可以实施许多其他选项,但此时我们已经开发出可靠的MVP(最小可行产品)。在此状态下,我们已准备好启动我们的API!在下一章中,我们会将API部署到公共Web服务器。如果有什么不明白的地方,请指正。如果觉得还可以,请点赞、收藏或分享,希望能帮助到更多的人。

猜你喜欢