SpringMVC: First

张天宇 on 2020-04-11

SpringMVC第一篇,简单工程的创建。

三层架构

表现层

也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web 层,web 需要接收 http 请求,完成 http 响应。表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。表现层的设计一般都使用 MVC 模型。(MVC 是表现层的设计模型,和其他层没有关系)

业务层

也就是我们常说的 service 层。它负责业务逻辑处理,和我们开发项目的需求息息相关。web 层依赖业务层,但是业务层不依赖 web 层。业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务一致性。(也就是我们说的,事务应该放到业务层来控制)

持久层

也就是我们是常说的 dao 层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中。通俗的讲,持久层就是和数据库交互,对数据库表进行曾删改查的。

SpringMVC

基于Java的实现MVC设计模型的请求驱动类型的轻量级Web框架。

优势

  1. 清晰的角色划分:
    前端控制器(DispatcherServlet)
    请求到处理器映射(HandlerMapping)
    处理器适配器(HandlerAdapter)
    视图解析器(ViewResolver)
    处理器或页面控制器(Controller)
    验证器( Validator)
    命令对象(Command 请求参数绑定到的对象就叫命令对象)
    表单对象(Form Object 提供给表单展示和提交到的对象就叫表单对象)。

  2. 分工明确,而且扩展点相当灵活,可以很容易扩展,虽然几乎不需要。

  3. 由于命令对象就是一个 POJO,无需继承框架特定 API,可以使用命令对象直接作为业务对象。

  4. 和 Spring 其他框架无缝集成,是其它 Web 框架所不具备的。

  5. 可适配,通过 HandlerAdapter 可以支持任意的类作为处理器。

  6. 可定制性,HandlerMapping、ViewResolver 等能够非常简单的定制。

  7. 功能强大的数据验证、格式化、绑定机制。

  8. 利用 Spring 提供的 Mock 对象能够非常简单的进行 Web 层单元测试。

  9. 本地化、主题的解析的支持,使我们更容易进行国际化和主题的切换。

  10. 强大的 JSP 标签库,使 JSP 编写更容易。

    ………………还有比如RESTful风格的支持、简单的文件上传、约定大于配置的契约式编程支持、基于注解的零配
    置支持等等。

和Structs2的比较

  • 共同点:
    它们都是表现层框架,都是基于 MVC 模型编写的。
    它们的底层都离不开原始 ServletAPI。
    它们处理请求的机制都是一个核心控制器。
  • 区别:
    Spring MVC 的入口是 Servlet, 而 Struts2 是 Filter
    Spring MVC 是基于方法设计的,而 Struts2 是基于类,Struts2 每次执行都会创建一个动作类。所
    以 Spring MVC 会稍微比 Struts2 快些。
    Spring MVC 使用更加简洁,同时还支持 JSR303, 处理 ajax 的请求更方便
    (JSR303 是一套 JavaBean 参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注
    解加在我们 JavaBean 的属性上面,就可以在需要校验的时候进行校验了。)
    Struts2 的 OGNL 表达式使页面的开发效率相比 Spring MVC 更高些,但执行效率并没有比 JSTL 提
    升,尤其是 struts2 的表单标签,远没有 html 执行效率高。

简单工程的创建

  1. 导入配置坐标

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    <!-- 版本锁定:指定Spring版本 -->
    <properties>
    <spring.version>5.0.2.RELEASE</spring.version>
    </properties>

    <!-- 依赖坐标 -->
    <dependencies>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>${spring.version}</version>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    <scope>provided</scope>
    </dependency>
    <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.0</version>
    <scope>provided</scope>
    </dependency>
    </dependencies>
  2. 核心控制器基本配置web.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd" >

    <web-app>
    <display-name>Archetype Created Web Application</display-name>
    <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>
    </web-app>
  3. 配置springmvc.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 配置spring创建容器时要扫描的包 -->
    <context:component-scan base-package="com.itheima"></context:component-scan>
    <!-- 配置视图解析器 -->
    <bean id="viewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/pages/"></property>
    <property name="suffix" value=".jsp"></property>
    </bean>
    <!-- 配置spring开启注解mvc的支持
    <mvc:annotation-driven></mvc:annotation-driven>-->
    </beans>
  4. 其他

    1
    2
    3
    4
    <body>
    <h3>入门案例</h3>
    <a href="hello">入门案例</a>
    </body>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Controller
    public class HelloController {
    /**
    * 接收请求
    * @return
    */
    @RequestMapping(path="/hello")
    public String sayHello() {
    System.out.println("Hello SpringMVC!!");
    return "success";
    }
    }
  5. 在WEB-INF目录下创建pages文件夹,编写success.jsp的成功页面

