采取RESTful风格创新JavaWeb版权声明

原创
小哥 3年前 (2022-10-28) 阅读数 102 #JAVA
文章标签 javajava教程

什么是RESTful风格?

REST是 REpresentational State Transfer 缩写(通常中文翻译为富有表现力的状态转移),REST 是一种架构,而 HTTP 是包含以下内容的类型 REST 协议的架构属性,为了便于理解,我们将其首字母分为不同的部分:

  • 表述性(REpresentational): REST 资源实际上可以用各种形式来表示,包括 XML、JSON 甚至 HTML--任何最适合资源使用者的形式;
  • 状态(State): 当使用 REST 当我们这样做时,我们更关心资源的状态,而不是资源的行为;
  • 转义(Transfer): REST 它涉及到传输资源数据,这些数据以某种表达形式从一个应用程序传输到另一个应用程序。

简单地说,REST 它是以适合于客户端或服务器的形式将资源的状态从服务器传输到客户端(反之亦然)。 在 REST 中,资源 通过 URL 识别和定位 ,然后通过。 行为(即 HTTP 方法) 来定义 REST 来完成什么样的功能。

实例说明:

在平时的 Web 开发中,method 常用的值有 GET 和 POST但在现实中,HTTP 方法还有 PATCH、DELETE、PUT 和其他值,则这些方法通常按如下方式匹配。 CRUD 动作:

CRUD 动作

HTTP 方法

Create

POST

Read

GET

Update

PUT 或 PATCH

Delete

DELETE

虽然总的来说,HTTP 方法已映射 CRUD 行动,但这不是一个严格的限制,有时 PUT 还可以用来创建新资源,POST 它还可用于更新资源。事实上,POST 请求 非幂等元的特征 (即同一个 URL 可以得到不同的结果)使它成为一种非常灵活的方法,因为它不能适应其他 HTTP 方法语义操作,它是胜任的。

在使用 RESTful 在时尚之前,如果我们想 添加商品数据 通常情况就是这样。:

/addCategory?name=xxx

但它的用途 RESTful 这样的风格就会变成:

/category

这就变成了 使用相同的 URL ,通过 约定的不同 HTTP 方法 为了实现不同的业务,这是 RESTful 风格做了什么,为了更直观地理解,引用how2j.cn的图:

SpringBoot 中使用 RESTful

下面是我用的。 SpringBoot 结合文章: http://blog.didispace.com/springbootrestfulapi/ 演示如何使用 SpringBoot 中使用 RESTful 风格编程以及如何进行单元测试。

RESTful API 具体设计如下:

User实体定义:

public class User { 

    private Long id; 
    private String name; 
    private Integer age; 

    // 省略setter和getter 

}

实现对User对象的操作界面

@RestController 
@RequestMapping(value="/users")     // 通过在此处配置,以下映射都在中。/users下 
public class UserController { 

    // 创建线程安全Map 
    static Map users = Collections.synchronizedMap(new HashMap()); 

    @RequestMapping(value="/", method=RequestMethod.GET) 
    public List getUserList() { 
        // 处理"/users/"的GET请求,用于获取用户列表。 
        // 也可以通过@RequestParam将参数从页面传递到查询条件或翻页信息。 
        List r = new ArrayList(users.values()); 
        return r; 
    } 

    @RequestMapping(value="/", method=RequestMethod.POST) 
    public String postUser(@ModelAttribute User user) { 
        // 处理"/users/"的POST请求,用于创建User 
        // 除了@ModelAttribute绑定参数之外,也可以通过@RequestParam从页面传递参数 
        users.put(user.getId(), user); 
        return "success"; 
    } 

    @RequestMapping(value="/{id}", method=RequestMethod.GET) 
    public User getUser(@PathVariable Long id) { 
        // 处理"/users/{id}"的GET请求,用于获取url中id值的User信息 
        // url中的id可通过@PathVariable绑定到函数的参数。 
        return users.get(id); 
    } 

    @RequestMapping(value="/{id}", method=RequestMethod.PUT) 
    public String putUser(@PathVariable Long id, @ModelAttribute User user) { 
        // 处理"/users/{id}"的PUT请求,用于更新User信息 
        User u = users.get(id); 
        u.setName(user.getName()); 
        u.setAge(user.getAge()); 
        users.put(id, u); 
        return "success"; 
    } 

    @RequestMapping(value="/{id}", method=RequestMethod.DELETE) 
    public String deleteUser(@PathVariable Long id) { 
        // 处理"/users/{id}"的DELETE请求,用于删除User 
        users.remove(id); 
        return "success"; 
    } 

}

