springmvc(二)

1. 访问静态资源:解决方案一

步骤

  1. 创建静态页面,目前可以正常访问

  2. 将web.xml中DispatcherServlet的url-pattern改成/,重启服务器tomcat

  3. 再次访问后台控制器的方法,去掉后缀名.do,可以正确访问

  4. 但所有的静态资源都访问不了

原因:也被DispatcherServlet拦截,而这个Servlet不能处理静态资源。在tomcat中专门有一个默认的Servlet用来处理静态资源。而我们配置的DispatcherServlet覆盖了默认的Servlet,导致所有的静态资源不能访问。

访问地址的区别

区别项*.do/
说明拦截所有后缀名是.do的资源拦截所有的资源,包括静态资源,但不包含.jsp文件
访问方式http://localhost:8080/save.dohttp://localhost:8080/save
是否支持RESTful不支持支持

分析

查看tomcat/conf/web.xml 查看113行和429行

<servlet>
      <servlet-name>default</servlet-name>
      <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
      <servlet-name>default</servlet-name>
      <url-pattern>/</url-pattern>
</servlet-mapping>

DefaultServlet

  1. Tomcat中提供了一个servlet叫做DefaultServlet,他的全名(servlet-class)为org.apache.catalina.servlets.DefaultServlet。

  2. load-on-startup为1是说明当应用启动时就在加载该servlet,默认的情况下是用户第一次访问该servlet方法时才会实例化并加载servlet。

  3. DefaultServlet的servlet-mapping配置的为/,可以处理所有的请求,一般只有defaultServlet会配置为/,如果自定义的Servlet也配置为/,那么将会覆盖defaultServlet的配置。

web.xml中的解决方案

  • 思路:在自己的web.xml中,让指定扩展名的静态资源找default名称对应的servlet即可。

  • 方案:所有html、js、css、jpg等扩展名都由default的Servlet进行处理,配置servlet-mapping即可

<!-- 解决方案1:将所有的静态资源交给默认的default去处理 -->
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.html</url-pattern>
    <url-pattern>*.css</url-pattern>
    <url-pattern>*.js</url-pattern>
    <url-pattern>*.jpg</url-pattern>
</servlet-mapping>

2. 访问静态资源:解决方案二

技术点

<mvc:resources/> 由Spring MVC框架自己处理静态资源,传统Web容器的静态资源只能放在Web容器的根路径下, 而<mvc:resources/>允许静态资源放在任何地方,如:WEB-INF目录下、类路径下等。

属性

location:指定静态资源的位置,可以使用诸如"classpath:"等的资源前缀指定资源位置。 可以同时指定多个路径使用逗号分隔,路径要以/结束

mapping:映射地址(即访问地址),以/**结尾,它表示映射目录下所有的URL,包括子孙路径的资源

步骤

操作一

  1. 映射webapp的根目录为根目录,映射地址为/**

  2. 访问根目录和它子目录下的静态资源

注:/处出现红色不用理会

代码

<mvc:resources mapping="/**" location="/"/>

操作二

  1. 映射webapp的根目录和类路径下的static为根目录,映射地址为/**

  2. 访问resources/static/下的静态资源

代码

<!--
访问静态资源解决方法2:
配置静态资源的访问地址映射,原理:由SpringMVC来处理静态资源
    mapping:指定访问地址映射,设置成/**,表示子孙目录都可以映射
    location:真实的地址,/ 表示webapp目录。(报红忽略)
    可以指定模块下任意的目录进行映射,如:classpath:static/
    可以指定多个目录,使用逗号分隔
 -->
<mvc:resources mapping="/**" location="/,classpath:static/"/>

3. 访问静态资源:解决方案三【推荐】

解决方法

将静态资源交给默认Servlet处理

mvc:default-servlet-handler

在springMVC.xml中配置<mvc:default-servlet-handler />后,会在Spring MVC容器中定义一个

org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler

它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。

 

springMVC.xml

<mvc:default-servlet-handler/>

4. 方法的返回值

控制器方法三种常见的返回值

  1. void:没有返回值,进行转发,重定向和打印数据

  2. String:进行转发,重定向和显示数据

  3. ModelAndView:同步操作在页面上显示数据

1.方法的返回值:void

代码

/**
 * 方法的返回值是void的情况:
 * 1. 如果没有返回值,跳转到:/pages/account/update.jsp (前缀+访问地址+后缀) 就是它跳转的页面
 * 2. 要转发:使用request对象的转发器
 * 3. 重定向:使用response对象
 * 4. 直接在方法中使用打印流输出
 */
