Restfule风格接口

一、产生背景

网络应用程序,越来越流行前端和后端的分离设计。当前的发展趋势是前端的设计层出不穷。比如:各种型号的手机、平板灯其他设计。因为必须要一种统一的机制方便不同的前端和后端进行通信。这就导致了API结构的流行。其中Restful API是目前比较成熟的一套互联网应用程序的API设计理论。

二、简介

解释一

Restfule风格是一种软件架构风格,而不是标准,只是提供了一种设计原则和约束条件。主要适用于客户端和服务器端交互的软件。是基于http协议实现。目的是为了提高系统的可伸缩性降低应用之间的耦合度,方便框架分布式处理程序。基于这个风格的软件可更加的简单、更有层次,更易于实现缓存的机制。
在resultful风格中,用户请求的url使用同一个URL而用请求方式get/post/delete/put等方式对请求的处理方法进行区分。这样可以在前后台分离的开发中让前端开发人员不会对请求的资源地址产生混淆,形成一个统一的接口。

解释二:什么是RESTful风格

REST是REpresentational State Transfer的缩写(一般中文翻译为表述性状态转移),REST 是一种体系结构,而 HTTP 是一种包含了 REST 架构属性的协议,为了便于理解,我们把它的首字母拆分成不同的几个部分:

  • 表述性(REpresentational): REST 资源实际上可以用各种形式来进行表述,包括 XML、JSON 甚至 HTML——最适合资源使用者的任意形式;
  • 状态(State): 当使用 REST 的时候,我们更关注资源的状态而不是对资源采取的行为;
  • 转义(Transfer): REST 涉及到转移资源数据,它以某种表述性形式从一个应用转移到另一个应用。

简单地说,REST 就是将资源的状态以适合客户端或服务端的形式从服务端转移到客户端(或者反过来)。在 REST 中,资源通过 URL 进行识别和定位,然后通过行为(即 HTTP 方法)来定义 REST 来完成怎样的功能。

三、特点

  • 每种url代表了一种资源。
  • 客户端和服务器之间,传递这个资源的某种表现层。
  • 客户端通过四个http动词,对服务器资源进行操作。实现表现层状态的转化。

四、RestFul的url规范(各种请求方式)

对Student类的操作 统一的虚拟目录为 /students 如:

@Controller
@RequestMapping("/exams")
public class ExamAction {
}

添加一个

请求方式:POST

请求参数: json对象

请求url: /exams

删除一个

请求方式:DELETE

请求参数: 无

请求url: /exams/{id}

修改一个

请求方式:PUT

请求参数: json对象

请求url: /exams

获取一个

请求方式:GET

请求参数:无

请求url: /exams/{id}

获取所有

请求方式:GET

请求参数:无

请求url: /exams

条件查询

请求方式:GET

请求参数:xxx=xxx&xxx=xxx

请求url: /exams

HTTP 方法

使用场景

GET

对应select:是从服务器查询,可以在服务器通过请求的参数区分查询的方式

POST

对应Create:在服务器新建立一个资源,调用insert操作。

PUT

对应update操作:在服务器更新资源,调用update操作

PATCH

对应update操作,在服务器更新资源,客户端提供改变的属性。(JDK7没有实现,tomcat7也不行。)

DELETE

对应DELETE操作,从服务器删除资源,调用delete语句

五、实例说明

在平时的 Web 开发中,method 常用的值是 GET 和 POST,但是实际上,HTTP 方法还有 PATCH、DELETE、PUT 等其他值,这些方法又通常会匹配为如下的 CRUD 动作:

CRUD 动作

HTTP 方法

Create

POST

Read

GET

Update

PUT 或 PATCH

Delete

DELETE

这并不是严格的限制,有时候 PUT 也可以用来创建新的资源,POST 也可以用来更新资源。

controller代码示例

@RequestMapping(value = "/TestController",method = {RequestMethod.GET})
    public @ResponseBody
    ResponseEntity<String> get01(){

        return new ResponseEntity<String>(200,"Time:"+new Date().toLocaleString(),"GET");
    }

    @RequestMapping(value = "/TestController/{id}",method = {RequestMethod.GET})
    public @ResponseBody
    ResponseEntity<String> get02(@PathVariable("id")Integer id){

        return new ResponseEntity<String>(200,"学号:"+id,"GET");
    }
    @RequestMapping(value = "/TestController",method = {RequestMethod.PUT})
    public @ResponseBody
    ResponseEntity<String> get03(){

        ResponseEntity<String> put = new ResponseEntity<String>(200, "Time:"+new Date().toLocaleString(), "PUT");
        return put;
    }

    @RequestMapping(value = "/TestController",method = {RequestMethod.DELETE})
    public @ResponseBody
    ResponseEntity<String> get04(){

        ResponseEntity<String> put = new ResponseEntity<String>(200, "Time:"+new Date().toLocaleString(), "DELETE");
        return put;
    }
    @RequestMapping(value = "/TestController",method = {RequestMethod.POST})
    public @ResponseBody
    ResponseEntity<String> get05(){

        ResponseEntity<String> put = new ResponseEntity<String>(200, "Time:"+new Date().toLocaleString(), "POST");
        return put;
    }

另:Spring 4.3 之后,为了更好的支持 RESTful 风格,增加了几个注解:@PutMapping@GetMapping@DeleteMapping@PostMapping

-----------改造前-----------
@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";
}

六、让form表单可以发送类put,delete请求

浏览器使用form提交信息的时候只支持GET和POST,SpringMvc提供了HiddenHttpMethodFilter类来提供支持(并不是让form表单可以发送普通,delete请求,而是欺骗)

public class HiddenHttpMethodFilter extends OncePerRequestFilter {

    /** Default method parameter: {@code _method} */
    //我们的隐藏字段name必须为_method
    public static final String DEFAULT_METHOD_PARAM = "_method";

    private String methodParam = DEFAULT_METHOD_PARAM;


    /**
     * Set the parameter name to look for HTTP methods.
     * @see #DEFAULT_METHOD_PARAM
     */
    public void setMethodParam(String methodParam) {
        Assert.hasText(methodParam, "'methodParam' must not be empty");
        this.methodParam = methodParam;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        HttpServletRequest requestToUse = request;

        if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
            String paramValue = request.getParameter(this.methodParam);
            if (StringUtils.hasLength(paramValue)) {
                requestToUse = new HttpMethodRequestWrapper(request, paramValue);
            }
        }

        filterChain.doFilter(requestToUse, response);
    }


    /**
     * Simple {@link HttpServletRequest} wrapper that returns the supplied method for
     * {@link HttpServletRequest#getMethod()}.
     */
    private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {

        private final String method;

        public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
            super(request);
            this.method = method.toUpperCase(Locale.ENGLISH);
        }
        //通过继承方式对getMethod方法做了下改变,就变成了PUT或者DELETE了
        @Override
        public String getMethod() {
            return this.method;
        }
    }

}

配置步骤

  1. web.xml配置
<!-- HTTP PUT Form -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

2.form表单

<form method="POST" action="">
    	<!--设置一个隐藏属性,name必须为_method,value填写PUT和DELETE可以使用PUT和DELETE请求-->
        <input type="hidden" name="_method" value="PUT">
    
        <p>姓名:</p><input type="text" name="name" /><br/>
        <p>性别:</p><input type="text" name="sex" /><br/>
        <p>年龄:</p><input type="text" name="age" /><br/>
        <button type="submit">提交</button>
    </form>