写作测试单元

参考文章: http://tengj.top/2017/12/28/springboot12/#Controller 单元测试
看完这些文章,感觉棒极了,还有这么便捷的检测方法,以前从未碰过。...

以下是关于这一点的Controller编写测试用例以验证正确性,如下所示。当然,您也可以通过浏览器插件等方式请求提交验证,因为这涉及到一些包的导入,所有代码如下所示:

package cn.wmyskxz.springboot;

import cn.wmyskxz.springboot.controller.UserController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
 * @author: @我没有三颗心
 * @create: 2018-05-29-上午 8:39
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = MockServletContext.class)
@WebAppConfiguration
public class ApplicationTests {

    private MockMvc mvc;

    @Before
    public void setUp() throws Exception {
        mvc = MockMvcBuilders.standaloneSetup(new UserController()).build();
    }

    @Test
    public void testUserController() throws Exception {
        // 测试UserController
        RequestBuilder request = null;

        // 1、get查一下user列表,应为空
        request = get("/users/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[]")));

        // 2、post提交一个user
        request = post("/users/")
                .param("id", "1")
                .param("name", "测试大师")
                .param("age", "20");
        mvc.perform(request)
                .andExpect(content().string(equalTo("success")));

        // 3、get获取user列表中,应该有刚刚插入的数据
        request = get("/users/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[{"id":1,"name":"测试大师","age":20}]")));

        // 4、put修改id为1的user
        request = put("/users/1")
                .param("name", "测试终极大师")
                .param("age", "30");
        mvc.perform(request)
                .andExpect(content().string(equalTo("success")));

        // 5、get一个id为1的user
        request = get("/users/1");
        mvc.perform(request)
                .andExpect(content().string(equalTo("{"id":1,"name":"测试终极大师","age":30}")));

        // 6、del删除id为1的user
        request = delete("/users/1");
        mvc.perform(request)
                .andExpect(content().string(equalTo("success")));

        // 7、get查一下user列表,应为空
        request = get("/users/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[]")));

    }

}

MockMvc实现了对HTTP从该示例的代码中可以看到对请求的模拟。MockMvc使用简单,可以直接转换成网络的形式。Controller这使得测试快速、独立于网络环境,并提供了一套验证工具,可以使请求验证统一、方便。

值得注意的是,在MockMvc在使用之前,您需要先使用它。MockMvcBuilders构建MockMvc对象,如果您对单元测试感兴趣,请点击上面的链接,所以我不会在这里详细介绍。

测试信息

运行测试类时,控制台返回以下信息:

 __      __                               __
/   __/                              / 
  /       ___ ___   __  __    ____  /    __  _  ____
        / __ __/ /   /,__\  , <   / //\_ ,`
    \_/ \_ / / /   \_ /\__, \   \/>  \___x___/ \_ \_ \_/____ /\____/  \_ \_/\_/\_ /\____
    /__//__/  /_//_//_/`/___/> /___/   /_//_////_/ /____/
                               /\___/
                               /__/
2018-05-29 09:28:18.730  INFO 5884 --- [           main] cn.wmyskxz.springboot.ApplicationTests   : Starting ApplicationTests on SC-201803262103 with PID 5884 (started by Administrator in E:Java Projectsspringboot)
2018-05-29 09:28:18.735  INFO 5884 --- [           main] cn.wmyskxz.springboot.ApplicationTests   : No active profile set, falling back to default profiles: default
2018-05-29 09:28:18.831  INFO 5884 --- [           main] o.s.w.c.s.GenericWebApplicationContext   : Refreshing org.springframework.web.context.support.GenericWebApplicationContext@7c37508a: startup date [Tue May 29 09:28:18 CST 2018]; root of context hierarchy
2018-05-29 09:28:19.200  INFO 5884 --- [           main] cn.wmyskxz.springboot.ApplicationTests   : Started ApplicationTests in 1.184 seconds (JVM running for 2.413)
2018-05-29 09:28:19.798  INFO 5884 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/users/{id}],methods=[PUT]}" onto public java.lang.String cn.wmyskxz.springboot.controller.UserController.putUser(java.lang.Long,cn.wmyskxz.springboot.pojo.User)
2018-05-29 09:28:19.800  INFO 5884 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/users/],methods=[GET]}" onto public java.util.List cn.wmyskxz.springboot.controller.UserController.getUserList()
2018-05-29 09:28:19.800  INFO 5884 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/users/],methods=[POST]}" onto public java.lang.String cn.wmyskxz.springboot.controller.UserController.postUser(cn.wmyskxz.springboot.pojo.User)
2018-05-29 09:28:19.801  INFO 5884 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/users/{id}],methods=[DELETE]}" onto public java.lang.String cn.wmyskxz.springboot.controller.UserController.deleteUser(java.lang.Long)
2018-05-29 09:28:19.801  INFO 5884 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/users/{id}],methods=[GET]}" onto public cn.wmyskxz.springboot.pojo.User cn.wmyskxz.springboot.controller.UserController.getUser(java.lang.Long)
2018-05-29 09:28:19.850  INFO 5884 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.test.web.servlet.setup.StubWebApplicationContext@42f8285e
2018-05-29 09:28:19.924  INFO 5884 --- [           main] o.s.mock.web.MockServletContext          : Initializing Spring FrameworkServlet 
2018-05-29 09:28:19.925  INFO 5884 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : FrameworkServlet : initialization started
2018-05-29 09:28:19.926  INFO 5884 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : FrameworkServlet : initialization completed in 1 ms