@RequestMapping("/update")
public void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    System.out.println("更新");
    //request.getRequestDispatcher("/pages/success.jsp").forward(request, response);
    //response.sendRedirect(request.getContextPath() + "/pages/success.jsp");

    response.setContentType("text/html;charset=utf-8");
    PrintWriter out = response.getWriter();
    out.print("你好,我是新的页面");
}

2.方法的返回值:String类型

代码

/**
 * 返回值是String类型:
 * 转发 forward:url (真实地址)
 * 重定向  redirect:url
 *
 */
@RequestMapping("/delete")
public String delete() {
    System.out.println("删除记录");
    //return "forward:/pages/success.jsp";
    return "redirect:/pages/success.jsp";
}

3. 方法的返回值:ModelAndView类型

代码

 

/**
 * 返回值是:ModelAndView对象
 */
@RequestMapping("/list")
public ModelAndView list(ModelAndView mv) {
    //设置视图
    mv.setViewName("success");
    //设置模型,支持链式写法,同时设置多个 (放在请求域中)
    mv.addObject("name", "孙悟空").addObject("sex", "男");
    //返回ModelAndViw对象
    return mv;
}

@RequestMapping("/find")
public ModelAndView find() {
    //设置视图,键,值
    return new ModelAndView("success", "name", "猪八戒").addObject("sex","女");
}

5.方法返回对象:JSON数据的交互

1.pom.xml导入jackson包

<!--json支持包-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.0</version>
</dependency>

<!--lombok可选-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.18</version>
    <scope>provided</scope>
</dependency>

2.Account实现类,生成get和set方法,生成toString方法

package com.it.entity;

import lombok.Data;

@Data
public class Account {

    private int id;
    private String name;
    private double money;
}

3.编写html, 发送ajax异步请求

 <!DOCTYPE html>
   <html lang="en">
   <head>
       <meta charset="UTF-8">
       <title>首页</title>
       <!--导入jq-->
       <script src="js/jquery-3.3.1.js"></script>
   </head>
   <body>
   <h2>提交JSON数据格式</h2>
   <button id="btn" type="button">提交按钮</button>
   
   <script type="text/javascript">
       $("#btn").click(function () {
           $.ajax({
               url: "account/change",
               method: "post",
               data: '{"id": 1,"name": "张三", "money": 50}',
               contentType: "application/json;charset=utf-8",   //必须指定,否则数据格式错误
               success: function (account) { //返回account对象
                   alert(account.id + "," + account.name + "," + account.money);
               }
           });
       });
   </script>
   </body>
   </html>

4.编写后台控制器类

/**
 * 异步操作:
 * 1. 获取提交的JSON格式数据,转成实体类  @RequestBody 要求提交的必须是JSON格式的字符串
 * 2. 修改实体类的属性
 * 3. 返回JSON字符串
 * @ResponseBody 放在方法上面或返回值的前面,将返回的对象转成JSON字符串
 */
@RequestMapping("/change")
@ResponseBody
public Account change(@RequestBody Account account) {
    System.out.println("用户提交的对象属性值:" + account);
    //修改属性
    account.setId(100);
    account.setName("嫦娥");
    account.setMoney(15000d);
    return account;
}
注解作用
@RequestBody位置:放在方法的参数前面 作用:获取请求体中JSON数据,封装成实体类对象
@ResponseBody位置:放在方法上面或方法返回值的前面 作用:将方法的返回对象转成JSON格式的字符串

