├── .editorconfig
├── .gitattributes
├── LICENSE
├── README.md
├── csharp
├── .editorconfig
└── README.md
└── http-api
├── README.md
├── date-time.md
├── error.md
├── http-header.md
├── http-method.md
├── http-status-code.md
├── i18n.md
├── img
└── richardson-maturity-model.png
├── json.md
├── name-case.md
├── url.md
└── versioning.md
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | end_of_line = lf
8 | indent_style = space
9 | indent_size = 2
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.md]
14 | trim_trailing_whitespace = false
15 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 linianhui
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Moved to https://linianhui.github.io/code-guide/
2 |
3 | 1. [HTTP APIs设计/规范指南][HTTP API]
4 | 2. [C#代码规范][C#]
5 |
6 |
7 | [HTTP API]:http-api
8 | [C#]:csharp
9 |
--------------------------------------------------------------------------------
/csharp/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | # VS 2017 : 15.6+ 内置支持;Tools > Options > Text Editor > "Follow project coding conventions"
4 | # VS Code : (Ctrl+E) ext install editorconfig
5 |
6 |
7 | root = true
8 |
9 | [*]
10 | charset = utf-8
11 | end_of_line = lf
12 | indent_style = space
13 | indent_size = 2
14 | insert_final_newline = true
15 | trim_trailing_whitespace = true
16 |
17 | [*.cs]
18 | end_of_line = crlf
19 | indent_size = 4
20 |
21 | [*.cake]
22 | end_of_line = crlf
23 | indent_size = 4
24 |
25 | [*.ps1]
26 | end_of_line = crlf
27 | indent_size = 4
28 |
29 | [*.csproj]
30 | indent_size = crlf
31 |
32 | [*.sln]
33 | indent_size = crlf
34 |
35 |
36 | # C# 命名规则
37 | # https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions
38 | [*.cs]
39 | ## 接口示例:IPascalCase
40 | dotnet_naming_rule.interface_rule.symbols = interface_symbol
41 | dotnet_naming_rule.interface_rule.style = interface_style
42 | dotnet_naming_rule.interface_rule.severity = error
43 | dotnet_naming_symbols.interface_symbol.applicable_kinds = interface
44 | dotnet_naming_style.interface_style.required_prefix = I
45 | dotnet_naming_style.interface_style.capitalization = pascal_case
46 |
47 | ## 类,结构,枚举,委托,方法,属性,事件示例: PascalCase
48 | dotnet_naming_rule.pascal_case_rule.symbols = pascal_case_symbol
49 | dotnet_naming_rule.pascal_case_rule.style = pascal_case_style
50 | dotnet_naming_rule.pascal_case_rule.severity = error
51 | dotnet_naming_symbols.pascal_case_symbol.applicable_kinds = class,struct,enum,delegate,method,property,event
52 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case
53 |
54 | ## 私有字段示例: _camelCase
55 | dotnet_naming_rule.private_field_rule.symbols = private_field_symbol
56 | dotnet_naming_rule.private_field_rule.style = private_field_style
57 | dotnet_naming_rule.private_field_rule.severity = error
58 | dotnet_naming_symbols.private_field_symbol.applicable_kinds = field
59 | dotnet_naming_symbols.private_field_symbol.applicable_accessibilities = private
60 | dotnet_naming_style.private_field_style.required_prefix = _
61 | dotnet_naming_style.private_field_style.capitalization = camel_case
62 |
63 | ## 非私有字段示例: PascalCase
64 | dotnet_naming_rule.non_private_field_rule.symbols = non_private_field_symbol
65 | dotnet_naming_rule.non_private_field_rule.style = non_private_field_style
66 | dotnet_naming_rule.non_private_field_rule.severity = error
67 | dotnet_naming_symbols.non_private_field_symbol.applicable_kinds = field
68 | dotnet_naming_symbols.non_private_field_symbol.applicable_accessibilities = public,internal,protected,protected_internal
69 | dotnet_naming_style.non_private_field_style.capitalization = pascal_case
70 |
71 | ## 参数示例: camelCase
72 | dotnet_naming_rule.parameter_rule.symbols = parameter_symbol
73 | dotnet_naming_rule.parameter_rule.style = parameter_style
74 | dotnet_naming_rule.parameter_rule.severity = error
75 | dotnet_naming_symbols.parameter_symbol.applicable_kinds = parameter
76 | dotnet_naming_style.parameter_style.capitalization = camel_case
77 |
78 | ## 常量示例: ALL_UPPER
79 | dotnet_naming_rule.const_rule.symbols = const_symbol
80 | dotnet_naming_rule.const_rule.style = const_style
81 | dotnet_naming_rule.const_rule.severity = error
82 | dotnet_naming_symbols.const_symbol.required_modifiers = const
83 | dotnet_naming_symbols.const_symbol.applicable_kinds = field
84 | dotnet_naming_style.const_style.capitalization = all_upper
85 | dotnet_naming_style.const_style.word_separator = _
86 |
87 | ## 异步方法示例: xxxAsync
88 | dotnet_naming_rule.async_method_rule.symbols = async_method_symbol
89 | dotnet_naming_rule.async_method_rule.style = async_method_style
90 | dotnet_naming_rule.async_method_rule.severity = error
91 | dotnet_naming_symbols.async_method_symbol.required_modifiers = async
92 | dotnet_naming_symbols.async_method_symbol.applicable_kinds = method
93 | dotnet_naming_style.async_method_style.required_suffix = Async
94 |
95 |
96 | # C# 代码风格规则
97 | # https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference#language-conventions
98 | [*.cs]
99 | # this.限定符
100 | ## IDE0003
101 | dotnet_style_qualification_for_field = false : error
102 | ## IDE0003
103 | dotnet_style_qualification_for_property = false : error
104 | ## IDE0003
105 | dotnet_style_qualification_for_method = false : error
106 | ## IDE0003
107 | dotnet_style_qualification_for_event = false : error
108 |
109 | # 基元类型
110 | ## IDE0012
111 | dotnet_style_predefined_type_for_locals_parameters_members = true : error
112 | ## IDE0013
113 | dotnet_style_predefined_type_for_member_access = true : error
114 |
115 | # 修饰符
116 | ## IDE0040
117 | dotnet_style_require_accessibility_modifiers = always : error
118 | ## IDE0036
119 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async : error
120 |
121 | # 表达式
122 | ## IDE0017
123 | dotnet_style_object_initializer = true : error
124 | ## IDE0028
125 | dotnet_style_collection_initializer = true : error
126 | ## IDE0033
127 | dotnet_style_explicit_tuple_names = true : error
128 | ## IDE0029
129 | dotnet_style_coalesce_expression = true : error
130 | ## IDE0031
131 | dotnet_style_null_propagation = true : error
132 | ## IDE0037
133 | dotnet_style_prefer_inferred_tuple_names = false : error
134 | ## IDE0037
135 | dotnet_style_prefer_inferred_anonymous_type_member_names = false : error
136 |
137 | # 隐式和显式类型(var)
138 | ## IDE0007
139 | csharp_style_var_for_built_in_types = true : error
140 | ## IDE0007
141 | csharp_style_var_when_type_is_apparent = true : error
142 | ## IDE0007
143 | csharp_style_var_elsewhere = true : error
144 |
145 | # 表达式体成员
146 | ## IDE0022
147 | csharp_style_expression_bodied_methods = false : error
148 | ## IDE0021
149 | csharp_style_expression_bodied_constructors = false : error
150 | ## IDE0023,IDE0024
151 | csharp_style_expression_bodied_operators = false : error
152 | ## IDE0025
153 | csharp_style_expression_bodied_properties = when_on_single_line : error
154 | ## IDE0026
155 | csharp_style_expression_bodied_indexers = when_on_single_line : error
156 | ## IDE0027
157 | csharp_style_expression_bodied_accessors = when_on_single_line : error
158 |
159 | # 模式匹配
160 | ## IDE0020
161 | csharp_style_pattern_matching_over_is_with_cast_check = true : error
162 | ## IDE0019
163 | csharp_style_pattern_matching_over_as_with_null_check = true : error
164 |
165 | # 内联变量声明
166 | ## IDE0018
167 | csharp_style_inlined_variable_declaration = true : error
168 |
169 | # 表达式
170 | ## IDE0034
171 | csharp_prefer_simple_default_expression = true : error
172 | ## IDE0042
173 | csharp_style_deconstructed_variable_declaration = false : error
174 | ## IDE0039
175 | csharp_style_pattern_local_over_anonymous_function = true : error
176 |
177 | # Null检查
178 | ## IDE0016
179 | csharp_style_throw_expression = false : error
180 | ## IDE0041
181 | csharp_style_conditional_delegate_call = true : error
182 |
183 | # 代码块
184 | ## IDE0011
185 | csharp_prefer_braces = true : error
186 |
187 |
188 | # C# 代码格式化规则
189 | # https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference#formatting-conventions
190 | # https://github.com/dotnet/roslyn/pull/15020
191 | [*.cs]
192 | # using排序
193 | dotnet_sort_system_directives_first = true
194 |
195 | # 新行
196 | csharp_new_line_before_open_brace = all
197 | csharp_new_line_before_else = true
198 | csharp_new_line_before_catch = true
199 | csharp_new_line_before_finally = true
200 | csharp_new_line_before_members_in_object_initializers = true
201 | csharp_new_line_before_members_in_anonymous_types = true
202 | csharp_new_line_between_query_expression_clauses = true
203 |
204 | # 缩进
205 | csharp_indent_block_contents = true
206 | csharp_indent_braces = false
207 | csharp_indent_case_contents = true
208 | csharp_indent_case_labels = true
209 | csharp_indent_switch_labels = true
210 | csharp_indent_labels = one_less_than_current
211 | csharp_label_indentation = true
212 |
213 | # 间距
214 | csharp_space_after_cast = false
215 | csharp_space_after_colon_in_inheritance_clause = true
216 | csharp_space_after_comma = true
217 | csharp_space_after_dot = false
218 | csharp_space_after_keywords_in_control_flow_statements = true
219 | csharp_space_after_semicolon_in_for_statement = true
220 | csharp_space_around_binary_operators = before_and_after
221 | csharp_space_around_declaration_statements = do_not_ignore
222 | csharp_space_before_colon_in_inheritance_clause = true
223 | csharp_space_before_comma = false
224 | csharp_space_before_dot = false
225 | csharp_space_before_open_square_brackets = false
226 | csharp_space_before_semicolon_in_for_statement = false
227 | csharp_space_between_empty_square_brackets = false
228 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
229 | csharp_space_between_method_call_name_and_opening_parenthesis = false
230 | csharp_space_between_method_call_parameter_list_parentheses = false
231 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
232 | csharp_space_between_method_declaration_name_and_open_parenthesis = false
233 | csharp_space_between_method_declaration_parameter_list_parentheses = false
234 | csharp_space_between_parentheses = none
235 | csharp_space_between_square_brackets = false
236 |
237 | # 换行
238 | csharp_preserve_single_line_statements = false
239 | csharp_preserve_single_line_blocks = true
240 |
--------------------------------------------------------------------------------
/csharp/README.md:
--------------------------------------------------------------------------------
1 | # 代码规范
2 |
3 | 1. 代码风格配置文件 [.editorcofig](.editorconfig)
4 |
5 | # 项目结构
6 |
7 | https://github.com/linianhui/cake.example
--------------------------------------------------------------------------------
/http-api/README.md:
--------------------------------------------------------------------------------
1 | # Moved to https://linianhui.github.io/code-guide/
2 |
3 | # HTTP APIs 设计/规范指南
4 |
5 | 根据REST APIs的成熟度模型 ,此规范关注的是`Level 2`的APIs。
6 | 
7 |
8 | # 1 设计指南
9 |
10 | HTTP APIs主要由四部分组成:`HTTP`,`URL`,`资源`,`资源的表述(JSON)`。资源的表述格式通常都采用`JSON`,故而后面就使用`JSON`代指`资源的表述`。根据这些组成部分,按照以下**3**个步骤设计APIs:
11 | 1. 基于`资源`设计APIs。
12 | 1. 基于`URL`标识`资源`。
13 | 1. 基于`JSON`和`HTTP`操作`URL`标识的`资源`。
14 |
15 | ## 1.1 基于`资源`设计API
16 |
17 | 设计HTTP APIs的首要任务是识别出业务领域中存在的资源。资源是对服务端提供的服务进行分解、组合后的一个被命名的抽象概念。
18 |
19 | >有一个很重要的点需要明确一下:资源**≠**数据表,它们两个之间并没有直接的映射关系。如果直接把数据存储结构映射为资源,则只会让资源无法有效的表达业务需要,也会造成资源本身和底层存储的紧耦合。
20 |
21 | 资源的设计是以`名词`为中心的。比如`今天的天气`是一个资源;而`获取今天的天气`则不是,它代表的是对`今天的天气`资源的一个读取操作。基于此我们可以抽象出来一个`天气`的资源。
22 |
23 | ## 1.2 基于`URL`标识`资源`
24 |
25 | 识别出`资源`后,则需要为其分配一个`URL`进行标识。
26 | 1. 一个`资源`可以有多个`URL`。
27 | 1. 一个`URL`只能标识一个`资源`。
28 | >总结来说就是`资源`:`URL`的关系就是`1:N`的关系。
29 |
30 | 比如上面提到的`天气`和`今天的天气`这两个资源,可以用如下的`URL`进行标识。
31 |
32 | | 资源 | URL |
33 | | ---------- | --------------------------------------- |
34 | | 天气 | `/weather` |
35 | | 今天的天气 | `/weather/today` |
36 | | 今天的天气 | `/weather/2018-04-01`,今天是2018-04-01 |
37 |
38 | `资源`体现在`URL`中的`Path`部分。
39 | >关于资源名采用单数还是复数的问题,这里统一为单数(即使代表的是一个集合资源)。原因有3:
40 | > 1. 一致性:中文中并无复数的概念,可保持一致。
41 | > 2. 无二义性:比如news,既是单数也是复数。所以就不必追求它们的单数或者复数形式形式;基于同样的原则,那么原本就是单数的名词,也无需刻意追求复数形式。
42 | > 3. 简单性:英文名词的复数形式并不统一(比如order > orders, history > histories),使用单数可以避免团队成员对于这些差异的不同理解与争执。
43 |
44 | `资源`存在子资源的情况下,可以把子资源提升为顶层的资源。比如有一个订单资源`/order/{order_id}`,订单中包含2件物品。
45 | ```
46 | # 不推荐 单个子资源
47 | /order/{order_id}/item/{item_id}
48 |
49 | # 推荐 单个子资源
50 | /order-item/{order_item_id}
51 |
52 | # 推荐 子资源集合
53 | /order/{order_id}/item
54 | ```
55 | ## 1.3 基于`JSON`和`HTTP`操作`URL`标识的`资源`
56 |
57 | 在标识出`资源`以后,就可以使用`HTTP`通过`JSON`来操作资源了。
58 | 1. 使用`HTTP Method`来映射对资源的操作请求(CRUD或者其他)。
59 | 2. 使用`HTTP Header`携带请求/响应所需的元数据信息。
60 | 3. 使用`HTTP Stauts Code`代表`HTTP协议层面`的响应状态。
61 | 4. 使用`JSON`作为数据交换格式。
62 |
63 | # 2 规范指南
64 |
65 | ## 2.1 通用部分
66 | 1. [HTTP Method 规范][HTTP Method]
67 | 1. [HTTP Header 规范][HTTP Header]
68 | 1. [HTTP Stauts Code 规范][HTTP Stauts Code]
69 | 1. [URL 规范][URL]
70 | 1. [JSON 规范][JSON]
71 | 1. [命名(URL和JSON)规范][Name Case]
72 | 1. [日期和时间格式化 规范][Date Time]
73 | 1. [国际化 规范][i18n]
74 | 1. [版本化 规范][versioning]
75 | 1. [错误处理 规范][error]
76 |
77 |
78 | ## 2.2 Request 公共查询参数
79 |
80 | | 参数用途 | 参数名 | 取值范围 |
81 | | -------- | ---------------------------------------------- | ----------------------------------------------------- |
82 | | 分页 | `page`
`page_size` | >=1 |
83 | | 排序 | `sort` | `{field_name}\|{asc\|desc},{field_name}\|{asc\|desc}` |
84 | | 区间 | `{field_name}_before`
`{field_name}_after` | 无要求 |
85 | | 时间 | `{field_name}_at` | 无要求 |
86 |
87 | 示例:
88 | ```http
89 | GET /user
90 | ?page=2
91 | &page_size=10
92 | &sort=name,age|desc
93 | &created_at_after=2018-01-01
94 | &created_at_before=2018-06-01
95 | ```
96 |
97 | 上面的查询代表的含义:按照`name`升序和`age`倒序的排序方式;获取`created_at`时间位于`2018-01-01`和`2018-06-01`区间内;按照每页`10`条数据,获取第`2`页的数据。
98 |
99 | ## 2.3 Response 分页数据结构
100 |
101 | 在分页请求的时候,API会返回分页后的数据和分页的信息。
102 | ```json
103 | {
104 | "page": 2,
105 | "page_size": 10,
106 | "total_count": 100,
107 | "items":[
108 | {...},
109 | {...},
110 | ]
111 | }
112 | ```
113 | # 3 示例
114 |
115 | ... 待补充
116 |
117 | # 参考资料
118 |
119 | PayPal的API设计指南:https://github.com/paypal/api-standards
120 |
121 | REST架构风格的出处:架构风格与基于网络的软件架构设计(by Fielding, R.)论文。
122 | 1. 英文版: https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
123 | 1. 中文版: http://www.infoq.com/cn/minibooks/web-based-apps-archit-design
124 | 1. 本人的解读REST系列博客:http://www.cnblogs.com/linianhui/category/774322.html
125 |
126 | HTTP APIs 四大组成部分(HTTP,URI,MIME,JSON)
127 | 1. HTTP/1.1 ( RFC7230-7235 ) :https://tools.ietf.org/html/rfc7230
128 | 1. URI ( RFC 3986 ) :https://tools.ietf.org/html/rfc3986
129 | 1. MIME ( RFC 2387 ):https://tools.ietf.org/html/rfc2387
130 | 1. JSON : http://json.org/
131 |
132 | Hypermedia
133 | 1. JSON Schema: http://json-schema.org/
134 |
135 | URL模板
136 | 1. URI Template ( RFC6570 ) :https://tools.ietf.org/html/rfc6570
137 |
138 | 时间日期格式化
139 | 1. Date and Time Formats - ISO 8601:https://www.w3.org/TR/NOTE-datetime
140 | 1. Date and Time on the Internet: Timestamps ( RFC 3339 ) :https://tools.ietf.org/html/rfc3339#section-5.6
141 |
142 | 国际化
143 | 1. https://www.iso.org/iso-3166-country-codes.html
144 | 1. https://www.iso.org/iso-4217-currency-codes.html
145 | 1. https://www.iso.org/iso-639-language-codes.html
146 |
147 | REST APIs 成熟度模型:https://martinfowler.com/articles/richardsonMaturityModel.html
148 |
149 | [HTTP Header]:http-header.md
150 | [HTTP Method]:http-method.md
151 | [HTTP Stauts Code]:http-status-code.md
152 | [Name Case]:name-case.md
153 | [Date Time]:date-time.md
154 | [URL]:url.md
155 | [JSON]:json.md
156 | [i18n]:i18n.md
157 | [versioning]:versioning.md
158 | [error]:error.md
159 |
--------------------------------------------------------------------------------
/http-api/date-time.md:
--------------------------------------------------------------------------------
1 | # 时间
2 |
3 | 时间采用[RFC 3339]中定义的通用的格式。表示方法如下:
4 |
5 | | 格式 | 组成部分 | 示例 |
6 | |------------------|-----------|-----------|
7 | | `date-fullyear` | 4位数的年份 | 2018 |
8 | | `date-month` | 2位数的月份 | 04 |
9 | | `date-mday` | 2位数的日期 | 01 |
10 | | `time-hour` | 2位数的小时 | 02 |
11 | | `time-minute` | 2位数的分钟 | 08 |
12 | | `time-second` | 2位数的秒数 | 59 |
13 | | `time-secfrac` | 秒的分数部分 | .256 |
14 | | `time-numoffset` | **+**/**-**`time-hour`**:**`time-minute` | +08:00 |
15 | | `time-offset` | **Z**/`time-numoffset` | Z+08:00 |
16 | | `partial-time` | `time-hour`**:**`time-minute`**:**`time-second time-secfrac` | 02:08:59.256 |
17 | | `full-date` | `date-fullyear`**-**`date-month`**-**`date-mday` | 2018-04-01 |
18 | | `full-time` | `partial-time time-offset` | 02:08:59.256+08:00 |
19 | | `date-time` | `full-date`**T**`full-time` | 2018-04-01T02:08:59.256Z+08:00 |
20 |
21 | >扩展:`date-fullyear-month`(年月)可表示为`date-fullyear`**-**`date-month`,比如`2018-04`。
22 |
23 | 1. 日期和时间应该满足上面表格中定义的格式。
24 | 1. 优先采用UTC时间(即Z+00:00)。即使没有跨时区的要求,也必须携带时区偏移信息,比如`2018-04-01T02:08:59.256Z+08:00`。
25 |
26 |
27 | # 参考
28 |
29 | https://tools.ietf.org/html/rfc3339
30 |
31 | https://www.w3.org/TR/NOTE-datetime
32 |
33 |
34 | [RFC 3339]:https://tools.ietf.org/html/rfc3339
35 |
36 | [Date and Time Formats]:https://www.w3.org/TR/NOTE-datetime
37 |
--------------------------------------------------------------------------------
/http-api/error.md:
--------------------------------------------------------------------------------
1 | # 错误处理
2 | 虽然`HTTP Stauts Code`有`4xx`和`5xx`的状态码来表示哪里出错了,但是其代表的只是`HTTP协议层面`的错误描述,它无法提供和业务相关的更具体错误描述。基于此种情况,我们需要设计一套描述业务层面错误的数据结构:
3 |
4 | ```json
5 | [
6 | {
7 | "error": "user_name",
8 | "message": "用户名不能为空。"
9 | },
10 | {
11 | "error": "age",
12 | "message": "用户年龄不能小于0。"
13 | }
14 | ]
15 | ```
16 |
17 | 1. 这个数据结构仅在状态码为`4xx`和`5xx`出现的时候才会使用;`2xx`的时候则不包含此数据结构。
18 | 2. `error`字段可以是一些出错的字段名、某一错误类别(比如`no_permission`)等等。
19 | ```json
20 | [
21 | {
22 | "error": "no_permission",
23 | "message": "没有user.delete的权限"
24 | }
25 | ]
26 | ```
27 |
28 | # 参考
29 |
30 | Problem Details for HTTP APIs : https://tools.ietf.org/html/rfc7807
31 |
--------------------------------------------------------------------------------
/http-api/http-header.md:
--------------------------------------------------------------------------------
1 | # HTTP Headers
2 |
3 | Http Header的用途在于携带`HTTP Request`和`HTTP Response`的元数据信息的`Name:Value`数据(Name不区分大小写,通常都采用首字母大写,`-`分隔的写法,比如`Content-Type`)。按其用途可以分为如下4类:
4 |
5 | 1. 通用类:描述请求和响应的。
6 | 2. 请求类:描述请求的。
7 | 3. 响应类:描述响应的。
8 | 4. 表述类:描述请求和响应的`Body`部分的。
9 |
10 | HTTP APIs中常用到的Headers:
11 |
12 | | Header | 描述说明 | 示例 |
13 | |--------------------|-------------------------------|------|
14 | | [Accept] | 客户端期望服务器返回的数据格式。 | `Accept:application/json` |
15 | | [Accept-Charset] | 客户端期望服务器返回的数据的字符集。| `Accept-Charset:utf-8` |
16 | | [Content-Type] | 描述`Body`的数据类型。 | `Content-Type:application/json`
17 | | [Content-Encoding] | 描述`Body`的编码方式。 | `Content-Encoding: gzip`|
18 | | [Content-Length] | 描述`Body`的长度。 | `Content-Length: 1234`|
19 | | [Location] | `Response`中提供给客户端的连接 | `Location: /user/1`|
20 |
21 | ## 示例
22 |
23 | HTTP Request:
24 | ```http
25 | POST /xxx HTTP/1.1
26 | Accept: application/json;
27 | Accept-Charset: utf-8
28 | Content-Type: application/json;charset=utf-8
29 |
30 | {
31 | "x":1,
32 | "y":2
33 | }
34 | ```
35 |
36 | HTTP Response:
37 | ```http
38 | HTTP/1.1 201 Created
39 | Content-Type: application/json;charset=utf-8
40 | Content-Encoding: gzip
41 | Location: /xxx/1
42 | Request-Id: {id}
43 |
44 | ```
45 |
46 | >`Request-Id`可以由`Http Request`传入,也可以由服务端生成,追加此信息到`log`中,便于服务端追踪请求。
47 |
48 | # 参考
49 |
50 | https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
51 |
52 | https://tools.ietf.org/html/rfc7231#section-5
53 |
54 | https://tools.ietf.org/html/rfc7231#section-7
55 |
56 |
57 | [Accept]:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept
58 |
59 | [Accept-Charset]:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Charset
60 |
61 | [Content-Type]:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
62 |
63 | [Content-Encoding]:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
64 |
65 | [Content-Length]:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length
66 |
67 | [Location]:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location
68 |
--------------------------------------------------------------------------------
/http-api/http-method.md:
--------------------------------------------------------------------------------
1 | # HTTP Method
2 |
3 | 面向资源设计的HTTP APIs中,绝大部分的操作都是`CRUD(Create,Read,Update,Delete)`,都可以映射为某一个[HTTP Method]。其余的无法映射的操作一般存在两种解决方案:
4 |
5 | 1. 抽象出新的资源,比如`禁用用户`的操作。假设用户的资源是`/user`,那么可以抽象出来一个被锁定的用户的资源`/user/disabled`。如此以来,
6 | 1. `禁用用户`:`POST /user/disabled`或者`PUT /user/disabled/{user_id}`。
7 | 2. `取消禁用`:`DELETE /user/disabled/{user_id}`。
8 | 3. `获取被禁用的用户列表`:`GET /user/disabled`。
9 | 2. 如果上面的方式无法满足需要,则可以采用`POST`和`URL/动词`的组合。还拿上面的举例:
10 | 1. `禁用用户`:`POST /user/{user_id}/disable`或者`PUT /user/{user_id}/disable`。
11 | 2. `取消禁用`:`DELETE /user/{user_id}/disable`。
12 | 3. `获取被禁用的用户列表`:`GET /user?status=DISABLED`。
13 |
14 | ## HTTP Method 语义列表
15 |
16 | 每一个`HTTP Method`都具有一下两个HTTP协议层面的语义。
17 |
18 | | HTTP 语义 | 含义 |
19 | |----------|-----|
20 | | [安全] | 操作不会对资源产生副作用,不会修改资源。|
21 | | [幂等] | 执行一次和重复执行N次,结果是一样的。|
22 |
23 | ## HTTP Method 列表
24 |
25 | | HTTP Method | 安全 | 幂等 | 描述说明 |
26 | |-------------|-----|-----|----------|
27 | | [GET] | ✔ | ✔ | 获取一个资源 |
28 | | [PUT] | ✘ | ✔ | 更新或创建一个资源(完整替换) |
29 | | [PATCH] | ✘ | ✘ | 更新一个资源(部分更新) |
30 | | [DELETE] | ✘ | ✔ | 删除一个资源 |
31 | | [POST] | ✘ | ✘ | 创建,或者不满足以上四个Method语义的所有操作 |
32 |
33 | >`PATCH`和`POST`都是`不安全`且`不幂等`的,差异在于`PATCH`仅是用于部分更新资源。`PATCH`是一个可选支持的`HTTP Method`,可能会存在一些代理、网关等组件不支持的情况,所以推荐用`POST`来代替它。
34 |
35 | # 参考
36 |
37 | https://tools.ietf.org/html/rfc7231#section-4
38 |
39 | https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
40 |
41 |
42 | [HTTP Method]:https://tools.ietf.org/html/rfc7231#section-4
43 |
44 | [安全]:https://tools.ietf.org/html/rfc7231#section-4.2.1
45 |
46 | [幂等]:https://tools.ietf.org/html/rfc7231#section-4.2.2
47 |
48 | [GET]:https://tools.ietf.org/html/rfc7231#section-4.3.1
49 |
50 | [POST]:https://tools.ietf.org/html/rfc7231#section-4.3.3
51 |
52 | [PUT]:https://tools.ietf.org/html/rfc7231#section-4.3.4
53 |
54 | [DELETE]:https://tools.ietf.org/html/rfc7231#section-4.3.5
55 |
56 | [PATCH]:https://tools.ietf.org/html/rfc5789
57 |
--------------------------------------------------------------------------------
/http-api/http-status-code.md:
--------------------------------------------------------------------------------
1 | # HTTP Status Code
2 |
3 | [HTTP Status Code]用来指示`HTTP协议层面的请求状态`。它由一个数字和一个描述消息构成,比如`200 OK`。有以下几类状态码。
4 | 1. `2xx`:执行成功。
5 | 2. `4xx`:客户端的错误,通常情况下客户端需要修改请求然后再次发送请求。
6 | 3. `5xx`:服务端的错误。
7 |
8 | | Status Code | 描述说明 |
9 | |------------------------------|---------|
10 | | [200 OK] | 执行成功。 |
11 | | [201 Created] | 资源创建成功,应该在`HTTP Response Header`中返回`Location`来提供新创建资源的URL地址。 |
12 | | [202 Accepted] | 服务端已经接受了请求,但是并未处理完成,适用于一些异步操作。 |
13 | | [204 No Content] | 执行成功,但是不会在`HTTP Response Body`中放置数据。 |
14 | | [400 Bad Request] | 客户端请求错误,客户端应该根据`HTTP Response Body`中的错误描述来修改请求,然后才能再次发送。 |
15 | | [401 Unauthorized] | 客户端未提供授权信息。 |
16 | | [403 Forbidden] | 客户端无权访问(客户端可能已经提供了授权信息,但是权限不够)。如果出于信息隐藏的目的,也可以使用`404 Not Found`来替代。|
17 | | [404 Not Found] | 客户端请求的资源不存在。 |
18 | | [405 Method Not Allowed] | 客户端使用了不被允许的HTTP Method。比如某一个URL只允许`POST`,但是客户端采用了`GET`。 |
19 | | [406 Not Acceptable] | 客户端发送的`Accept`不被支持。比如客户端发送了`Accept:application/xml`,但是服务器只支持`Accept:application/json`。 |
20 | | [409 Conflict] | 客户端提交的数据过于陈旧,和服务端的存在冲突,需要客户端重新获取最新的资源再发起请求。 |
21 | | [415 Unsupported Media Type] | 客户端发送的`Content-Type`不被支持。比如客户端发送了`Content-Type:application/xml`,但是服务器只支持`Content-Type:application/json`。 |
22 | | [429 Too Many Requests] | 客户端在指定的时间内发送了太多次数的请求。用于限速,比如只允许客户端在1分钟内发送100次请求,客户端在发送101次请求的时候,会得到这样的响应。 |
23 | | [500 Internal Server Error] | 服务器遇见了未知的内部错误。 |
24 | | [501 Not Implemented] | 服务器还未实现次功能。 |
25 | | [503 Service Unavailable] | 服务器繁忙,暂时无法处理客户端的请求。 |
26 |
27 | # 参考
28 |
29 | https://tools.ietf.org/html/rfc7231#section-6
30 |
31 | https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
32 |
33 | https://httpstatuses.com/
34 |
35 | [HTTP Status Code]:https://tools.ietf.org/html/rfc7231#section-6
36 |
37 | [200 OK]:https://tools.ietf.org/html/rfc7231#section-6.3.1
38 |
39 | [201 Created]:https://tools.ietf.org/html/rfc7231#section-6.3.2
40 |
41 | [202 Accepted]:https://tools.ietf.org/html/rfc7231#section-6.3.3
42 |
43 | [204 No Content]:https://tools.ietf.org/html/rfc7231#section-6.3.5
44 |
45 | [400 Bad Request]:https://tools.ietf.org/html/rfc7231#section-6.5.1
46 |
47 | [401 Unauthorized]:https://tools.ietf.org/html/rfc7235#section-3.1
48 |
49 | [403 Forbidden]:https://tools.ietf.org/html/rfc7231#section-6.5.3
50 |
51 | [404 Not Found]:https://tools.ietf.org/html/rfc7231#section-6.5.4
52 |
53 | [405 Method Not Allowed]:https://tools.ietf.org/html/rfc7231#section-6.5.5
54 |
55 | [406 Not Acceptable]:https://tools.ietf.org/html/rfc7231#section-6.5.5
56 |
57 | [409 Conflict]:https://tools.ietf.org/html/rfc7231#section-6.5.8
58 |
59 | [415 Unsupported Media Type]:https://tools.ietf.org/html/rfc7231#section-6.5.13
60 |
61 | [429 Too Many Requests]:https://tools.ietf.org/html/rfc6585#section-4
62 |
63 | [500 Internal Server Error]:https://tools.ietf.org/html/rfc7231#section-6.6.1
64 |
65 | [501 Not Implemented]:https://tools.ietf.org/html/rfc7231#section-6.6.2
66 |
67 | [503 Service Unavailable]:https://tools.ietf.org/html/rfc7231#section-6.6.4
68 |
--------------------------------------------------------------------------------
/http-api/i18n.md:
--------------------------------------------------------------------------------
1 | # 国际化
2 |
3 | 国家代码:采用[Country Code]`ISO 3166 alpha-2`版本(2个字母)。示例:
4 | 1. 中国:`CN`
5 | 1. 美国:`US`
6 | 1. 其他:https://en.wikipedia.org/wiki/ISO_3166-2#Current_codes
7 |
8 |
9 | 货币代码:采用[Currency Code]`ISO 4217:2015`版本(3个字母)。示例:
10 | 1. 人民币:`CNY`
11 | 1. 美元:`USD`
12 | 1. 其他:https://en.wikipedia.org/wiki/ISO_4217#Active_codes
13 |
14 |
15 | 语言代码:采用[Language Code]`ISO 639-1:2002`版本(2个字母)。示例:
16 | 1. 中文:`zh`
17 | 1. 英文:`en`
18 | 1. 其他:https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
19 |
20 |
21 | Locale Code由[Language Code]和[Country Code]组合而成。示例:
22 | 1. `zh-CN`
23 | 1. `en-US`
24 |
25 | # 参考
26 |
27 | https://www.iso.org/iso-3166-country-codes.html
28 |
29 | https://www.iso.org/iso-4217-currency-codes.html
30 |
31 | https://www.iso.org/iso-639-language-codes.html
32 |
33 | [Country Code]:https://www.iso.org/iso-3166-country-codes.html
34 | [Currency Code]:https://www.iso.org/iso-4217-currency-codes.html
35 | [Language Code]:https://www.iso.org/iso-639-language-codes.html
36 |
--------------------------------------------------------------------------------
/http-api/img/richardson-maturity-model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linianhui/code-guide/083b6d6305656c2a6b2c030b594b188baa7bd25e/http-api/img/richardson-maturity-model.png
--------------------------------------------------------------------------------
/http-api/json.md:
--------------------------------------------------------------------------------
1 | # JSON
2 | [JSON]是一种应用非常广泛的数据交换格式。其包含6种基本的数据类型。
3 |
4 | | JSON数据类型 | 示例 |
5 | |------------ |-------|
6 | | `string` | "lnh" |
7 | | `number` | 1.23 |
8 | | `array` | [...] |
9 | | `object` | {...} |
10 | | `bool` | true/false |
11 | | `null` | null |
12 |
13 | 1. JSON的命名遵循[命名规范#JSON][JSON命名规范]。
14 | 1. JSON中没有原生的日期和时间类型,应该遵循[日期和时间的格式化规范][Date Time]的要求,使用`string`类型表示。
15 | 1. JSON中出现的和国际化相关的数据遵循[国际化规范][i18n]中的要求。
16 | 1. `null`值的字段不能忽略掉,应该显式的表示为`"field_name":null`。
17 |
18 | JSON示例:
19 | ```json
20 | {
21 | "first_name": "li",
22 | "lase_name": "nianhui",
23 | "gender": "MALE",
24 | "age": 123,
25 | "tags": ["coder"],
26 | "is_admin": true,
27 | "address": null,
28 | "country_code":"CN",
29 | "currency_code":"CNY",
30 | "language_code":"zh",
31 | "locale_code":"zh-CN",
32 | "created_at":"2018-01-02T03:04:05.128Z+08:00",
33 | "time_zone":"+08:00"
34 | }
35 | ```
36 |
37 | # 参考资料
38 | http://json.org/
39 |
40 | https://tools.ietf.org/html/rfc7159
41 |
42 | [JSON]:http://json.org/
43 | [JSON命名规范]:name-case.md#json
44 | [Date Time]:date-time.md
45 | [i18n]:i18n.md
46 |
--------------------------------------------------------------------------------
/http-api/name-case.md:
--------------------------------------------------------------------------------
1 | # 命名规范
2 |
3 | | 规则名称 | 说明 | 取值范围 |
4 | |-----------------------------|--------------------|--------|
5 | | `all-lower-hyphen-case` | 采用`-`分隔符的全小写 | `a-z 0-9 -`|
6 | | `all_lower_underscore_case` | 采用`_`分隔符的全小写 | `a-z 0-9 _`|
7 | | `ALL_UPPER_UNDERSCORE_CASE` | 采用`_`分隔符的全大写 | `A-Z 0-9 _`|
8 |
9 | ## URL
10 |
11 | | URL组件 | 命名规则 |
12 | |-----------| ----------------------------|
13 | | scheme | `all-lower-hyphen-case` |
14 | | authority | `all-lower-hyphen-case` |
15 | | path | `all-lower-hyphen-case` |
16 | | query | `all_lower_underscore_case` |
17 | | fragment | `all-lower-hyphen-case` |
18 |
19 | >URL的query部分是`name=value`而不是`key=value`,URL支持name重复存在,Web服务端框架绝大部分都支持直接映射为数组。此外命名规则约束的是`name`部分,而不关心`value`部分,`value`部分应该采用`urlencode`进行编码。
20 |
21 | 示例:
22 | ```http
23 | https://api.my-server.com/v1/user-stories?dipplay_names=abc&display_names=efg
24 | ```
25 | 服务端会得到一个类型为数组的`dispaly_names`参数。
26 | ```json
27 | display_names = [
28 | "abc",
29 | "efg"
30 | ];
31 | ```
32 |
33 | ## JSON
34 |
35 | | JSON | 命名规则 |
36 | |-------------------| ----------------------------|
37 | | filed_name | `all_lower_underscore_case` |
38 | | filed_value | 无要求 |
39 | | ENUM_FILED_VALUE | `ALL_UPPER_UNDERSCORE_CASE` |
40 |
41 | >`ENUM_FILED_VALUE`用于表示枚举字段,用全大写和`_`分隔符,以示和普通的字符串进行区分。
42 |
43 | 示例:
44 |
45 | ```json
46 | {
47 | "first_name":"li",
48 | "lase_name":"nianhui",
49 | "gender":"MALE",
50 | "remark":"描述信息",
51 | "age":1234
52 | }
53 | ```
54 | # 参考
55 |
56 | https://support.google.com/webmasters/answer/76329
57 |
58 | https://stackoverflow.com/questions/5543490/json-naming-convention
59 |
60 | https://tools.ietf.org/html/rfc3986#section-2.4
61 |
--------------------------------------------------------------------------------
/http-api/url.md:
--------------------------------------------------------------------------------
1 | # URL
2 | URL遵循[RFC 3986]规范,由以下几部分组成。
3 |
4 |
5 | https://api.linianhui.test:8080/user/disabled?first_name=li#title 6 | \___/ \______________________/\_____________/\___________/\____/ 7 | | | | | | 8 | scheme authority path query fragment 9 |10 | 11 | URL的命名遵循[命名规范#URL][URL命名规范]的要求。 12 | 13 | # 参考 14 | 15 | https://tools.ietf.org/html/rfc3986 16 | 17 | [RFC 3986]:https://tools.ietf.org/html/rfc3986 18 | [URL命名规范]:name-case.md#url 19 | -------------------------------------------------------------------------------- /http-api/versioning.md: -------------------------------------------------------------------------------- 1 | # 版本化 2 | 3 | 在`Level 2`的HTTP APIs中,虽然我们推荐也努力使得我们的APIs不做不兼容的改动,但是依然无法彻底的避免不兼容的升级。这就使得我们不得不对APIs进行版本化管理。通常有以下 **3** 种方案: 4 | 5 | 1. URL 6 | ```http 7 | GET http://api.linianhui.com/v1/weather HTTP/1.1 8 | ``` 9 | 2. Request Header 10 | ```http 11 | GET http://api.linianhui.com/weather HTTP/1.1 12 | Api-Version: v1 13 | ``` 14 | 3. Request Header (Accept Header) 15 | ```http 16 | GET http://api.linianhui.com/weather HTTP/1.1 17 | Accept: application/vnd.v1+json 18 | ``` 19 | 20 | **在`Level 2`的API中优先推荐使用方案1(`URL`)**。理由是其更直观,便于实现,便于日志追踪。 21 | --------------------------------------------------------------------------------