执行过程分析

  1. 入门案例的执行流程
  2. 当启动Tomcat服务器的时候,因为配置了load-on-startup标签,所以会创建DispatcherServlet对象,
    就会加载springmvc.xml配置文件
  3. 开启了注解扫描,那么HelloController对象就会被创建
  4. 从index.jsp发送请求,请求会先到达DispatcherServlet核心控制器,根据配置@RequestMapping注解
    找到执行的具体方法
  5. 根据执行方法的返回值,再根据配置的视图解析器,去指定的目录下查找指定名称的JSP文件
  6. Tomcat服务器渲染页面,做出响应

RequestMapping注解

  1. RequestMapping注解的作用是建立请求URL和处理方法之间的对应关系
  2. RequestMapping注解可以作用在方法和类上
    1. 作用在类上:第一级的访问目录
    2. 作用在方法上:第二级的访问目录
    3. 细节:路径可以不编写 / 表示应用的根目录开始
    4. 细节:${ pageContext.request.contextPath }也可以省略不写,但是路径上不能写 /
  3. RequestMapping的属性
    1. path 指定请求路径的url
    2. value value属性和path属性是一样的
    3. method 指定该方法的请求方式
    4. params 指定限制请求参数的条件
    5. headers 发送的请求中必须包含的请求头

请求参数的绑定

支持数据类型

  • 基本类型参数 :
    包括基本类型和 String 类型

  • POJO 类型参数 :
    包括实体类,以及关联的实体类

  • 数组和集合类型参数 :
    包括 List 结构和 Map 结构的集合(包括数组)

SpringMVC 绑定请求参数是自动实现的,但是要想使用,必须遵循使用要求。

基本类型的绑定

1
<a href="param/testParam?username=hehe">请求参数绑定</a>
1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/testParam")
public String testParam(String username){
System.out.println("执行了"+username);
return "success";
}
}

实体类的绑定

1
2
3
4
5
6
7
8
9
<!-- Account属性中包含User类 -->
<form action="param/saveAccount" method="post">
姓名:<input type="text" name="username"/><br>
密码:<input type="text" name="password"/><br>
金额:<input type="text" name="money"/><br>
用户姓名:<input type="text" name="user.uname"/><br>
用户年龄:<input type="text" name="user.age"/><br>
<input type="submit" value="提交"/><br>
</form>
1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/saveAccount")
public String savaAccount(Account account){
System.out.println("执行了"+account.getUsername()+account.getUser().getUname());
return "success";
}
}

集合的绑定

1
2
3
4
5
6
7
8
9
10
11
12
<form action="param/saveAccount" method="post">
姓名:<input type="text" name="username"/><br>
密码:<input type="text" name="password"/><br>
金额:<input type="text" name="money"/><br>

用户姓名:<input type="text" name="list[0].uname"/><br>
用户年龄:<input type="text" name="list[0].age"/><br>

用户姓名:<input type="text" name="map['one'].uname"/><br>
用户年龄:<input type="text" name="map['one'].age"/><br>
<input type="submit" value="提交"/><br>
</form>
1
2
3
4
5
6
7
8
public class Account implements Serializable {
private String username;
private String password;
private Double money;
private List<User> list;
private Map<String, User> map;
//...
}
1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/saveAccount")
public String savaAccount(Account account){
System.out.println("执行了"+account.getUsername()+" "+account.getList().get(0).getUname()+" "+account.getMap().get("one"));
return "success";
}
}

