潘景

使用Spring MVC开发RESTful API - 1
一.Restful简介在介绍Restful之前,先让我们看一张图:上图左边使用的是传统下的API接口,右边使用的是...
扫描右侧二维码阅读全文
22
2019/03

使用Spring MVC开发RESTful API - 1

一.Restful简介

在介绍Restful之前,先让我们看一张图:

上图左边使用的是传统下的API接口,右边使用的是Restful API接口。从图中可以看出,传统请求用URL描述行为,而Restful API用URL描述资源。
Restful API还有以下几个特点:

  • 使用HTTP方法描述行为。使用HTTP状态码来表述不同的结果。
  • 使用json交互数据。
  • Restful只是一种风格,并不是强制的标准。

说了这么多,那么什么是Restful API呢?

简单的说:Restful是一种架构的规范与约束、原则,符合这种规范的架构就是RESTful架构。

REST成熟度模型

REST成熟度模型是通往真正REST的步骤,如下图所示。

  • Level0:使用Http作为传输方式。
  • Level1:引入资源概念。每个资源都有对应的URL。
  • Level2:使用HTTP方法进行不同的操作。使用HTTP状态码来表示不同的结果。
  • Level3:使用超媒体。在资源的表达中包含了链接信息。

注:一般情况下,开发中达到Level2级别即可。

二.编写第一个Restful API

以下图为例,编写我们的第一个Restful API。

基于SpringBoot1.5.9.RELEASE进行开发。代码已经托管到github:使用Spring MVC开发RESTful API

1.用户查询服务

(1).编写测试用例

在正式编写代码之前,我们需要先编写测试用例,这是一个好的习惯。

此处也可以不编写测试用例,而改用Restlet Client或Postman等软件进行测试。

首先编写用户实体类。

public class User {
    private String id;
    private String username;
    private String password;
    private Date birthday;
    //相应的get/set方法已省略
}

然后是查询的测试用例。
注:代码已托管到github上,故以下均只列出主要代码片段。

@Test
public void whenQuerySuccess() throws Exception {
    //模拟浏览器,发出get请求,请求地址为/user,携带参数...,内容类型为JSON格式,期望返回的状态码是200(即OK),期望返回的json数据长度为3。
    String result = mockMvc.perform(get("/user")
                    .param("username", "panjing")
                    .param("password", "123456")
                    .param("size", "15")
                    .param("page", "3")
                    .param("sort", "age,desc")
                    .contentType(MediaType.APPLICATION_JSON_UTF8))
             .andExpect(status().isOk()).andExpect(jsonPath("$.length()").value(3))
                .andReturn().getResponse().getContentAsString();
        System.out.println(result);
    }

(2).使用注解声明RestfulAPI

编写完测试用例后,我们开始编写控制器,coding之前,需要先了解几个重要的注解。

  • @RestController 标明此Controller提供RestAPI
  • @RequestMapping及其变体。映射http请求url到java方法
  • @RequestParam 映射请求参数到java方法的参数。
  • @PageableDefault 指定分页参数默认值
@RestController
@RequestMapping("/user")
public class UserController {
    /**
     * 查询所有用户
     * @param user
     * @param pageable
     * @return
     */
    @GetMapping //RequestMapping的变体,表示发出的是get请求
    public List<User> query(User user,
                            @PageableDefault(page = 2, size = 17, sort = "username,asc") Pageable pageable) {
        //由于测试用例中指定的集合长度为3,所以这里添加3个用户
        List<User> users = new ArrayList<>();
        users.add(user);
        users.add(user);
        users.add(user);
        return users;
    }
}

运行测试用例,如果输出以下结果则表示编码成功:

[{"username":"panjing","birthday":null},{"username":"panjing","birthday":null},{"username":"panjing","birthday":null}]

2.用户详情服务

(1).编写测试用例

查询用户详情的测试用例与查询所有用户的测试用例相似。在此不再赘述,详细代码可参考github。

(2).在URL声明中使用正则表达式

一般的情况下,我们都是根据用户的id来查询用户详情,而id一般情况下为整数,故我们需要用正则表达式限制传入参数的格式。
编码之前,需要了解@PathVariable的用法。

  • @PathVariable 映射url片段到Java方法的参数
//此处URL片段中的id将会被作为参数传递到getInfo方法中
@GetMapping("/{id:\\d+}")
public User getInfo(@PathVariable String id) {
    User user = new User();
    user.setUsername("panjing");
    return user;
}

(3).使用@JsonView控制Json的输出内容

试想一下,如果任何请求都返回所有Json数据,必将造成用户账号密码泄露的危险,而使用@JsonView则可以通过控制JSON的输出内容从而提高安全性。
使用@JsonView大概分为以下三步:

  1. 在实体类中使用接口来声明多个视图
public class User {
    public interface UserSimpleView{};
    public interface UserDetailView extends UserSimpleView{};
}
  1. 在属性的get方法上指定视图
 @JsonView(UserSimpleView.class)
public String getUsername() {return username;}
@JsonView(UserDetailView.class)
public String getPassword() {return password;}
@JsonView(UserSimpleView.class)
public Date getBirthday() {return birthday;}
@JsonView(UserSimpleView.class)
public String getId() {return id;}
  1. 在Controller方法上指定视图
@GetMapping
@JsonView(User.UserSimpleView.class)
public List<User> query(...){...}

@GetMapping("/{id:\\d+}")
@JsonView(User.UserDetailView.class)
public User getInfo(...) {}

3. 用户创建服务

(1).@RequestBody

用户创建的代码较为简单,但需要注意一个坑,那就是@RequestBody的使用。

  • @RequestBody映射请求体到java方法的参数
@PostMapping
//@RequestBody可以解析传过来的Json数据,如果不加的话,user的各个属性都将是null
public User create(@RequestBody User user) {
    user.setId("1");
    return user;
}

(2).验证请求参数的合法性并处理校验结果

如果想要验证请求参数的合法性以及处理校验结果,需要以下几步:

  1. 在实体类的属性上添加注解
public class User {
    ...
    @NotBlank(message = "密码不能为空")
    private String password;
    @Past(message = "生日必须是过去的时间")
    private Date birthday;
    ...
}
  1. 使用@Valid注解和BindingResult进行验证及处理
 @PostMapping
 //错误信息会放在BindingResult中
public User create(@Valid @RequestBody User user, BindingResult errors) {
    if(errors.hasErrors()){
        errors.getAllErrors().stream().forEach(error -> System.out.println(error.getDefaultMessage()));
    }
    user.setId("1");
    return user;
}

(3).常用的验证注解


4.用户信息修改和删除服务

用户信息修改和删除服务与上述操作流程相同。

@PutMapping("/{id:\\d+}")
public User update(@Valid @RequestBody User user, BindingResult errors) {
    if(errors.hasErrors()){
        errors.getAllErrors().stream().forEach(error -> System.out.println(error.getDefaultMessage()));
    }
    user.setId("1");
    return user;
}
@DeleteMapping("/{id:\\d+}")
public void delete(@PathVariable String id) {
    System.out.println(id);
}
Last modification:March 23rd, 2019 at 07:15 pm
If you think my article is useful to you, please feel free to appreciate

Leave a Comment