RestFul

229 阅读3分钟

1、RESTFul简介

REST:Representational State Transfer,表现层资源状态转移;

(1)云里雾里的描述

  • REST 指的是 一组架构约束条件和原则,如果一个架构符合 REST 的约束条件和原则,就称之为 RESTful 架构。
  • RESTful 是一种软件架构风格,而不是标准。

(2)知乎大神的总结

用 URL 定位资源,用 HTTP 动词(GET,POST,DELETE,PUT)描述操作。

RESTful 是一种 web 服务设计风格,风格意思就是大家默认的但不是强制的。

2、RESTFul的实现

具体说来,就是Http协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。

它们分别表示四种基本操作:GET用来获取资源、POST用来新建资源、PUT用来更新资源、DELETE用来删除资源。

REST风格倡导URL地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对的方式携带请求参数,而是将要发送给服务器的数据作为URL地址的一部分,以保证整体风格的一致性。

image.png

3、HiddenHttpMethodFilter

由于浏览器只支持发送get和post的请求,那么该如何发送put和delete请求呢?

SpringMVC提供了HiddenHttpMethodFilter来将get和post转换为put和delete请求

4、HiddenHttpMethodFilter源码剖析

    private static final List<String> ALLOWED_METHODS;
    public static final String DEFAULT_METHOD_PARAM = "_method";
    private String methodParam = "_method";

    public HiddenHttpMethodFilter() {
    }

    public void setMethodParam(String methodParam) {
        Assert.hasText(methodParam, "'methodParam' must not be empty");
        this.methodParam = methodParam;
    }

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        HttpServletRequest requestToUse = request;
        if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
            String paramValue = request.getParameter(this.methodParam);
            if (StringUtils.hasLength(paramValue)) {
                String method = paramValue.toUpperCase(Locale.ENGLISH);
                if (ALLOWED_METHODS.contains(method)) {
                    requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
                }
            }
        }

        filterChain.doFilter((ServletRequest)requestToUse, response);
    }

    static {
        ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
    }

    private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
        private final String method;

        public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
            super(request);
            this.method = method;
        }

        public String getMethod() {
            return this.method;
        }
    }
}

(1)我们首先要知道它是一个Filter,与CharacterEncodingFilter一样继承了OncePerFilter,所以我们最重要的就是关注的它的doFilterInternal方法
(2)我们首先关注第二行的HttpServletRequest requestToUse = request;新建requestToUse是为了替换原有的request做准备。
(3)条件if("POST".equals(request.getMethod()) &&request.getAttribute("javax.servlet.error.exception") == null)进入条件是方法必须为"post" ,后面一个条件为无异常。
(4)获取请求方法:String paramValue = request.getParameter(this.methodParam);通过此行代码获取到请求的方式,如:PUT、DELETE、PATCH。方式是通过自己set方法设置:public void setMethodParam(String methodParam) { Assert.hasText(methodParam, "'methodParam' must not be empty"); this.methodParam = methodParam; }
(5)替换请求方法

     String method = paramValue.toUpperCase(Locale.ENGLISH);
     if (ALLOWED_METHODS.contains(method)) {
         requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
     }
}

5、RESTFul案例

1、利用web.xml文件配置两个过滤器和DispatcherServlet

<filter>
    <filter-name>Encoding</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>Encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>Encoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>Hidden</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>Hidden</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<servlet>
    <servlet-name>Dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springMVC.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>Dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

注:CharacterEncodingFilter一定要放在最前面,因为当参数取出来的时候,该过滤器就失效了
2、配置springMVC的配置文件,添加视图解析器等

<context:component-scan base-package="Controller,Dao,Pojo"></context:component-scan>

    <bean id="Resolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
<!--    视图控制器,当没有操作的时候可以直接配置-->
    <mvc:view-controller path="/" view-name="index"></mvc:view-controller>
<!--    防止其他的视图访问失效-->
    <mvc:annotation-driven/>

<!--    当访问静态资源的时候,走dispatherservlet无法访问,就走默认的servlet-->
    <mvc:default-servlet-handler/>

3、编写model层,即dao和pojo
pojo类:

public class User {
    private int id;
    private String name;
    private String sex;
    private int age;
    private String email;

    public User(int id, String name, String sex, int age, String email) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.email = email;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

}

dao类:

@Repository
public class UserDao {
    private static Map<Integer, User> Users = null;
    static {
        Users = new HashMap<Integer,User>();
        Users.put(1001,new User(1001,"liuwei","男",25,"1084227037@qq.com"));
        Users.put(1002,new User(1002,"liuwei1","男",25,"123@qq.com"));
        Users.put(1003,new User(1003,"liuwei2","女",25,"4556@qq.com"));
        Users.put(1004,new User(1004,"liuwei3","男",25,"dfsgtert@qq.com"));
    }
    private static Integer id = 1005;

    public Collection<User> getAllUsers() {
       return Users.values();
    }

    public void save(User user){
        if((Integer)user.getId() == null){
            user.setId(id++);
        }
        Users.put(user.getId(),user);
    }

    public User getUserById(Integer id) {
        return Users.get(id);
    }


    public void DeleteById(Integer id){
        Users.remove(id);
    }
}

4、Controller文件

public class HelloController {

    @Autowired
    private UserDao userDao;

    @GetMapping("/queryable")
    public String getAllUsers(Model model) {
        Collection<User> users= userDao.getAllUsers();
        model.addAttribute("msg","hello");
        model.addAttribute("users",users);
        return "list";
    }
    @RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)
    public String DeleteUserById(@PathVariable("id") int id){
        userDao.DeleteById(id);
        return "redirect:/queryable";
    }
}

5、jsp文件

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: liuwei
  Date: 2022/4/12
  Time: 11:16
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>用户列表</title>
</head>
<body>
    <table id="dataTable" border="1" cellspacing="0" cellpadding="0" style="text-align:center">
        <tr>
            <th>id</th>
            <th>name</th>
            <th>sex</th>
            <th>age</th>
            <th>email</th>
        </tr>
        <c:forEach var="u" items="${users}" varStatus="idx">
            <% System.out.println( "u.name:" +
                    pageContext.findAttribute("u") ); %>
            <tr>
                <td>${u.id}</td>
                <td>${u.name}</td>
                <td>${u.sex}</td>
                <td>${u.age}</td>
                <td>${u.email}</td>
                <td>
                    <a href="${pageContext.request.contextPath}/user/${u.id}" @click="deleteUser">delete</a>
                </td>
            </tr>
        </c:forEach>
    </table>

    <form method="post" id="deleteForm">
        <input type="hidden" name="_method" value="delete">
    </form>

    <script type="text/javascript" src="${pageContext.request.contextPath}/static/vue.js"></script>
    <script type="text/javascript">
        var vue = new Vue({
            el:"#dataTable",
            methods:{
                deleteUser:function (event){
                    var deleteForm = document.getElementById("deleteForm");
                    deleteForm.action = event.target.href;
                    deleteForm.submit();
                    //取消超链接的默认行为
                    event.preventDefault();
                }
            }
        });
    </script>
</body>
</html>