通过控制台的信息,我们知道通过。 RESTful 样式可以成功地调用正确的方法,并且没有任何错误地获取或返回正确的参数,那么它就成功了!

如果你想了解更多细节,你可以每次都打电话。 perform() 在方法和上一句话之后 .andDo(MockMvcResultHandlers.print()) ,例如:

        // 1、get查一下user列表,应为空
        request = get("/users/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[]")))
                .andDo(MockMvcResultHandlers.print());

您可以查看详细信息,如下所示:

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /users/
       Parameters = {}
          Headers = {}
             Body = 
    Session Attrs = {}

Handler:
             Type = cn.wmyskxz.springboot.controller.UserController
           Method = public java.util.List cn.wmyskxz.springboot.controller.UserController.getUserList()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = {Content-Type=[application/json;charset=UTF-8]}
     Content type = application/json;charset=UTF-8
             Body = []
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

总结

我们仍在使用 @RequestMapping 注释,但不同之处在于我们指定 method 属性来处理不同的 HTTP 方法,以及 @PathVariable 注解来将 HTTP 请求中的属性被绑定到我们指定的形式参数。

事实上,Spring 4.3 在那之后,为了更好的支持 RESTful Style,添加了几条评论: @PutMapping@GetMapping@DeleteMapping@PostMapping ,从名字上也大致可以看出,其实会 method 该属性的值与 @RequestMapping 它只是有约束力的,例如,我们在一起。UserController中的deleteUser方法转换:

-----------改造前-----------
@RequestMapping(value="/{id}", method=RequestMethod.DELETE)
public String deleteUser(@PathVariable Long id) {
    // 处理"/users/{id}"的DELETE请求,用于删除User
    users.remove(id);
    return "success";
}

-----------改造后-----------
@DeleteMapping("/{id}")
public String deleteUser(@PathVariable Long id) {
    // 处理"/users/{id}"的DELETE请求,用于删除User
    users.remove(id);
    return "success";
}

使用Swagger2构造RESTful API文档

参考文章: http://blog.didispace.com/springbootswagger2/

RESTful Style为背景和前景之间的交互提供了一个简洁的界面。API并有助于降低与其他团队沟通的成本,通常我们会创建一个RESTful API记录所有接口详细信息的文档,但这样做有几个问题:

  1. 由于界面众多,细节复杂(不同HTTP请求类型,HTTP标题信息,HTTP请求内容等), 创建高质量的这份文件本身就是一项非常困难的任务。 ,下游的抱怨也被听到了。
  2. 随着时间的推移,当不断修改接口实现时,必须同时修改接口文档,并且文档和代码在两种不同的介质中,除非有严格的管理机制,否则 这很容易导致不一致。

Swagger2上述问题的出现就是为了解决这些问题,并可以很容易地融入我们的SpringBoot过去,它不仅可以减少创建文档的工作量,还可以将内容集成到代码中,使维护文档和修改代码融为一体,可以让我们在修改代码逻辑的同时,方便地修改文档描述。这太酷了。此外 Swagger2页面提供了强大的页面测试功能来调试每个页面RESTful API ,具体效果如下:

让我们快速地看一看:

第一步:添加Swagger2依赖:

pom.xml 中加入Swagger2的依赖:


    io.springfox
    springfox-swagger2
    2.2.2


    io.springfox
    springfox-swagger-ui
    2.2.2

第2步:创建Swagger2配置类

在SpringBoot在启动类的同级目录下创建Swagger2的配置类 Swagger2

