当前位置: 首页 > 后端技术 > Node.js

Express实战(六):构建API接口

时间:2023-04-03 12:30:41 Node.js

介绍了这么多Express的核心概念后,下一篇将重点介绍如何构建一个真实的应用。在这里,我们从构建应用程序API接口开始。在某种程度上,几乎所有的软件应用程序都是由一组功能强大的API驱动的。API实际上是一种代码之间的交互方式,既可以在程序内部进行,也可以通过网络跨机器进行。例如,Express中的app.use和app.get是内部使用的。通过HTTP或FTP等协议发送JSON和XML数据的方式属于后者。对于后一种方法,需要注意的是API的提供者和使用者必须就数据格式达成一致。在本文的例子中,我们将讨论如何使用Express构建后一类API接口,同时所有HTTP接口返回的数据格式都会使用JSON。此外,本章还将讨论如何设计优雅的API来提高用户体验和效率,让API的含义一目了然,而无需阅读冗长的臭文档.就像“好代码”和“坏代码”一样,API是否优雅更多取决于实际情况。盲目遵循API设计中的最佳实践有时看起来很迂腐,因为它有可能不符合消费者的期望。以下内容:什么是API。在Express中构建API的基础内容。HTTP方法与应用程序逻辑的关联。多版本API的实现和管理。正确使用HTTP状态代码。JSON格式的简单API示例首先,我们需要明确示例的功能以及如何使用API??,然后再编写代码。假设,现在程序需要在接收到表示时区的字符串如America/Los_Angeles或Europe/后返回该时区的当前时间信息(例如:2015-04-07T20:09:58-07:00)伦敦。返回的信息与现实中人类可读的时间格式不一样,因为它是为计算机设计的。通过HTTP请求调用应用API,URL格式类似:/timezone?tz=America+Los_Angeles,服务端API返回的JSON数据格式如下:{"time":"2015-06-09T16:20:00+01:00","zone":"America/Los_Angeles"}只要你能调用API并解析JSON数据,你就可以在任何平台上构建任何应用程序。如下图所示,您可以通过AJAX请求该API,实现一个显示时区信息的单页应用。您也可以使用该接口来实现如下图所示的移动应用程序。你甚至可以使用这个API实现如下图所示的终端命令行工具:在终端中打印服务器端API接口返回的数据。就像上一章的天气应用一样,我们可以利用这些API返回的冷数据来构建更具表现力的UI。Express驱动的JSONAPI服务理解了API的概念之后,我们来实现一个Express驱动的API服务。实现原理很简单:通过中间件和内置函数解析网络请求,将JSON数据和HTTP状态码封装成响应对象,返回给客户端。从技术角度来说,API服务除了可以使用JSON格式,还可以使用XML或者纯文本。但是Express和JavaScript对JSON的支持最好,也是目前最流行的格式,所以以后会一直使用JSON作为默认的数据格式。接下来,我们编写一个服务,为多个平台提供随机数生成。API会有以下特点:请求API时,必须附上随机数的最小值和最大值。解析请求获取随机数范围,并以JSON格式返回结果。您可能认为这里可以使用纯文本而不是JSON格式。但是发送JSON数据是开发者必备的技能,而且JSON格式非常容易扩展。项目的构建步骤如下:新建一个package.json。创建项目主入口文件app.js。在app.js中创建应用程序和路由中间件。首先,在新的package.json中,复制以下内容并遵循dependencies:{"name":"random-number-api","private":true,"scripts":{"start":"nodeapp"},"dependencies":{"express":"^5.0.0"}}接下来,将以下代码复制到入口文件app.js中:varexpress=require("express");varapp=express();app.get("/random/:min/:max",function(req,res){varmin=parseInt(req.params.min);varmax=parseInt(req.params.max);if(isNaN(min)||isNaN(max)){res.status(400);res.json({error:"Badrequest."});return;}varresult=Math.round((Math.random()*(max-min))+min);res.json({result:result});});app.listen(3000,function(){console.log("Appstartedonport3000");});现在启动应用程序,访问http://localhost:3000/random/...,你会看到一个随机数在10-100范围内的JSON数据。接下来我们来分析一下上面的代码。和以前一样,前两行代码导入Express并创建Express应用程序的实例。然后,我们创建一个路由中间件来处理API请求,例如/random/10/100。当然这里还是有一些bug,比如对/random/foo/bar的请求没有被过滤掉。因此,调用API时,请确保使用的参数为整型变量。然后,我们使用内置的parseInt来解析range参数,这个函数的返回值只能是整数或者NaN。如果传入参数之一为NaN,则会向客户端返回一条错误消息。以下部分代码对于整个程序来说非常重要:if(isNaN(min)||isNaN(max)){res.status(400);res.json({错误:“错误的请求。”});return;}如果以上参数检查至少有一个为NaN,程序将进行如下处理:将HTTP状态码设置为400。常见的404错误是它的一个特定变体,意思是:用户的请求。发送带有错误信息的JSON数据。结束请求处理,跳出中间件执行。在代码的最后,我们会在合法的参数return内生成一个随机数,并将结果返回给客户端。例子虽然简单,但是已经涵盖了用Express构建API的基本流程:解析请求,设置HTTP状态码,返回响应数据。您可以在此基础上构建更复杂、更优雅的API。CURD操作APICURD是程序中Create、Read、Update、Delete这四个业务动作的缩写。大多数应用程序都涉及CURD操作。例如,对于一个照片分享应用,所有涉及图片的操作都是典型的CRUD:用户上传照片的行为对应于创建操作。用户浏览照片的行为就是读取操作。用户更新照片的行为就是更新操作。用户删除照片的行为就是删除操作。无论是用于分享照片的社交应用程序还是文件存储服务,这种模式被用在你生活中使用的许多服务中。但在我们开始讨论用于构建CRUD功能的API之前,让我们看一下所谓的HTTP方法。HTTP方法HTTP规范对其方法的定义如下:HTTP方法指定了对请求URI标识的资源的操作,方法区分大小写。一个比较容易理解的解释是:客户端在发送HTTP请求时需要指定一个HTTP方法,然后服务器会根据不同的HTTP方法做出不同的响应。虽然可用的HTTP方法很多,但常用的并不多。其中,Web应用程序中常用的有以下四种:GET是最常用的HTTP方法,表示请求服务器端资源。例如GET用于加载网站首页,请求图片资源。尽管服务器的响应可能不同,但是GET请求不会改变服务器的资源。例如,对图像资源的一个或多个请求不会导致图像本身有任何差异。POST是另一种常用的HTTP方法。比如新建博客、上传照片、注册用户、清空购物车等都使用了POST。与GET的不同之处在于,每次POST请求都会导致服务器被修改。PUT方法是用来修改已有记录的,所以我觉得叫“UPDATE”更合适。比如修改博客标题、修改用户昵称等操作都是PUT操作。此外,PUT还具有POST的功能:即当要修改的记录不存在时,可以进行新的操作(不是必须的)。其次,PUT还具有GET方法的特点:对同一个URL的一次或多次PUT请求结果是一致的。DELETE方法用于记录删除。比如删除用户文章,删除网络照片。另外,和PUT一样,同一个删除请求执行一次或多次,最终结果是一致的。HTTP虽然还有很多其他的方法,但是在真正的开发过程中并不常见。理论上,您甚至可以仅使用GET和POST请求来完成所有业务,但这是一种不好的做法,因为它违反了HTTP规范并且会使开发人员感到困惑。此外,许多浏览器还指定了根据HTTP方法执行的操作类型。因此,即使不是强制性的,您也应该参照本规范来约束您的行为。您之前已经在Express中看到过一些方法,但是下面的代码将同时涵盖上述所有四种方法:varexpress=express("express");varapp=express();app.get("/",function(req,res){res.send("你刚刚发送了一个GET请求,朋友");});app.post("/",function(req,res){res.send("一个POSTrequest?nice");});app.put("/",function(req,res){res.send("我再也看不到很多PUT请求了");});app.delete("/",function(req,res){res.send("天哪,删除了??");});app.listen(3000,function(){console.log("App正在监听3000端口");});将代码复制到入口文件app.js中并启动服务,即可使用cURL命令测试不同的HTTP方法。默认情况下,cURL使用GET发送请求,但您可以使用-X选项指定其他方法。例如,curl-XPUThttp://localhost:3000。通过HTTP方式构建CRUD接口回想一下之前的图片分享应用,里面可能有以下CRUD操作:用户上传一张图片,这就是Create。用户浏览图片,这是Read。用户更新图片备注等信息,这就是Update。用户从站点中删除图像,这就是删除。不难看出,CRUD操作对应前面四种HTTP方法:Create=POSTRead=GETUpdate=PUTDelete=DELETE。因此,通过这四种HTTP方法,我们可以很好地实现最常见的CRUD风格的Web应用。事实上,有些人对更新和创建动作如何与HTTP方法对应有自己的看法。他们认为PUT应该对应createaction而不是POST。另外,新的PATCH方法对应更新操作。虽然本文会使用上面比较规范的对应关系,但是大家可以根据自己的意愿进行选择。API版本控制为了应对未来可能的API更新,API的版本控制是一种非常有效的方法。比如之前的获取指定时区当前时间的API,上线后就被很多厂商和开发者使用。然而,多年后由于某种原因必须更新API,同时您不能影响以前的用户。此时,我们可以通过添加新版本来解决这个问题。其中,原API请求可以通过:/v1/timezone,新版API请求可以使用:/v2/timezone,这样不仅可以防止在更新API时对代码进行破坏性修改。而且接口用户也有更灵活的选择,可以在需要的时候切换API。在Express中,可以使用Router中间件来实现API的版本管理。将以下代码复制到文件app1.js中,作为第一版API的实现:varexpress=require("express");varapi=express.Router();api.get("/timezone",function(req,res){res.send("/timezone的示例响应");});api.get("/all_timezones",function(req,res){res.send("/all_timezones的示例响应");});模块.exports=api;请注意,上述中间件代码处理的URL不包含/v1。接下来在入口文件中引入这个Router中间件,进行路由映射。varexpress=require("express");varapiVersion1=require("./api1.js");varapp=express();app.use("/v1",apiVersion1);app.listen(3000,函数(){console.log("App在3000端口启动");});然后,将最新版本的API实现放在api2.js文件中:varexpress=require("express");varapi=express.Router();api.get("/timezone",function(req,res){res.send("API2:超酷的/timezone新响应");});module.exports=api;最后通过Router将这两个版本的API同时添加到主入口:varexpress=require("express");varapiVersion1=require("./api1.js");varapiVersion2=require("./api2.js");varapp=express();app.use("/v1",apiVersion1);app.use("/v2",apiVersion2);app.listen(3000,function(){控制台.log("App在3000端口启动");});您可以通过浏览器验证这些版本化的API是否正常工作,也可以使用cURL命令进行测试。前面章节介绍过,Router允许你将不同的路由存放在不同的文件中,以便于管理。版本化API是最典型的应用示例。设置HTTP状态代码每个HTTP响应都应附带一个HTTP状态代码,其中最著名的是404NotFound。虽然404最著名,但200状态代码最常见。与404不同的是,虽然网页加载成功或JSON数据返回成功时会包含状态码200,但不会显示。当然,除了404和200之外,HTTP中还定义了很多其他的状态码,包括100、200、300、400、500系列。需要注意的是,并非每个系列中的所有100个数字都有明确的定义。比如100系列中只有100、101、102三个有效代码,后面跟着200。每个状态代码系列其实都有特定的含义和主题。摘要是:1xx:成功收到请求。2xx:成功3xx:重定向4xx:客户端错误5xx:服务器错误规范中只定义了大约60个状态码。您可以在此基础上扩展自己的状态代码,但通常情况并非如此。因为好的API设计的首要原则是保证用户没有歧义,所以应该最大程度的遵循官方规范的指导。后面我们会解释每个区间的状态码,但在此之前,我们先看看如何在Express中设置状态码。少数应用程序仍在使用HTTP1.0版协议,而大多数应用程序已切换到1.1版。作为HTTP2.0标准的下一版本,正在逐步推广中。好在2.0版本的协议更新大多是低级的,切换不会涉及太多的工作。此外,2.0版本还增加了421状态码。设置HTTP状态码默认情况下,HTTP状态码为200。如果用户访问的URL对应的资源不存在,Express会报404错误。如果访问的服务器有问题,Express会发送500错误。但这些都是Express的默认行为,在某些情况下你可能需要自己设置状态码。为此,Express的response对象提供了一个status方法,调用时需要传入相应的状态码来完成设置。//...res.status(404);//...这个方法可以被链接起来,所以你可以使用json来设置返回的数据。res.status(404).json({error:"Resourcenotfound!"});//等同于:res.status(404);res.json({error:"Resourcenotfound!"});虽然Express扩展了原生Node的响应对象,使用Express时应该遵循Express风格,但仍然可以使用原生方法完成设置。res.statusCode=404;100interval100官方状态码只有两个:100(continue)和101(switchprotocol),很少用到。如果非要处理的话,可以去官网或者wiki上查看。200interval200interval状态码表示请求成功。这个区间的状态码虽然有很多,但常用的有以下四种:200:作为最常见的状态码,也被称为“OK”。这意味着请求和响应都正确执行,没有任何错误或重定向。201:与200非常相似,但用例略有不同。它通常在POST或PUT请求成功创建记录后使用。例如创建博文、上传图片等操作成功后会发送201。202:202是201的变种。因为资源的创建大多是异步的,这些操作也很耗时。所以,此时可以向客户端响应202。表示数据已经接收成功,正在等待创建。204:表示用户删除请求对应的资源不存在,已被删除。300范围同样,在300范围内,我们只介绍其中常用的三个,它们都涉及到重定向。301:表示访问的资源位置已修改,请访问最新的网址。通常它还带有一个Location标头,指示重定向到哪里。303:表示请求的资源已经创建,现在你将被重定向到一个新的页面。307:与301类似,表示当前URL不存在。但不同的是,301重定向是永久性的,而307可能只重定向一个临时URL。400interval400interval的状态码最多,通常表示由于客户端错误导致请求失败。401和403:这两个状态码分别代表“Unauthorized”和“Forbidden”。从字面上看,两者类似,但前者可能表示用户未登录,后者可能表示用户已登录但权限不足。404:表示用户URL请求的资源不存在。至于这个范围内的其他状态码,读者可以自行去wiki查看,这里就不一一介绍了。此外,当您不确定要使用哪个客户端错误状态代码时,您可以只使用400。500间隔是HTTP规范中的最后一个间隔,500间隔状态代码表示服务内部发生错误。例如,请求过载或数据库连接中断。另外,理论上这个范围内的错误只能由服务自身触发。最后,为了防止黑客窥探过多的内部信息,您可以为所有内部错误返回抽象的“InternalServerError”消息。总结本章涵盖的内容:使用Express构建API服务。HTTP方法及其与CRUD操作的关系。如果对API进行版本控制,会提示服务的兼容性和稳定性。HTTP状态码的使用和意义。原地址