├── .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 | ![REST APIs 成熟度模型](img/richardson-maturity-model.png) 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 | --------------------------------------------------------------------------------