SpringBoot入门实践(四)-RESTful API 最佳设计

 我的博客:兰陵笑笑生,欢迎浏览博客!

 上一章 SpringBoot入门实践(三)-工程结构与常用注解当中,我们介绍了SpringBoot的项目的工程结构和一些常用的注解。本章简单的讲一讲API的设计。

 网上有非常多的关于API设计的介绍,本章内容从自己的实际经验简单介绍如何更好的设计API。

API的有什么关键的要求

1 任何客户端都能够调用,需要一个标准,使得客户端和服务端达成一致,必须简单

2 api独立于客户端,api的发展,现有的客户端可以继续运行,不需要修改。

RESTful URLS

REST API 是围绕这资源设计的,资源有一个唯一的标识符,并且使用JSON

使用HTTP 定义资源的操作

  1. GET 检所获取资源
  2. POST 创建资源,当然POST也可以用来触发实际上不创建资源的操作
  3. PUT 创建或者更新
  4. DELETE 移除

SpringBoot与RESTful

 在springBoot当中提供了很多的注解,使用这些注解就很容易的实现RESTful的api

@RequestMapping:

 该注解有6个参数:

  • value/path :路径名称
  • params :请求的参数
  • method :方法类型 GET、POST、PUT、DELETE 等
  • headers: request 中必须包含某些指定的 header 值,才能让该方法处理请求。
  • consumes : (Content-Type),例如application/json,text/html;

例如请求 表示一個GET请求:

@RequestMapping(path = "/list",method = RequestMethod.GET, consumes ="application/json")
public HttpResponse list() { 
    List<User> user = new ArrayList<>(userMap.values());   
    return HttpResponse.ok().setData(user);
}

@PostMapping

  等价于@RequestMapping(path = "/**",method = RequestMethod.POST)

@GetMapping

  等价于@RequestMapping(path = "/**",method = RequestMethod.GET)

@DeleteMapping

  等价于@RequestMapping(path = "/**",method = RequestMethod.DELETE)

@PutMapping

  等价于@RequestMapping(path = "/**",method = RequestMethod.PUT)

具体的设计

 URL的定义在每个公司中都不一样,这里简单给出一些我自己的实际经验,url路径定义:

1 url理解简单

2 api有版本控制, 版本控制可以快速迭代并避免无效的请求访问已更新的接入点

http://www.example.com/api/v{version}/模块名称/操作名称

获取单个用户

GET /api/v1/user/get/1

或者

GET /api/v1/user/1

分页或者全部的查询,输入的参数是JSON

POST /api/v1/user/list

这个新增和更新一般都是一个api,当然也可以使用PUT

POST /api/v1/user/save

删除用户资源

DELETE /api/v1/user/delete/12

或者

DELETE /api/v1/user/12

public class User {  
    private Long id; 
    private String userName; 
    private Long age;
    
    ...
        
}

自定义统一返回结果

package com.miroservice.chapter2.common;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.io.Serializable;
import java.util.*;


@JsonInclude(JsonInclude.Include.NON_NULL)
public class HttpResponse implements Serializable { 
    
    private long code;  
    private String message; 
    private Long total;  
    private Object data;  
    private List<Object> table;  
    private String requestid;  
    public static final long CODE_SUCCESS = 200;  
    public static final long CODE_ERROR = 500;  
    public static final long CODE_VALIDATE_FAILED = 404;  
    public static final long CODE_UNAUTHORIZED = 401;  
    public static final long CODE_FORBIDDEN = 403;    
    public HttpResponse() {    
        this(200, (String) null);    } 
    public HttpResponse(long code, String message) {    
        this.code = CODE_SUCCESS;     
        this.code = code;      
        this.message = message;      
        this.requestid = UUID.randomUUID().toString().replaceAll("-", "");  
    }   
    public static HttpResponse error(String message) {   
        return new HttpResponse(500, message);  
    }  
    public HttpResponse setData(Object data) {       
        this.data = data;    
        return this;  
    }  
    public HttpResponse data(Object data) {  
        return this.setData(data);  
    }
    public HttpResponse addListItem(Object item) {   
        if (this.table == null) {       
            this.table = new ArrayList();     
        }     
        this.table.add(item);       
        
        return this;  
    }  
    public HttpResponse setTotal(Long total) {    
        this.total = total;    
        return this;   
    }    
    public HttpResponse setTotal(Integer total) {    
        this.total = (long) total;      
        return this; 
    }   
    public static HttpResponse ok() {  
        return new HttpResponse();  
    }  
    public HttpResponse set(String field, String value) {  
        if (this.data == null || !(this.data instanceof Map)) {    
            this.data = new HashMap();    
        }      
        ((Map) this.data).put(field, value); 
        return this;
    }   
    public long getCode() {   
        return this.code;   
    }   
    public HttpResponse setCode(long code) {    
        this.code = code;      
        return this; 
    }    public String getMessage() {       
        return this.message;  
    }   
    public HttpResponse setMessage(String message) {     
        this.message = message;       
        return this;   
                                               
    }   
    public Long getTotal() {     
        return this.total == null && this.table != null ?     Long.valueOf(String.valueOf(this.table.size())) : this.total;   
    }   
    public Object getData() {  
        return this.data;   
                                 }  
    public List<Object> getTable() {      
        return this.table; 
    }  
    public HttpResponse setTable(List table) {    
        this.table = table;   
        return this;  
    }   
    public HttpResponse table(List table) {    
        return this.setTable(table); 
    }  
    public String getRequestid() {     
        return this.requestid; 
    }  
    public HttpResponse setRequestid(String requestid) {     
        this.requestid = requestid;    
        return this;    }
}

 Controller层

/** 
* 1 api 的设计 一眼明了
* 2 有版本
* 3 控制层统一返回自定义的结果 
*/
@RestController
@RequestMapping("/api/v1/user")
public class UserController { 
    static Map<Long, User> userMap = new HashMap<Long, User>();   
    /**   
    * 列表请求 
    * @return 
    */   
   @RequestMapping(method = RequestMethod.GET, consumes = "application/json")                @GetMapping(consumes = "application/json")    
    public HttpResponse list() {     
        List<User> user = new ArrayList<>(userMap.values());     
        return HttpResponse.ok().setData(user);  
    }  
    /**   
    * 保存  
    * @param user   
    * @return  
    */  
    @PostMapping("/save")   
    public HttpResponse save(@RequestBody User user) {      
        userMap.put(user.getId(), user);      
        return HttpResponse.ok();    }  
    /**     
    * 获取单个用户  
    * @param id   
    * @return   
    */  
    @GetMapping("/get/{id}") 
    public HttpResponse get(@PathVariable Long id) {   
        final User user = userMap.get(id);    
        return HttpResponse.ok().setData(user);    } 
    /**   
    * 删除  
    * @param id   
    * @return  
    */  
    @DeleteMapping("/delete/{id}")   
    public HttpResponse delete(@PathVariable Long id) {  
        userMap.remove(id);     
        return HttpResponse.ok(); 
    }
    
}

 以上就是本期的分享,你还可以关注本博客的#Spring Boot入门实践系列!#

 参考: #微软的API实践#
     #Spring REST DOC#

本文由博客一文多发平台 OpenWrite 发布!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容