自定义类型转换器

表单提交的任何数据类型全部都是字符串类型,但是后台定义Integer类型,数据也可以封装上,说明Spring框架内部会默认进行数据类型转换。如果想自定义数据类型转换,可以实现Converter的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
if (source == null){
throw new RuntimeException("传入数据为空");
}
SimpleDateFormat df =new SimpleDateFormat("yyyy-MM-dd");
try {
return df.parse(source);
} catch (Exception e) {
throw new RuntimeException("转换失败");
}
}
}
1
2
3
4
5
6
7
8
9
10
<!-- springmvc.xml主配置文件中配置自定义类型转换器 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters" >
<set>
<bean class="top.tyzhang.utils.StringToDateConverter"></bean>
</set>
</property>
</bean>
<!-- 启用 -->
<mvc:annotation-driven conversion-service="conversionService"/>

补充

Post请求中文乱码问题

1
request.setCharaterEncoding("UTF-8");

SpringMVC中的过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--解决中文乱码过滤器,写在servlet注释前-->
<filter>
<filter-name>characterEncodingFilter</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>
<!-- 启动过滤器 -->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
1
2
3
4
5
在 springmvc 的配置文件中可以配置,静态资源不过滤:
<!-- location 表示路径,mapping 表示文件,**表示该目录下的文件以及子目录的文件 -->
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/images/" mapping="/images/**"/>
<mvc:resources location="/scripts/" mapping="/javascript/**"/>

Get请求方式

Tomcat 对 GET和 POST 请求处理方式是不同的,GET请求的编码问题,要改 tomcat 的 server.xml
配置文件,如下:

1
2
3
<Connector connectionTimeout="20000" port="8080"
protocol="HTTP/1.1" redirectPort="8443"
useBodyEncodingForURI="true"/>

如果遇到 ajax 请求仍然乱码,useBodyEncodingForURI="true"改为URIEncoding="UTF-8"即可。

常用注解

@RequestParam

  • 作用:
    把请求中指定名称的参数给控制器中的形参赋值。
  • 属性:
    value:请求参数中的名称。
    required:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错。
1
<a href="anno/testRequestParam?name=haha">RequestParam</a>
1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("/anno")
public class AnnoController {
@RequestMapping("/testRequestParam")
public String testRequestParam(@RequestParam(name="name") String username, @RequestParam(value="age",required=false)Integer age){
System.out.println("执行了"+username);
return "success";
}
}

@RequestBody

  • 作用:
    用于获取请求体内容。直接使用得到是 key=value&key=value…结构的数据。
    get 请求方式不适用。
  • 属性:
    required:是否必须有请求体。默认值是:true。当取值为 true 时,get 请求方式会报错。如果取值
    为 false,get 请求得到是 null。
1
2
3
4
5
6
<form action="springmvc/useRequestBody" method="post">
用户名称:<input type="text" name="username" ><br/>
用户密码:<input type="password" name="password" ><br/>
用户年龄:<input type="text" name="age" ><br/>
<input type="submit" value=" 保存 ">
</form>
1
2
3
4
5
@RequestMapping("/useRequestBody")
public String useRequestBody(@RequestBody(required=false) String body){
System.out.println(body);
return "success";
}

@PathVaribale

  • 作用:
    用于绑定 url 中的占位符。例如:请求 url 中 /delete/{id},这个{id}就是 url 占位符。
    url 支持占位符是 spring3.0 之后加入的。是 springmvc 支持 rest 风格 URL 的一个重要标志。
  • 属性:
    value:用于指定 url 中占位符名称。
    required:是否必须提供占位符。
  • 参见下文REST风格URL
1
<a href="springmvc/usePathVariable/100">pathVariable注解</a>
1
2
3
4
5
@RequestMapping("/usePathVariable/{id}")
public String usePathVariable(@PathVariable("id") Integer userid){
System.out.println(id);
return "success";
}

@HiddentHttpMethodFilter

  • 作用:
    由于浏览器 form 表单只支持 GET 与 POST 请求,而 DELETE、PUT 等 method 并不支持,Spring3.0 添
    加了一个过滤器,可以将浏览器请求改为指定的请求方式,发送给我们的控制器方法,使得支持 GET、POST、PUT
    与 DELETE 请求。
  • 使用方法:
    第一步:在 web.xml 中配置该过滤器。
    第二步:请求方式必须使用 post 请求。
    第三步:按照要求提供_method 请求参数,该参数的取值就是我们需要的请求方式。
  • 比较麻烦,了解即可
1
2
3
4
5
<!-- 删除 -->
<form action="springmvc/testRestDELETE/1" method="post">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value=" 删除 ">
</form>
1
2
3
4
5
@RequestMapping(value="/testRestDELETE/{id}",method=RequestMethod.DELETE)
public String testRestfulURLDELETE(@PathVariable("id")Integer id){
System.out.println("rest delete "+id);
return "success";
}

@RequestHeader

  • 作用:
    用于获取请求消息头。
  • 属性:
    value:提供消息头名称
    required:是否必须有此消息头
  • 注:
    在实际开发中一般不怎么用。
1
<a href="springmvc/useRequestHeader">获取请求消息头</a>
1
2
3
4
5
@RequestMapping("/useRequestHeader")
public String useRequestHeader(@RequestHeader(value="Accept-Language", required=false)String requestHeader){
System.out.println(requestHeader);
return "success";
}

@CookieValue

  • 作用:
    用于把指定 cookie 名称的值传入控制器方法参数。
  • 属性:
    value:指定 cookie 的名称。
    required:是否必须有此 cookie。
1
<a href="springmvc/useCookieValue">绑定 cookie 的值</a>
1
2
3
4
5
@RequestMapping("/useCookieValue")
public String useCookieValue(@CookieValue(value="JSESSIONID",required=false) String cookieValue){
System.out.println(cookieValue);
return "success";
}

@ModelAttribute

  • 作用:
    该注解是 SpringMVC4.3 版本以后新加入的。它可以用于修饰方法和参数。
    出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可
    以修饰有具体返回值的方法。
    出现在参数上,获取指定的数据给参数赋值。
  • 属性:
    value:用于获取数据的 key。key 可以是 POJO 的属性名称,也可以是 map 结构的 key。
  • 应用场景:
    当表单提交数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库对象原来的数据。
    例如:
    我们在编辑一个用户时,用户有一个创建信息字段,该字段的值是不允许被修改的。在提交表单数
    据是肯定没有此字段的内容,一旦更新会把该字段内容置为 null,此时就可以使用此注解解决问题。
  1. 基于 POJO 属性的基本使用

    1
    <a href="springmvc/testModelAttribute?username=test">测试 modelattribute</a>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @ModelAttribute
    public void showModel(User user) {
    System.out.println("执行了 showModel 方法"+user.getUsername());
    }
    @RequestMapping("/testModelAttribute")
    public String testModelAttribute(User user) {
    System.out.println("执行了控制器的方法"+user.getUsername());
    return "success";
    }
    /*结果
    执行了 showModel 方法test
    执行了控制器的方法test
    */
  2. 基于 Map 的应用场景示例

    1. ModelAttribute 修饰方法带返回值
    1
    2
    3
    4
    5
    6
    <!-- 修改用户信息,要求不修改用户密码 -->
    <form action="springmvc/updateUser" method="post">
    用户名称:<input type="text" name="username" ><br/>
    用户年龄:<input type="text" name="age" ><br/>
    <input type="submit" value=" 保存 ">
    </form>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @ModelAttribute
    public User showModel(String username) {
    //模拟去数据库查询
    User abc = findUserByName(username);
    System.out.println("执行了 showModel 方法"+abc);
    return abc;
    }
    @RequestMapping("/updateUser")
    public String testModelAttribute(User user) {
    System.out.println("控制器中处理请求的方法:修改用户:"+user);
    return "success";
    }
    1. 不带返回值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      @ModelAttribute
      public void showModel(String username,Map<String,User> map) {
      //模拟去数据库查询
      User user = findUserByName(username);
      System.out.println("执行了 showModel 方法"+user);
      map.put("abc",user);
      }
      @RequestMapping("/updateUser")
      public String testModelAttribute(@ModelAttribute("abc")User user) {
      System.out.println("控制器中处理请求的方法:修改用户:"+user);
      return "success";
      }