@Configuration
@EnableSwagger2
public class Swagger2 {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("cn.wmyskxz.springboot"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Spring Boot中使用Swagger2构建RESTful APIs")
                .description("原始地址链接:http://blog.didispace.com/springbootswagger2/")
                .termsOfServiceUrl("http://blog.didispace.com/")
                .contact("@我没有三颗心")
                .version("1.0")
                .build();
    }

}

如上面的代码所示, @Configuration 注解让Spring加载配置类,然后传递。 @EnableSwagger2 要开始的注释Swagger2;

再通过 createRestApi 函数创建 Docket 的Bean之后, apiInfo() 用来创建这个API基本信息(此基本信息显示在文档页面上), select() 函数返回一个 ApiSelectorBuilder 实例用于控制向哪些接口公开Swagger为了说明这一点,此示例使用。 指定扫描的包路径 来定义,Swagger将扫描所有Controller定义的API并生成文档内容(除了 @ApiIgnore 指定请求)

第三步:添加文档内容

在上述配置完成后,已经可以生成文档内容,但这样的文档主要是针对请求本身的,描述主要来自功能的命名等,对用户不友好。我们通常需要添加一些说明来丰富文档内容。如下所示,我们通过 @ApiOperation 注解来给API添加说明,至 @ApiImplicitParams@ApiImplicitParam 用于向参数添加说明的注释。

@RestController
@RequestMapping(value="/users")     // 通过在此处配置,以下映射都在中。/users下,可取下
public class UserController {

    static Map users = Collections.synchronizedMap(new HashMap());

    @ApiOperation(value="获取用户列表", notes="")
    @RequestMapping(value={""}, method=RequestMethod.GET)
    public List getUserList() {
        List r = new ArrayList(users.values());
        return r;
    }

    @ApiOperation(value="创建用户", notes="根据User对象创建用户")
    @ApiImplicitParam(name = "user", value = "用户详细信息实体user", required = true, dataType = "User")
    @RequestMapping(value="", method=RequestMethod.POST)
    public String postUser(@RequestBody User user) {
        users.put(user.getId(), user);
        return "success";
    }

    @ApiOperation(value="获取用户详细信息", notes="根据url的id获取用户详细信息")
    @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long")
    @RequestMapping(value="/{id}", method=RequestMethod.GET)
    public User getUser(@PathVariable Long id) {
        return users.get(id);
    }

    @ApiOperation(value="更新用户详细信息", notes="根据url的id指定更新对象,并基于传递的user更新用户详细信息的信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long"),
            @ApiImplicitParam(name = "user", value = "用户详细信息实体user", required = true, dataType = "User")
    })
    @RequestMapping(value="/{id}", method=RequestMethod.PUT)
    public String putUser(@PathVariable Long id, @RequestBody User user) {
        User u = users.get(id);
        u.setName(user.getName());
        u.setAge(user.getAge());
        users.put(id, u);
        return "success";
    }

    @ApiOperation(value="删除用户", notes="根据url的id要指定删除对象,请执行以下操作。")
    @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long")
    @RequestMapping(value="/{id}", method=RequestMethod.DELETE)
    public String deleteUser(@PathVariable Long id) {
        users.remove(id);
        return "success";
    }

}

完成上述代码添加后,启动Spring Boot计划,访问: http://localhost:8080/swagger-ui.html ,你可以看到之前的节目RESTful API我们可以点击特定的页面API请求,POST类型的/users例如,您可以找到上面配置的代码。Notes信息和参数user描述信息如下图所示:

API文档访问和调试

在上面请求的页面中,我们可以看到一个Value在输入框的右侧。Model Schema有一些例子User对象模板,我们单击右侧的黄色区域。Value该示例的模板数据将自动填写到框中。我们可以对其稍作修改,然后单击下面 “Try it out!” 按钮来完成请求调用,这太酷了。

总结

此前,这种比较已被记录在案。RESTful API如此一来,在原有代码的基础上,添加少量配置内容,入侵容差范围内的代码,就可以达到这样便捷直观的效果,可以说是使用了。Swagger2来对API文档管理是一个很好的选择!

欢迎转载,转载请注明出处!
简书ID: @我没有三颗心
github: wmyskxz
欢迎关注公共微信号:wmyskxz_javaweb
分享你自己的Java Web学习之路和各种Java学习资料

作者:我没有三颗心
链接:https://www.jianshu.com/p/91600da4df95
资料来源:简讯
版权归作者所有。请联系作者以获得商业转载的授权,并注明非商业转载的来源。

版权声明

所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除

热门