文档驱动的API开发:什么是文档驱动的开发,您是如何实现的,以及

2020-10-20 02:25:08

文档驱动开发是一种API开发方法,您首先编写文档,然后根据规范实现API。如果您的系统中有API的任何客户端(例如前端应用程序),那么您也可以根据规范来实现它们。此方法通常也称为API优先。

经常有这样的想法,即API更改应该由后端驱动,并且后端可以随时更改API,然后API客户端(例如前端应用程序)必须遵守对后端所做的任意更改。

许多开发人员认为,在实现后端API之前,您不能开始在API客户端(例如前端应用程序)上工作。事实并非如此:如果您先编写文档,那么客户端应用程序和API服务器都可以同时实现。

然而,当涉及到API时,并不是所有东西都算作文档。API必须以标准格式(如OpenAPI)记录,这样我们才能利用文档驱动开发的好处。我见过很多团队使用Confluence、Google Docs、Sharepoint、Dropbox Papers等工具记录他们的API。这不起作用,因为我们无法从它们生成标准规范,以便与其他工具结合使用来测试我们的API实现。

这在实践中是如何运作的呢?它到底有没有好处呢?我将向您展示如何实践文档驱动的开发,以使用Flask开发REST API。对于任何其他类型的API,该方法都是相同的,并且它可以与任何其他框架一起使用。

我们将实现一个简单的API来管理待办事项列表。待办事项的架构将具有以下属性:

创建的属性ID将由后端设置,因此对于API客户端是只读的。属性TASK表示必须完成的任务,而STATUS属性表示任务的状态,并且只能采用枚举值之一。为了最佳实践和可靠性,我们将使包含模式中未列出的任何属性的任何请求无效。

/TODO表示待办事项的集合,我们将使用它来获取待办事项列表并创建新的待办事项。我们将使用/todo/{item_id}来管理特定任务,并使用它来检索、更新和删除特定任务的详细信息。

现在我们已经完成了设计API的过程,在开始实现之前,让我们先对其进行文档记录!

我在开始时提到过,我们将实现REST API,所以我们将使用OpenAPI来记录它。创建一个名为oas.yaml的文件,并向其中写入以下内容:

Openapi:3.0.3信息:标题:TODO API描述:允许您管理待办事项列表版本的API:1.0.0路径:/TODO/:Get:Summary:返回待办事项列表响应:';200';:描述:JSON任务数组内容:Application/json:Schema:Type:Array Items:$ref:';#/Components/Schemas/GetTaskSchema';发布:摘要:创建任务请求Body:Required:True Content:Application/Json:Schema:$ref:';#/components/schemas/CreateTaskSchema';响应:';201';:描述:创建的任务内容的Json表示形式:Application/Json:Schema:$ref:';#/Components/Schemas/GetTaskSchema';/todo/{item_id}:参数:-in:路径名:item_id必填:true schema:type:string format:uuid get:Summary:返回任务的详细信息响应:';200';:描述:任务内容的JSON表示形式:application/json:schema:$ref:';#/Components/Schemas/GetTaskSchema';';404';:$ref:';#/Components/Responses/NotFound';PUT:摘要:更新现有任务请求Body:Required:True Content:Application/Json:Schema:$ref:';#/components/schemas/CreateTaskSchema';Responses:';200';:描述:任务内容的Json表示形式:Application/Json:Schema:$ref:';#/Components/Schemas/GetTaskSchema';';404';:$Ref:';#/Components/Responses/NotFound';删除:摘要:删除现有任务响应:';204';:描述:已成功删除资源';404';:$ref:';#/Components/Responses/NotFound';Components:Responses:NotFound:Description:未找到指定的资源。内容:application/json:schema:$ref:';#/Components/Schemas/Error';架构:Error:Type:Object Properties:Code:Type:Number Message:Type:String Status:Type:String CreateTaskSchema:Type:Object Required:-任务添加属性:FALSE属性:STATUS:TYPE:String枚举:-PENDING-PROGRESS-COMPLETED DEFAULT:PENDING TASK:TYPE:String GetTaskSchema:TYPE:Object Required:-Created-id-Priority-Status-Task AdditionalProperties:FALSE属性:ID:TYPE:STRING FORMAT:UUID Created:TYPE:INTEGER DESCRIPTION:UNIX时间映射状态格式的日期:类型:字符串枚举:-挂起-进度-已完成任务:类型:字符串。

既然我们已经有了API规范,我们就可以开始实现API服务器了。如果您使用的另一个团队必须实现API的客户端应用程序(如前端应用程序),请确保中心位置(如GitHub存储库或URL端点)的所有团队都可以使用API规范。稍后我会再写一篇文章来说明你是如何做到这一点的。

要实现该API,我们将结合使用Flask和Flask-Smorest。Flask-Smorest是一个rest API框架,它使用棉花糖来验证模式。出于说明的目的,我们将保持这一超级简单,整个应用程序将在一个文件中,项目列表将由内存中的列表表示。在真实的应用程序中,您希望使用持久存储,并确保不同的组件进入不同的模块。要使代码正常工作,您需要的唯一依赖项是最平滑的烧瓶,因此请确保安装了它。创建一个名为app.py的文件,并将以下内容复制到其中:

导入时间从flask导入UUID从flask导入Flask从flask导入MethodView从flask_momoest导入Api,Blueprint,从棉花糖中止导入架构,字段,排除,验证app=Flask(__Name__)app.config[';API_Title';]=';TODO API';app.config[';API_Version';]=';1.0.0';app.config[';OPENAPI_Version';]=';3.0.3';App.config[';OPENAPI_JSON_PATH';]=";API-spec.json";app.config[';OPENAPI_URL_PREFIX';]=";/";app.config[';OPENAPI_SWAGGER_UI_PATH';]=";/docs";app.config[';OPENAPI_SWAGGER_UI_URL';]=";Https://cdn.jsdelivr.net/npm/swagger-ui-dist/";api_title=';api_version=';1.0.0';api=API(APP)类创建任务模式(架构):类元:未知=排除状态=fields.String(默认值=';待定';,验证=有效.OneOf([';待定';,';进度';,';完成';]),)task=fields.String()class GetTaskSchema(CreateTaskSchema):Created=fields.Integer(Required=True)id=fields.UUID(Required=True)Blueprint=Blueprint(';TODO';,';TODO';,url_prefix=';/TODO';,description=';API

如果您现在运行它,您将收到一个错误,指出我们需要为/todo/{item_id}URL路径中的item_id参数提供一个示例。您还将看到一些关于规范格式问题的警告,您可以安全地忽略这些警告,因为API规范是有效的(您可以使用如下所示的外部工具进行验证:https://editor.swagger.io/).。让我们继续为item_id添加一个示例:

/todo/{item_id}:参数:-in:路径名:item_id必填:true schema:type:string format:uuid示例:d222e7a3-6afb-463a-9709-38eb70cc670d...。

如果现在再次运行Dredd,将会有5个测试通过,3个失败。如果仔细观察这些故障,您会发现所有这些故障都与/todo/{item_id}URL路径下的现有资源上的操作相关。看起来Dredd正在选择我们在规范中提供的示例ID,并期望存在具有该ID的资源。显然,在我们开始运行API之前不存在任何资源,因此我们希望Dredd首先使用post/todo/实际创建一个资源,获取创建的资源的ID,并使用它测试/todo/{item_id}URL路径下的端点。我们该怎么做呢?使用Dredd钩子!

Dredd钩子提供了一个简单的接口,允许用户在事务之前和之后执行操作。每个事务都由“名称”标识,“名称”是唯一标识操作的不同参数的组合。要列出规范中的所有可用名称,请运行以下命令:

我们需要/todo/{item_id}URL路径下成功操作的名称,即返回200和204等成功状态代码的操作(即不返回404的操作)。创建一个名为hooks.y的文件,并将以下内容复制到其中:

Import json import Dredd_Hooks Response_Stash={}@Dredd_Hooks.After(';/TODO/&>;创建任务&>201&>应用程序/json;)定义SAVE_CREATED_TASK(TRANSACTION):RESPONSE_PAYLOAD=transaction[';results';][';fields';][';body';][';values';][';actual';]task_id=json.load(Response_Payload)[';id';]response_stash[';Created_task_id';]=task_id@Dredd_hooks.Being(';/todo/{item_id}>;)返回任务>;200>;应用程序/json';)def BEVER_GET_TASK(Transaction):Transaction[';fullPath&39;]=';/todo/';+response_stash[';created_task_id';]@dredd_hooks.Being(';/todo/{item_id}>;)更新现有任务>;200>;应用程序/json';def PUT_TASK(TRANSACTION):transaction[';fullPath';]=';/todo/';+response_stash[';Created_task_id';]@Dredd_hooks.pre(';/TODO/{Item_id}>;删除现有任务>;204';)def BEFORE_DELETE_TASK(TRANSACTION):TRANSACTION[';fullPath';]=';/TODO/';+response_stash[';CREATED_TASK_ID';]。

我将在另一个教程中详细介绍Dredd和Dredd Hooks。您可以使用以下命令通过这些挂钩运行Dredd:

对,所以现在我们可以确定我们的API实现符合规范。如果另一个开发前端应用程序的团队也进行了类似的测试,我们可以相当肯定的是,服务器和前端应用程序都会很好地集成在一起,并且没有错误。或者至少我们不会因为不符合API而遇到集成错误。

现在假设我们想要更改API。随着时间的推移,我们希望能够为每项任务分配优先级。这将在任务资源有效负载中产生一个称为“优先级”的新字段,该字段可能具有下列值之一:

我们应该如何对待这一变化呢?我们正在实践文档驱动的开发,所以首先我们将更改规范。一旦我们更新了规范,我们就可以同时更改后端和前端。对后端API的任何不符合规范的更改都将导致测试失败,不应该发布。

..。CreateTaskSchema:Type:Object Required:-Task AdditionalProperties:False属性:Priority:Type:String enum:-Low-Medium-High Default:Low Status:Type:String Enum:-Pending-Progress-Completed Default:Pending Task:Type:String GetTaskSchema:Type:Object Required:-Created-id-Priority-Status-Task AdditionalProperties属性:False属性:ID:Type:String Format:UUID Created:Type:Integer Description:日期格式为UNIX时间映射优先级:TYPE。用法:string枚举:-低-中-高默认:低状态:type:string枚举:-挂起-进度-已完成任务:type:string。

类CreateTaskSchema(Schema):class Meta:UNKNOWN=排除优先级=fields.String(默认=';低';,';,验证=validate.OneOf([';低';,';中';,';高';]),)status=fields.String(默认=';等待';,验证=validate.OneOf([';等待';,';进度';,';完成';]),)task=fields.String()类GetTaskSchema(CreateTaskSchema):Created=fields.Integer(Required=True)id=fields.UUID(Required=True)。

如果再次运行dredd命令,测试现在通过。在此示例中,更新棉花糖架构足以更新应用程序。在实际的应用程序中,您将把数据持久化到数据库中,并且还需要运行一些迁移来更新模型。