Spring Boot集成REST

  1. 集成REST
<!--只需要在spring-boot-starter-web依赖在pom中,就会自动支持REST:-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  1. ResTemplate
    Spring Boot提供了ResTemplate来辅助发起一个REST请求,默认通过JDK自带的HttpURLConnection来作为底层的HTTP消息发送的方式,使用JackSon来序列化服务返回的JSON数据。
  • ResTemplate是核心类,提供了所有访问REST服务的接口。
HTTP Method JavaAPI
DELETE delete
GET getForObject,getForEntity
HEAD headForHeaders
OPTIONS optionsForAllow
POST postForObject,postForLocation
PUT put
其他 exchange(通用)
  • Spring Boot提供了ResTemplateBulider来创建一个ResTemplate
@Resource
private RestTemplateBuilder restTemplateBuilder;

public void foo(){
    RestTemplate restTemplate= restTemplateBuilder.build();
}
@RestController
@RequestMapping(value ="/api")
@Slf4j
@PropertySource(value = "classpath:config/constant.properties")
public class DemoController {


    @Value(value = "${api.user}")
    private String URI;

    @Resource
    private RestTemplateBuilder restTemplateBuilder;


    @GetMapping(value = "/user/{id}")
    public User testUser(@PathVariable String id){

        RestTemplate restTemplate =restTemplateBuilder.build();

        String url=URI+"/user/queryByInfo?openId="+id;

        User user=restTemplate.getForObject(url,User.class,id);

        return user;
    }
}

用postman测试一下:localhost:8987/api/user/111 返回结果如下:

{
    "userId": "1633c318f09f4071a1609cb5b1a952c5",
    "userOpenid": "111",
    "userName": "不如吃茶去",
    "userImage": "https://wx.qlogo.cn/mmopen/vi_32/DYAIOgq83eqTrMHUs50DDYOiaB8j6Fibvge3DfgO8nskx2s2vPsut3AvyvYTibRYXoeG19kYlsozm9VXd4hGQP4icg/132",
    "userReport": 3,
    "userTel": "123",
    "userSname": "3",
    "userSno": "3",
    "userStatus": 1
}

然后来说一下getForObject方法,第一个参数是URI模板,第二个参数是期待返回的对象,后面是URI模板对应的参数列表。参数列表既可以是数组,也可以是Map,以上代码也可以写成:

Map map=new HashMap<>();

map.put("id",id);

User user=restTemplate.getForObject(url,User.class,map);

还可以获取返回HTTP相关的头信息,可以调用restTemplate.getEntity,此方法返回RespomseEntity,包含了头信息:

ResponseEntity<User> responseEntity=restTemplate.getForEntity(url,User.class,id);

User user=responseEntity.getBody();

HttpHeaders httpHeaders=responseEntity.getHeaders();

log.info("httpheaders:{}",httpHeaders);

// 打印输出的结果
httpheaders:[Content-Type:"application/json;charset=UTF-8", Transfer-Encoding:"chunked", Date:"Mon, 18 Mar 2019 13:20:06 GMT"]
  • 添加信息时可以使用postForObject方法,此方法接受三个参数,第一个是URI,第二个是Post参数,可以是HttpEntity,或者是某个POJO对象,POJO对象在这种情况下会自动转成HttpEntity,第三个参数是返回值类型:
@RestController
@RequestMapping(value ="/api")
@Slf4j
@PropertySource(value = "classpath:config/constant.properties")
public class DemoController {

    @Value(value = "${api.user}")
    private String URI;

    @Resource
    private RestTemplateBuilder restTemplateBuilder;


    @GetMapping(value = "/user")
    public String testUser(){

        RestTemplate restTemplate =restTemplateBuilder.build();

        String url=URI+"/user";


        User user=User.builder().userId("xxx")
                .userImage("xxx")
                .userName("xxx")
                .userOpenid("xxx")
                .userTel("xxx").build();

        HttpEntity<User> body=new HttpEntity<>(user);

        ResponseEntity<String> responseEntity =restTemplate.postForEntity(url,body,String.class);

        String ret=responseEntity.getBody();
//{code:200}
        return ret;
    }
}

