API 就是开发者使用的界面。个人目标不只是能用,并且好用,跨平台(PC, Android, IOS, etc...)使用。本文将详细介绍 API 的设计及异常处理,并将异常信息进行封装友好地反馈给前端.前端
上篇文章 先后端彻底分离初探 只是讲了些宽泛的概念,接下来的文章将直接上干货,干货的源码会挂在 GitHub 上。git
先后端彻底分离后,前端和后端如何交互?答:经过双方协商好的API。github
接下来我分享我本身设计的 API 接口,欢迎各位朋友指教。json
将涉及的实体抽象成资源,即按 id
访问资源,在 url
上作文章,之后不再用为 url
起名字而苦恼了;segmentfault
使用 HTTP
动词对资源进行 CRUD
(增删改查):后端
get -> 查, post -> 增, put -> 改, delete -> 删
URL 命名规则,对于资源没法使用一个单数名词表示的状况,我使用中横线 -
链接api
资源采用名词命名,e.g:产品 -> product
安全
新增资源,e.g:新增产品 url -> /product, verb -> POST
分布式
修改资源,e.g:修改产品 url -> /products/{id}, verb -> PUT
模块化
资源详情,e.g:指定产品详情 url -> /products/{id}, verb -> GET
删除资源,e.g:删除产品 url -> /products/{id}, verb -> DELETE
资源列表,e.g:产品列表 url -> /products, verb -> GET
资源关联关系,e.g:收藏产品 url -> /products/{id}/star, verb -> PUT
资源关联关系,e.g:删除收藏产品 url -> /products/{id}/star, verb -> DELETE
目前我 API 的设计只涉及这两点,至于第三点 HATEOAS
(Hypermedia As The Engine Of Application State) 那就由读者本身去选择了,
<!--more-->
本文中只涉及了设计的理念,具体的实现请下载源码 rest-api,项目内写了比较详细的注释。
实战将从业务场景出发,详细介绍如何使用 HTTP verb 对资源进行操做(状态转移),使用 JSON 返回结果(资源表述),并定义 JSON 的基础结构。
requestParams:
{ }
responseBody:
{ "meta": { }, "data": { } }
meta
中封装操做成功或失败的消息,data
中封装返回的具体数据。
当新建商品或更新产品时,相关属性封装在 JSON 中,经过 POST 或 PUT 发送。
{ "name": "Apple Watch SPORT", "description": "Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。" }
当用户对商品进行操做后,将获得响应结果。GET, POST, PUT 操做成功,返回以下结果
{ "meta": { "code": 201, "message": "建立成功" }, "data": { "id": "5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9", "name": "Apple Watch SPORT", "description": "Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。" } }
DELETE 操做成功,返回以下结果
{ "meta": { "code": 204, "message": "删除成功" } }
电商网站的管理员对商品进行新增、编辑、删除、浏览的操做,暂时不考虑认证受权,只关注对商品的操做。
为了之后便于作分布式,全部资源 id(表主键)均采用 uuid。
url: /api/product
method: POST
requestParams:
{ "name": "Apple Watch SPORT", "description": "Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。" }
responseBody
{ "meta": { "code": 201, "message": "建立成功" }, "data": { "id": "5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9", "name": "Apple Watch SPORT", "description": "Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。" } }
url: /api/products/{id}
method: PUT
requestParams:
{ "name": "iPhone 6", "description": "这次苹果发布会发布了iPhone 6与iPhone 6 Plus,搭载iOS 8,尺寸分别是4.7和5.5英寸。外观设计再也不棱角分明,表层玻璃边有一个弧度向下延伸,与阳极氧化铝金属机身边框衔接。机身背部采用三段式设计。机身更薄,续航能力更强。" }
responseBody
{ "meta": { "code": 200, "message": "修改为功" }, "data": { "id": "5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9", "name": "iPhone 6", "description": "这次苹果发布会发布了iPhone 6与iPhone 6 Plus,搭载iOS 8,尺寸分别是4.7和5.5英寸。外观设计再也不棱角分明,表层玻璃边有一个弧度向下延伸,与阳极氧化铝金属机身边框衔接。机身背部采用三段式设计。机身更薄,续航能力更强。" } }
url: /api/products/{id}
method: DELETE
responseBody
{ "meta": { "code": 204, "message": "删除成功" }, "data": {} }
url: /api/products/{id}
method: GET
responseBody:
//删除前 { "meta": { "code": 200, "message": "查询成功" }, "data": { "id": "5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9", "name": "Apple Watch SPORT", "description": "Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。" } }
//删除后 { "meta": { "code": 404, "message": "指定产品不存在" } }
url: /api/products
method: GET
responseBody
{ "meta": { "code": 200, "message": "获取所有商品成功" }, "data": [ { "id": "5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9", "name": "Apple Watch SPORT", "description": "Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。" }, { "id": "9db1992a-c342-4ff0-a2a4-aeb3dbfd93f6", "name": "Apple Watch SPORT", "description": "Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。" }, { "id": "4481619b-45c5-4729-9539-f93bb01f10d8", "name": "Apple Watch SPORT", "description": "Sport 系列的表壳材料为轻巧的银色及深空灰色阳极氧化铝金属,强化 Ion-X 玻璃材质为显示屏提供保护。搭配高性能 Fluoroelastomer 表带,共有 5 款缤纷色彩。" } ] }
业务场景一中只涉及了单个资源的操做,但实际场景中还有些关联操做;如用户去电商网站浏览商品,并收藏了一些商品,以后又取消收藏了部分商品。
暂时不考虑用户认证受权,之后加了token
后,用户信息能够从中获取。
url: /api/products/{id}/star
method: PUT
responseBody
{ "meta": { "code": 200, "message": "收藏商品[5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9]成功" }, "data": [ { "id": "5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9", "name": "iPhone 6", "description": "这次苹果发布会发布了iPhone 6与iPhone 6 Plus,搭载iOS 8,尺寸分别是4.7和5.5英寸。外观设计再也不棱角分明,表层玻璃边有一个弧度向下延伸,与阳极氧化铝金属机身边框衔接。机身背部采用三段式设计。机身更薄,续航能力更强。" } ] }
url: /api/products/{id}/star
method: DELETE
responseBody
{ "meta": { "code": 200, "message": "删除收藏商品[5308e9c2-a4ce-4dca-9373-cc1ffe63d5f9]成功" }, "data": [] }
全部自定义异常继承 RuntimeException,在业务层抛出,统一在 Controller 层进行处理。
异常分为全局异常和局部异常,例如 http method unsupported (405),unauthorized (401),accessDenied (403),not found (404) 等属于全局异常。针对对独立业务的一些异常属于局部异常,例如产品编辑出错。
异常在 Controller 中进行处理,并封装成 json 返回给前端,封装后的数据以下,相关实现见源码
{ "meta": { "code": 404, "message": "指定产品不存在" } }
{ "meta": { "code": 405, "message": "Request method 'POST' not supported" } }
先后端彻底分离之安全认证与受权-上
先后端彻底分离之安全认证与受权-下
先后端彻底分离以前端模块化开发
先后端彻底分离以前端路由系统
先后端彻底分离以后端面向服务的模块化开发
原文出自 先后端彻底分离之API设计,欢迎转载,转载请注明出处。