6.RESTful风格的URL介绍

概述

REST(英文:Representational State Transfer表现层状态转换,简称REST)RESTful是一种网络应用程序的设计风格和开发方式,基于HTTP协议。

REST 指的是一组架构约束条件和原则,满足这些约束条件和原则的应用程序就是符合RESTful。

两个特点

既然它描述的是个设计风格,那什么样的接口规范的才算符合它的风格呢?我们先得来看RESTful其中的两个特点:

  1. 每一个URI代表1种资源;

  2. 客户端使用GET、POST、PUT、DELETE 四个表示操作方式的动词对服务端资源进行操作

    1. GET用来获取资源

    2. POST用来新建资源

    3. PUT用来更新资源

    4. DELETE用来删除资源

RESTful的实现

  1. 访问地址是一样的

  2. 参数是通过/分隔的,如果有多个参数,使用多个/

请求地址(URI请求含义请求方法
/user新增用户POST
/user/1删除编号1的用户DELETE
/user/1/Jack/18修改编号1的用户PUT
/user/1获取编号1的用户GET

RESTful风格的GET和POST请求代码

页面代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>RESTful操作</title>
</head>
<body>
<h2>GET请求</h2>
<form action="user/1/张三" method="get">
    <input type="submit" value="查询">
</form>
<hr/>

<h2>POST请求</h2>
<form action="user/2" method="post">
    <input type="submit" value="添加">
</form>
<hr/>
</body>
</html>

控制器

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

/**
 * 处理增删改查的操作
 */
@Controller
@RequestMapping("/user")
public class RestfulController {

    /**
     * 限制使用是GET请求
     * 如果要让参数有值,必须使用
     * @PathVariable 作用:从路径上获取变量的值
     *  name /value:指定路径上变量的名字,如果同名可以省略
     *  required:属性是否必须
        @RequestMapping(path = "/{id}/{name}", method = RequestMethod.GET)
     */
    @GetMapping("/{id}/{name}")  //限制使用GET方法的 @RequestMapping
    public String find(@PathVariable Integer id, @PathVariable(name = "name") String username) {
        System.out.println("查找");
        System.out.println("id:" + id + ", name:" + username);
        return "success";
    }

    @PostMapping("/{id}")   //@RequestMapping(method = RequestMethod.POST)
    public String save(@PathVariable Integer id) {
        System.out.println("添加");
        System.out.println("id: " + id);
        return "success";
    }


}

RESTful风格的PUT和DELETE请求

说明

由于浏览器 form 表单只支持 GET 与 POST 请求,而 DELETE、 PUT 等 method 并不支持, Spring3.0 添加了一个过滤器,可以将浏览器请求改为指定的请求方式,发送给我们的控制器方法,使得支持 GET、 POST、 PUT与 DELETE 请求。

使用PUT和DELETE请求步骤

  1. 在 web.xml 中配置HiddenHttpMethodFilter过滤器,过滤所有资源

  2. 表单的请求方式必须使用 post 请求。

  3. 要求提供_method 请求参数,该参数的取值就是我们需要的请求方式。

注意事项

  • PUT和DELETE的服务器端不能进行转发或重定向,否则会出现405错误。

  • 如果访问地址的参数与方法中参数个数不匹配,会出现405错误。

  • 如果方法体中没有输出任何内容,会出现404错误。

步骤

基本操作

  1. 配置HiddenHttpMethodFilter 过滤器,过滤所有的资源 注意:不要导错类,有两个同名的过滤器

  2. 编写HTML页面

    1. 添加2个表单,都使用post提交方式

    2. put请求:表单添加隐藏域:_method="PUT"

    3. delete请求:表单添加隐藏域:_method="DELETE"

    4. put或delete不区分大小写

  3. 编写控制器, 处理同一个地址的不同提交方式的请求。

action后面使用参数

  1. 修改表单action="user/参数"

  2. 修改控制器中的方法,在方法的访问地址后面使用参数:/{变量名}

  3. 在方法的参数上使用@PathVariable注解

  4. 方法返回void,在方法上添加@ResponseBody注解,返回一个字符串直接打印在浏览器上

代码

web.xml

 

<!-- 支持隐藏的方法,让表单支持put和delete请求 -->
<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>

html

<h2>PUT请求</h2>
<form action="user" method="post">
    <input type="hidden" name="_method" value="PUT">
    <input type="submit" value="更新">
</form>
<hr/>

<h2>DELETE请求</h2>
<form action="user" method="post">
    <input type="hidden" name="_method" value="DELETE">
    <input type="submit" value="删除">
</form>

控制器类

package com.it.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

/**
 * 处理增删改查的操作
 */
@Controller
@RequestMapping("/user")
public class RestfulController {

    /**
     * 限制使用是GET请求
     * 如果要让参数有值,必须使用
     * @PathVariable 作用:从路径上获取变量的值
     *  name /value:指定路径上变量的名字,如果同名可以省略
     *  required:属性是否必须
        @RequestMapping(path = "/{id}/{name}", method = RequestMethod.GET)
     */
    @GetMapping("/{id}/{name}")  //限制使用GET方法的 @RequestMapping
    public String find(@PathVariable Integer id, @PathVariable(name = "name") String username) {
        System.out.println("查找");
        System.out.println("id:" + id + ", name:" + username);
        return "success";
    }

    @PostMapping("/{id}")   //@RequestMapping(method = RequestMethod.POST)
    public String save(@PathVariable Integer id) {
        System.out.println("添加");
        System.out.println("id: " + id);
        return "success";
    }

    /**
     * 如果使用put或delete请求,不能使用转发或重定向,否则会出现405的错误。
     * 只能在页面上直接输出结果,添加一个@ResponseBody注解,直接获取响应体的结果
     */
    @PutMapping   //@RequestMapping(method = RequestMethod.PUT)
    @ResponseBody
    public String update() {
        System.out.println("更新");
        return "Update Success";
    }

    /*
    删除操作
     */
    @DeleteMapping   //@RequestMapping(method = RequestMethod.DELETE)
    @ResponseBody
    public String delete() {
        System.out.println("删除");
        return "Delete Success";
    }
}

7.SpringMVC实现文件上传

使用SpringMVC实现文件上传,本质就是用到了Common-FileUpload组件实现。

步骤

  1. 在pom.xml添加commons-fileupload依赖

  2. 编写文件上传页面fileupload.html

    注:HTML网页主文件名不能与控制器/upload的访问地址相同,否则会出现500错误。因为默认的Servlet在SpringMVC之后处理。所以要么上传页面使用jsp,要么网页的主文件名不要与控制器的访问地址相同。

  3. 创建新的控制器方法

    1. 有2个参数:HttpServletRequest,MultipartFile 也可以使用更多参数,将普通文本框的参数加进来

    2. 通过上下文对象,获取文件上传的真实目录

    3. 调用multipartFile中的getOriginalFilename()获取方法获取原始的文件名

    4. 调用multipartFile中的transferTo()将文件上传到指定的目录

  4. 配置springMVC.xml中的CommonsMultipartResolver对象

    1. 指定id为multipartResolver,这个id是固定的,不能随意修改。 因为SpringMVC封装文件上传的数据,找的是容器中指定的bean,否则会导致方法中MultipartFile对象为空。

    2. 可以指定上传文件大小的属性,比如:maxUploadSize文件最大不能超过10485760 (10M)

    3. 如果其它文本框或文件名有汉字出现乱码,需要指定属性:defaultEncoding为utf-8

代码

  1. 添加依赖

<!--添加Apache文件上传支持包-->
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.1</version>
</dependency>

2.上传的页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传文件</title>
</head>
<body>
<h2>上传文件</h2>
<!--
上传文件三要素:
    1. 必须使用post方法提交
    2. 必须指定:enctype="multipart/form-data"
    3. 必须使用文件域,指定name属性
-->
<form action="upload" method="post" enctype="multipart/form-data">
    用户名:
    <input type="text" name="username" placeholder="请输入用户名"> <br/>
    照片:
    <input type="file" name="photo" accept="image/*"> <br/>
    <input type="submit" value="上传文件">
</form>
</body>
</html>

3.编写控制器方法:参数是:请求对象,MultipartFile photo 注:参数名photo必须与表单中文件域的name相同

package com.it.controller;
import org.springframework.stereotype.Controller;
   import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

   import javax.servlet.http.HttpServletRequest;
import java.io.File;
   import java.io.IOException;

   /**
上传文件的控制器
*/
@Controller
public class UploadController {
/**
 * 上传文件方法
 * @param photo 参数名必须与表单中文件域的名字要一致
 * @return
 */
@RequestMapping("/upload")
public String upload(HttpServletRequest request, MultipartFile photo, String username) throws IOException {
    System.out.println("用户名:" + username);
    //1. 获取上传的文件名
    String filename = photo.getOriginalFilename();
    //获取服务器真实的地址: 请求对象 -> 上下文对象 -> 方法获取真实地址
    String realPath = request.getServletContext().getRealPath("/img/");
    System.out.println("服务器真实地址:" + realPath);
    //2. 写入文件,参数是一个文件对象。
    photo.transferTo(new File(realPath, filename));
    System.out.println("上传文件成功");
    return "success";
}
}

 4. 配置文件上传解析器

         1. id必须叫multipartResolver
         2. 指定最大上传文件大小

         3. 如果其它有汉字,可以指定汉字的编码

<!-- 配置上传文件处理器,id必须叫multipartResolver名字 -->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
    <!--上传文件大小的属性 -->
    <property name="maxUploadSize" value="10485760"/>
    <!-- 表单上传中如果有汉字乱码,指定它的编码 -->
    <property name="defaultEncoding" value="utf-8"/>
</bean>

8. SpringMVC中异常处理机制

处理流程

步骤

  1. 创建异常处理类,实现HandlerExceptionResolver接口

  2. 重写resolveException方法,四个参数,其中第三个参数是HandlerMethod对象

    1. 创建ModelAndView对象

    2. 向请求域中添加错误信息

    3. 设置显示的视图,注:错误页面放在了pages目录下

    4. 返回ModelAndView对象

    5. 输出异常的方法名和异常的处理器类

  3. 必须将这个异常处理类加入到Spring IoC容器中,无需指定id

代码

控制器类

  1. 在方法中模拟异常

/**
 * 更新
 */
@RequestMapping("/update")
public String update(Integer age) {
    System.out.println("用户年龄:" + age);
    if (age <=18) {
        throw new RuntimeException("未成年,请绕行");
    }
    return "success";
}

用户定义的异常处理类

package com.it.filter;

import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * 自定义异常处理实现类
 */
@Component  //使用注解加到容器中去,注:要扫描这个包
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
    /**
     * 处理异常的方法
     * @param request 请求对象
     * @param response 响应对象
     * @param handler HandlerMethod对象
     * @param ex 出现的异常
     * @return 跳转到哪个页面,并且指定模型数据
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println(handler.getClass());
        //第三个参数对象
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        //获取处理器中哪个方法出现了异常
        Method method = handlerMethod.getMethod();
        System.out.println("出现异常的方法:" + method.getName());  //update()
        Object bean = handlerMethod.getBean();
        System.out.println("出现异常的处理器对象:" + bean);  //ExceptionController
        //跳转到/pages/error.jsp页面
        return new ModelAndView("error", "msg", ex.getMessage());
    }
}

配置文件

springMVC.xml在IoC容器中声明异常处理类

可以配置,也可以使用注解
<!-- 1.扫描处理器所在包 -->
<context:component-scan base-package="com.it"/>

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值