@SessionAttribute

  • 作用:
    用于多次执行控制器方法间的参数共享。
  • 属性:
    value:用于指定存入的属性名称
    type:用于指定存入的数据类型。
1
2
3
<a href="springmvc/testPut">存入 SessionAttribute</a>
<a href="springmvc/testGet">取出 SessionAttribute</a>
<a href="springmvc/testClean">清除 SessionAttribute</a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Controller("sessionAttributeController")
@RequestMapping("/springmvc")
@SessionAttributes(value ={"username","password"},types={Integer.class})
public class SessionAttributeController {
/**
* 把数据存入 SessionAttribute
* @param model
* @return
* Model 是 spring 提供的一个接口,该接口有一个实现类 ExtendedModelMap
* 该类继承了 ModelMap,而 ModelMap 就是 LinkedHashMap 子类
*/
@RequestMapping("/testPut")
public String testPut(Model model){
model.addAttribute("username", "泰斯特");
model.addAttribute("password","123456");
model.addAttribute("age", 31);
//跳转之前将数据保存到 username、password 和 age 中,因为注解@SessionAttribute 中有
这几个参数
return "success";
}
@RequestMapping("/testGet")
public String testGet(ModelMap model){
System.out.println(model.get("username")+";"+model.get("password")+";"+model.get("a
ge"));
return "success";
}
@RequestMapping("/testClean")
public String complete(SessionStatus sessionStatus){
sessionStatus.setComplete();
return "success";
}
}

REST 风格 URL

什么是 rest

REST(英文:Representational State Transfer,简称 REST)描述了一个架构样式的网络系统,比如 web 应用程序。它首次出现在 2000 年 Roy Fielding 的博士论文中,他是 HTTP 规范的主要编写者之一。在目前主流的三种 Web 服务交互方案中,REST 相比于 SOAP(Simple Object Access protocol,简单对象访问协议)以及 XML-RPC 更加简单明了,无论是对 URL 的处理还是对 Payload 的编码,REST 都倾向于用更加简单轻量的方法设计和实现。值得注意的是 REST 并没有一个明确的标准,而更像是一种设计的风格。它本身并没有什么实用性,其核心价值在于如何设计出符合 REST 风格的网络接口。

restful 的优点

它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。

restful 的特性

资源( Resources):网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。可以用一个 URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要
获取这个资源,访问它的 URI 就可以,因此 URI 即为每一个资源的独一无二的识别符。
表现层( Representation):把资源具体呈现出来的形式,叫做它的表现层 ( Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。
状态转化( State Transfer):每 发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP 协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生 “状态转化 ”( State Tran sfer)。而这种转化是建立在表现层之上的,所以就是 “表现层状态转化 ”。具体说,就是 HTTP 协议里面,四个表示操作方式的动词: GET、 POST、 PUT、DELETE。它们分别对应四种基本操作: GET 用来获取资源, POST 用来新建资源, PUT 用来更新资源, DELETE 用来删除资源。

restful 的示例

/account/1 HTTP GET : 得到 id = 1 的 account
/account/1 HTTP DELETE: 删除 id = 1 的 account
/account/1 HTTP PUT: 更新 id = 1 的 account