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