注: 最好使用HttpEntuty,可以提供额外的HTTP头信息,比如在用于权限验证时添加jwt。(对于权限认证放在前端还是放在后端,以及动态权限的实现,目前为止还是不太清楚。)

以下来自一位大佬的总结:

先说一说我权限控制的主体思路,前端会有一份路由表,它表示了每一个路由可访问的权限。当用户登录之后,通过 token 获取用户的 role ,动态根据用户的 role 算出其对应有权限的路由,再通过router.addRoutes动态挂载路由。但这些控制都只是页面级的,说白了前端再怎么做权限控制都不是绝对安全的,后端的权限验证是逃不掉的。
我司现在就是前端来控制页面级的权限,不同权限的用户显示不同的侧边栏和限制其所能进入的页面(也做了少许按钮级别的权限控制),后端则会验证每一个涉及请求的操作,验证其是否有该操作的权限,每一个后台的请求不管是 get 还是 post 都会让前端在请求 header里面携带用户的 token,后端会根据该 token 来验证用户是否有权限执行该操作。若没有权限则抛出一个对应的状态码,前端检测到该状态码,做出相对应的操作。
权限 前端or后端 来控制?
有很多人表示他们公司的路由表是于后端根据用户的权限动态生成的,我司不采取这种方式的原因如下
1.项目不断的迭代你会异常痛苦,前端新开发一个页面还要让后端配一下路由和权限,让我们想了曾经前后端不分离,被后端支配的那段恐怖时间了。
2.其次,就拿我司的业务来说,虽然后端的确也是有权限验证的,但它的验证其实是针对业务来划分的,比如超级编辑可以发布文章,而实习编辑只能编辑文章不能发布,但对于前端来说不管是超级编辑还是实习编辑都是有权限进入文章编辑页面的。所以前端和后端权限的划分是不太一致。
3.还有一点是就vue2.2.0之前异步挂载路由是很麻烦的一件事!不过好在官方也出了新的api,虽然本意是来解决ssr的痛点的。


addRoutes
在之前通过后端动态返回前端路由一直很难做的,因为vue-router必须是要vue在实例化之前就挂载上去的,不太方便动态改变。不过好在vue2.2.0以后新增了router.addRoutes


  • 如果期望返回的类信号是一个列表,不能简单的使用xxxForObject,因为存在泛型的类型擦除,Restemplate在反序列化的时候并不知道实际返回反序列化的类型,因此可以使用ParameterzedTypeReference来包含泛型类型:
@GetMapping(value = "/user")
public List<LostVo> testUser(){

    RestTemplate restTemplate =restTemplateBuilder.build();

    String url=URI+"/lost";

    ParameterizedTypeReference<List<LostVo>> typeReference=new ParameterizedTypeReference<List<LostVo>>() {};

//        HttpHeaders headers = new HttpHeaders();
//        请勿轻易改变此提交方式,大部分的情况下,提交方式都是表单提交
//        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//        封装参数,千万不要替换为Map与HashMap,否则参数无法传递
//        MultiValueMap<String, Integer> params= new LinkedMultiValueMap<>();
//        params.add("pageIndex", 1);
//        params.add("pageSize", 5);
//        HttpEntity<MultiValueMap<String, Integer>> body = new HttpEntity<>(paranms,headers);

    HttpEntity body=null;
//exchange是一个基础的REST调用接口,需要指明HTTP Method,调用方法和其他方法类似
    ResponseEntity<List<LostVo>> rs=restTemplate.exchange(url, HttpMethod.GET,body,typeReference,LostVo.class);

    List<LostVo> users=rs.getBody();

    return users;
}

注:

  1. typeRef定义是用{}结束的,这里创建一个ParameterizedTypeReference子类,依据在类定义中的泛型信息保留的原则,typeRef保留了期望返回的泛型List。如果有参数,需要用HttpEntity进行封装。
  2. 除了使用ParameterizedTypeReference来保留泛型信息,也可以通过getForObject方法先映射成String,然后通过ObjectMapper转为指定类型
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容