API style guide
原文:https://docs.gitlab.com/ee/development/api_styleguide.html
- Instance variables
- Entities
- Documentation
- Methods and parameters description
- Using API path helpers in GitLab Rails codebase
API style guide
该样式指南建议 API 开发的最佳做法.
Instance variables
请不要使用实例变量,不需要它们(我们不需要像在 Rails 视图中那样访问它们),可以使用局部变量.
Entities
始终使用实体来呈现端点的有效负载.
Documentation
API 端点必须随附文档 ,除非文档在内部或在功能标志后面. 这些文档应位于同一合并请求中,或者在严格必要的情况下,应与原始合并请求具有相同的里程碑.
Methods and parameters description
每种方法都必须使用Grape DSL进行描述(有关示例,请参见https://gitlab.com/gitlab-org/gitlab/blob/master/lib/api/environments.rb ):
desc
的方法的总结. 您应该将其传递给其他细节,例如:- 添加端点时的 GitLab 版本. 如果它在功能标志后面,请提及: 该功能由:feature_flag_symbol 功能标志控制.
- 如果不赞成使用端点,那么,何时删除它
一个很好的例子如下:
desc 'Get all broadcast messages' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::BroadcastMessage
end
params do
optional :page, type: Integer, desc: 'Current page number'
optional :per_page, type: Integer, desc: 'Number of messages per page'
end
get do
messages = BroadcastMessage.all
present paginate(messages), with: Entities::BroadcastMessage
end
Declared parameters
Grape 允许您仅访问由
params
块声明的params
. 它过滤掉已传递但不允许的参数.
– https://github.com/ruby-grape/grape#declared
Exclude parameters from parent namespaces
默认情况下,
declared(params)
包括在所有父名称空间中定义的参数.
– https://github.com/ruby-grape/grape#include-parent-namespaces
在大多数情况下,您将希望从父名称空间中排除参数:
declared(params, include_parent_namespaces: false)
When to use declared(params)
You should always use declared(params)
when you pass the parameters hash as arguments to a method call.
例如:
# bad
User.create(params) # imagine the user submitted `admin=1`... :)
# good
User.create(declared(params, include_parent_namespaces: false).to_h)
注意:
Hashie::Mash
declared(params)
返回一个Hashie::Mash
对象,您必须在其上调用.to_h
.
但是,当我们访问单个元素时,可以直接使用params[key]
.
例如:
# good
Model.create(foo: params[:foo])
Array types
在 Grape v1.3 +中,必须使用coerce_with
块定义数组类型,否则当从 API 请求传递字符串时,参数将无法验证. 有关更多详细信息,请参见Grape 升级文档 .
Automatic coercion of nil inputs
在 Grape v1.3.3 之前,具有nil
值的 Array 参数将自动强制为空 Array. 但是,由于v1.3.3 中的拉取请求,情况不再如此. 例如,假设您定义一个具有可选参数的 PUT /test
请求:
optional :user_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The user ids for this rule'
通常情况下,把一个请求/test?user_ids
会导致葡萄传递params
的{ user_ids: nil }
.
这可能会导致端点期望为空数组且无法正确处理nil
输入的错误. 为了保留以前的行为,有一个辅助方法coerce_nil_params_to_array!
在所有 API 调用的before
块中使用的代码:
before do
coerce_nil_params_to_array!
end
进行此更改后,对 PUT /test?user_ids
的请求将使 Grape 传递的params
为{ user_ids: [] }
.
Grape Tracker 中存在一个未解决的问题,可以使此操作更容易.
Using HTTP status helpers
对于非 200 HTTP 响应,请使用lib/api/helpers.rb
提供的帮助lib/api/helpers.rb
以确保行为正确( not_found!
, no_content!
等). 这些将throw
Grape 并中止端点的执行.
对于DELETE
请求,通常还应该destroy_conditionally!
地使用destroy_conditionally!
默认情况下,helper 会在成功时返回204 No Content
响应,或者在给定的If-Unmodified-Since
标头超出范围时返回412 Precondition Failed
. 该助手在传递的资源上调用#destroy
,但是您也可以通过传递一个块来实现自定义删除方法.
Using API path helpers in GitLab Rails codebase
因为我们支持在相对 URL 下安装 GitLab ,所以在使用 Grape 生成的 API 路径帮助程序时必须考虑到这一点. 任何此类 API 路径帮助程序用法都必须包装在expose_path
帮助程序调用中.
例如:
- endpoint = expose_path(api_v4_projects_issues_related_merge_requests_path(id: @project.id, issue_iid: @issue.iid))
Custom Validators
为了验证 API 请求中的某些参数,我们先验证它们,然后再进一步发送它们(例如 Gitaly). 以下是自定义验证器 ,到目前为止,我们已经添加了它们以及如何使用它们. 我们还编写了有关如何添加新的自定义验证器的指南.
Using custom validators
FilePath
:GitLab 支持我们需要遍历文件路径的各种功能.
FilePath
验证器针对不同情况验证参数值. 主要是,它使用File::Separator
检查路径是否是相对路径,是否包含../../
相对遍历,以及路径是否是绝对路径,例如/etc/passwd/
.Git SHA
:Git SHA
验证器检查 Git SHA 参数是否为有效的 SHA. 它通过使用commit.rb
文件中提到的正则表达式进行检查.Absence
:Absence
验证器检查给定参数哈希中是否缺少特定参数.IntegerNoneAny
:IntegerNoneAny
验证器检查给定参数的值是Integer
,None
还是Any
. 它仅允许上述任何一个值在请求中前进.ArrayNoneAny
:ArrayNoneAny
验证器检查给定参数的值是Array
,None
还是Any
. 它仅允许上述任何一个值在请求中前进.
Adding a new custom validator
自定义验证器是在将参数发送到平台进行进一步处理之前对其进行验证的好方法. 如果我们在一开始就识别出无效的参数,它将在服务器和平台之间来回保存一些时间.
如果您需要添加自定义验证器,它将被添加到validators
目录中自己的文件中. 由于我们使用Grape添加 API,因此我们继承了Grape::Validations::Base
类中的Grape::Validations::Base
类. 现在,您要做的就是定义validate_param!
该方法具有两个参数: params
哈希和要验证的param
名称.
该方法的主体进行了验证参数值的艰苦工作,并将适当的错误消息返回给调用方方法.
最后,我们使用以下行注册验证器:
Grape::Validations.register_validator(<validator name as symbol>, ::API::Helpers::CustomValidators::<YourCustomValidatorClassName>)
添加验证器后,请确保将其rspec
添加到其在validators
目录中的自己的文件中.
Internal API
内部 API已记录供内部使用. 请保持最新状态,以便我们了解不同组件正在使用哪些端点.
Avoiding N+1 problems
为了避免在 API 端点中返回记录集合时常见的 N + 1 问题,我们应该使用预先加载.
在 API 中执行此操作的标准方法是让模型实现一个名为with_api_entity_associations
的范围,该范围将预加载 API 中返回的关联和数据. 在Issue
模型中可以看到此范围的示例.
在同一个模型的 API 中有多个实体的情况下(例如UserBasic
, User
和UserPublic
),您应谨慎使用此范围. 可能是您针对最基本的实体进行了优化,并在该范围上建立了连续的实体.
当在 Todos API 中返回时, with_api_entity_associations
范围还将自动为Todo
目标 预先加载数据 .
有关预加载的更多上下文和讨论,请参阅此合并请求 , 该合并请求引入了作用域.
Verifying with tests
当 API 端点返回集合时,无论现在还是将来,始终添加一个测试以验证 API 端点没有 N + 1 问题. 我们可以使用ActiveRecord::QueryRecorder
做到这一点.
Example:
def make_api_request
get api('/foo', personal_access_token: pat)
end
it 'avoids N+1 queries', :request_store do
# Firstly, record how many PostgreSQL queries the endpoint will make
# when it returns a single record
create_record
control = ActiveRecord::QueryRecorder.new { make_api_request }
# Now create a second record and ensure that the API does not execute
# any more queries than before
create_record
expect { make_api_request }.not_to exceed_query_limit(control)
end
Testing
在编写新的 API 端点的测试,可以考虑使用模式夹具位于/spec/fixtures/api/schemas
. 您可以expect
响应匹配给定的架构:
expect(response).to match_response_schema('merge_requests')
另请参阅在测试中验证 N + 1 性能 .