框架技术----SpringMVC的返回值和url-pattern

springMVC

内容管理

    • 处理器方法返回值
      • 返回ModelAndView 携带数据model跳转资源view
      • 返回String 只是跳转资源view
      • 返回值void 处理AJAX
      • 返回对象Object @ResponseBody辅助ajax
    • @RequestMapping属性produces
    • DispatcherServlet的url-pattern 使用/
      • 如果为/ ,【覆盖】无法访问静态资源
        • / 和/*的区别
      • 静态资源访问措施
        • 配置文件加上 default-servlet-handler标签
        • 在配置文件中加上resources标签
        • static直接指定静态资源,一个标签就可
    • 前台路径、后台路径和资源路径

javaWeb—SSM中最后控制层MVC框架


处理器方法返回值、静态资源处理


MVC框架主要就是按照MVC的设计模式,处理的数据model和视图的数据view都是通过处理器方法的返回值来实现的;同时视图解析对象功能很强大:属性前缀和后缀直接就只用写逻辑名称了;将安全的界面和lib放在WEB-INF下就只能服务器调用了

处理器方法返回值

使用@Controller注解的处理器的处理方法使用@RequestMapping修饰,value就是访问的资源路径;在类上面使用@RequestMapping就可以指定模块路径;减少处理器方法的value的长度。处理器方法的返回值代表的是处理的结果;【处理器方法加上method属性其实就可以直接类似servlet的doGet或者doPost方法】;处理器方法的返回值常用的有4种类型: ModelAndView、String、无返回值void、返回自定义类型对象

返回ModelAndView 携带数据model跳转资源view

适用范围 : 如果处理器方法处理完毕之后、需要跳转到其他的资源、并且又要在跳转的资源间传递数据,此时返回ModelAndView较好

如果要返回ModelAndView,处理器方法中就要有ModelAndView对象;1). 通过addObject方法将数据以键值对的方法存储到mv对象中【其实最终是放到了requestScope中,就是一种请求转发forward】 ,2). 使用setViewName指定要跳转的资源的名称

可以看出来,这个返回值就是适用于之前普通的Servlet将数据放入,然后将数据交给下一个资源使用的场景,如果处理器方法只是跳转但是不需要数据【比如之前修改学生后跳转执行显示页面】或者只是传递参数但是不需要跳转【AJAX请求】,这个时候就会出现model或者view多余;不适合使用

返回String 只是跳转资源view

适用范围 : 处理器方法返回的String可以指定逻辑视图名,然后视图解析的完整的资源名;适用于只是跳转页面但是不需要携带任何数据的情景

返回内部逻辑资源视图名和ModelAndView相同,在容器中使用bean注册一个InternalResourceViewResolver对象即可,逻辑资源名和prefix和suffix结合即可; 返回的字符串会直接当作逻辑视图的名称

返回值直接就是代表的逻辑视图的名称; 框架内部解析

@RequestMapping(value = "/other.do",method = RequestMethod.GET)
public String doOther(){
   System.out.println("执行了other.do的方法");
   return "result";  //这里的效果和modelAndView的mv.setViewName("result");类似
}

//测试了一下,如果返回空,服务器会寻找的是other.jsp,也就是访问的地址的逻辑名称加上prefix和suffix

访问的结果

<a href="test/other.do.do'">只是跳转结果a>

访问查看是否跳转

//后台
执行了other.do的方法
//前台
msg数据 :

fun数据 :

just for test, don't care

可以看到确实返回值就是直接代表的是view界面的逻辑名称

如果在使用String的情况下,也想传递数据; 那么就直接将HttpServletRequest对象,之后将数据放到请求作用域中,因为内部是使用的请求转发

 @RequestMapping(value = "/other.do",method = RequestMethod.GET)
public String doOther(HttpServletRequest req){
    System.out.println("执行了other.do的方法");
    req.setAttribute("msg","你好,Jning");
    req.setAttribute("fun","doOther方法");
    return "result";
}


-------------------result---------------------
    
msg数据 :你好,Jning

fun数据 :doOther方法

just for test, don't care

同时需要注意,如果配置了视图解析器,返回的就必须是逻辑名称,如果是完整的路径,就会出现错误,这里可以看看错误

return "/WEB-INF/view/result.jsp";

项目直接报错

HTTP状态 404 - 未找到
类型 状态报告
消息 文.件[/WEB-INF/view/WEB-INF/view/result.jsp.jsp] 未找到 ---->视图解析还是会加上前后缀

所以在用视图解析器的时候,必须是逻辑名称; 在IDEA中,当正确书写的时候,旁边会有提示,可以直接跳转到related view

返回值void 处理AJAX

适用范围 :处理AJax请求的时候使用void,因为不需要跳转页面和将数据放到RequestScope中,数据直接放到response中返回

之前使用AJAX的时候就是通过response直接返回传递给ajax数据,和视图view无关,Servlet的doGet方法也是void的

这里可以来演示ajax的请求,害,还是不手写util来封装json对象了,因为手工封装需要在前台使用eval,并且MIME类型是text/html,不是applicaion/json;这里直接使用jackson工具,首先导入依赖jackson-core,和jackson-databind ; 两个jar一起构成jackson的功能,如果要使用需要一起导入,不然无法正常使用【bind 结】 — 需要bind联系各个部分

<dependency>
    <groupId>com.fasterxml.jackson.coregroupId>
    <artifactId>jackson-coreartifactId>
    <version>2.13.1version>
dependency>

<dependency>
    <groupId>com.fasterxml.jackson.coregroupId>
    <artifactId>jackson-databindartifactId>
    <version>2.13.1version>
dependency>

使用也很简单,主要依赖的就是ObjectMapper的实例方法writeValueAsString方法来讲对象转为json格式

这里使用Junit测试一下结果

public class TestJson {
    //测试一下jackson
    @Test
    public void testJackson() throws JsonProcessingException {
        List<Student> stulist = new ArrayList<>();
        stulist.add(new Student(1001,"李四","HC2001")); //直接使用匿名对象了
        stulist.add(new Student(1002,"王五","HC2003"));
        stulist.add(new Student(1003,"Cfeng","HC2001"));
        String json = "";
        json = new ObjectMapper().writeValueAsString(stulist);
        System.out.println(json);
    }
}

[{"stuno":1001,"stuname":"李四","stuclass":"HC2001"},{"stuno":1002,"stuname":"王五","stuclass":"HC2003"},{"stuno":1003,"stuname":"Cfeng","stuclass":"HC2001"}]

就是转为一个json的数组,这样就可以直接通过数组的方式取出,和之前的util结果类似,当时其实也是不需要len的,因为可以通过数组直接获取len

所以这里的处理器方法

    @RequestMapping(value = "/ajax.do",method = RequestMethod.GET)
    public void doAjax(HttpServletResponse resp, Student student) throws IOException {
        //student用来接收上传的数据,这样就不用req来getParameter了
        System.out.println("上传的数据是" + student.getStuno() + " : " + student.getStuname() + ": " + student.getStuclass());
        //调用service处理 ……
        //返回处理结果,假设是一个student的list
        String json = ""; //避免空报错
        List<Student> stulist = new ArrayList<>();
        stulist.add(new Student(1001,"李四","HC2001")); //直接使用匿名对象了
        stulist.add(new Student(1002,"王五","HC2003"));
        stulist.add(new Student(1003,"Cfeng","HC2001"));
        //将结果转为json返回给ajax;使用jackson
        if(stulist != null) {
            json = new ObjectMapper().writeValueAsString(stulist);
        }
        //输出json数据给ajax异步对象
        resp.setContentType("application/json;charset=utf-8");//设置MIME格式,这里使用jackson,可以直接返回json
        PrintWriter out = resp.getWriter();
        out.println(json);
        out.flush();
        out.close();
    }

前端页面获取这个对象进行处理;dataType进行处理转为json;处理之后的状态为4,status为200;注意:script引入的时候也是有结束标签的,不然运行不了,我在这个问题上卡了会才发现

这样就可以成功的进行ajax的处理

框架技术----SpringMVC的返回值和url-pattern_第1张图片

可以看到响应的就是json的对象;JQuery会自动将字符串给转化为json对象

提示 : 这里遇到了一个让人很不舒服的乱码问题,就是设置MVC的过滤器之后会出现另外的乱码问题,可能是版本不兼容的关系,所以这里可以手工实现过滤,使用过滤器之后欢迎页面都乱码了;并且GET请求的参数也乱码了;这里仔细思考之后; 决定换拦截路径,这里的问题暂时保留,因为删除过滤器再加又恢复,所以目前不知道原因【评论区回答】

返回对象Object @ResponseBody辅助ajax

适用范围 这里不是针对的某种情景,而是作为void类型的ajax请求的辅助,分析上面的代码,会发现又很多重复的部分

 if(stulist != null) {
            json = new ObjectMapper().writeValueAsString(stulist);
        }
        //输出json数据给ajax异步对象
        resp.setContentType("application/json;charset=utf-8");//设置MIME格式,这里使用jackson,可以直接返回json
        PrintWriter out = resp.getWriter();
        out.println(json);
        out.flush();
        out.close();

就是这一块----将对象转化为json格式然后放入到响应体,这里就可以通过Object来进行简化

处理器方法可以返回Object对象,Object可以是Integer、String等多种类型,返回的对象不是作为逻辑视图出现,而是作为直接在页面进行显示的数据出现; 返回对象,需要使用@ResponseBody注解; 实现的步骤:

  1. 这里相当于是框架内会自动去调用jackson完成工作,所以必须加入jackson的依赖,包括jackson-core和jackson-bind
  2. 在springMvc配置文件中,因为要使用注解,所以必须加入注解驱动,注意是MVC命名空间【之前注解的方式实现事务也要加入驱动tx空间】 加入后会自动完成json = new ObjectMapper().writeValueAsString(stulist); — > 加入后会自动创建消息转化器接口的7个实现类对象,包括MappingJackson2HttpMessageConverter
<mvc:annotation-driven/>

实现的方式 : springmvc处理器方法返回Object,可以转为json输出到浏览器,响应ajax: 注解驱动完成java对象对象到json等数据格式的转化,使用的是HttpMessageConvert接口—消息转换器;各种实现类分别转化为不同格式的数据(strategy pattern)

//可以看看这个接口
public interface HttpMessageConverter<T> {
 boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

 boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);

 List<MediaType> getSupportedMediaTypes();

 default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
     return !this.canRead(clazz, (MediaType)null) && !this.canWrite(clazz, (MediaType)null) ? Collections.emptyList() : this.getSupportedMediaTypes();
 }

 T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;

 void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
}

除了之间的方法,其余的刚好成对,read和canread;write和canwrite ----> 控制器类将结果输出给浏览器使用

  • canwirte : 检查处理器方法是否可以将返回值能否转化为mediaType表示的数据格式,比如json、xml等,如果可以,就返回true
  • wirte : 把处理器方法的返回值对象,调用jackson的objectMapper转为json格式字符串

开发常用的实现类是:

public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {

public class MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter { ---->使用jackson工具库来完成类型的转换

加入之后会多创建几个对象,就上面提到的对象

  1. 在处理器方法的上面加上@ResponseBody注解,相当于上面代码的输出的部分 : 自动将返回值对象自动转为json放到response中输出到浏览器

这里可以使用方法来返回对象来修改一下上面的Ajax的方法;不再使用void返回值

    @RequestMapping(value = "/ajax.do",method = RequestMethod.GET)
    @ResponseBody
    public  List<Student> doAjax(HttpServletResponse resp, Student student) {
        //执行相关操作后需要返回数据; 这里加入直接后就和上面的String相同,都是被赋予了特定的含义,
        // 不加注解的String就是逻辑名称;这里加入注解后的对象就是json返回的对象
        List<Student> stulist = new ArrayList<>();
        stulist.add(new Student(1001,"李四","HC2001")); //直接使用匿名对象了
        stulist.add(new Student(1002,"王五","HC2003"));
        stulist.add(new Student(1003,"Cfeng","HC2001"));
       return stulist;
    }

这里就和上面的String一样,只要返回就好了,框架内部自动进行处理,可以看看响应

HTTP/1.1 200 
Content-Type: application/json   ----> 返回的类型自动就是json格式
Transfer-Encoding: chunked
Date: Sat, 15 Jan 2022 09:45:08 GMT
Keep-Alive: timeout=20
Connection: keep-alive

a2  ---> 传输的数据都是字节流     ISO-8859-1
[{"stuno":1001,"stuname":"鏉庡洓","stuclass":"HC2001"},{"stuno":1002,"stuname":"鐜嬩簲","stuclass":"HC2003"},{"stuno":1003,"stuname":"Cfeng","stuclass":"HC2001"}]
0

这里的效果和上面的void是相同的,所以之后就直接加入annotation-driven再加入注解就可以自动转为json了,注意一定要加入jackson的依赖
框架技术----SpringMVC的返回值和url-pattern_第2张图片

框架内部的执行的过程:

1.框架会把返回的List类型,调用ArrayList中的每一个类的canWrite方法检查那个HttpMessageConvter接口的实现类能处理其中的数据,依赖的MappingJackson2HttpMessageConverter
2. 框架调用实现类的write方法,MappingJackson2HttpMessageConverter的write方法,将List对象转为json;嗲用jackson的ObjectMapper.writeValueAsString等方法实现
3. 框架会调用@ResponseBody将2中的数据输出到浏览器的响应体中,完成ajax请求

其实这里的注解加上之后就相当于是直接将返回值输出到响应体,类似于一个out.println(returnResult); 所以如果是对象,配合annotation-driven就会输出对象到响应体,如果是其他的数据,就直接输出到响应体【也即是数据】

@RequestMapping属性produces

如果返回非中文字符串,将前面返回数值型数据直接修改为字符串即可,但如果其中包含中文,接收的时候就会出现乱码,这个时候使用数学produces来指定字符串;produce:产品、结果,这个用于设置结果的类型【之前的value指代的路径,method指代访问的方式】

处理器方法的返回值为String类型,String表示的是数据还是视图;就看是否有注解@ResponseBody

比如这里直接输出字符串数据到响应体中

@RequestMapping(value = "/data.do",method = RequestMethod.GET)
    @ResponseBody
    public String doData(){
        System.out.println("执行了data.do的方法");
        return "你好,我是MVC,欢迎使用";
    }

浏览器输出的结果是

???MVC??? ----出现了中文乱码,这个时候就要使用produces的属性了

HTTP/1.1 200 
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 13
Date: Sat, 15 Jan 2022 10:30:04 GMT
Keep-Alive: timeout=20
Connection: keep-alive

?????MVC?????

可以看到错误的原因就是因为采用的编码方式为ISO-8859-1

这里修改的范式就是通过属性produce,可以重新设置MIME类型

 @RequestMapping(value = "/data.do",method = RequestMethod.GET,produces = "text/plain;charset=utf-8")

这里就是设置了contentType,plain是无装饰的,表示的就是普通的text;框架内部完成这个也是通过的HttpMessageConverter接口的实现类完成转化,json是jackson的; 这里的String是StringHttpMessageConverter这个实现类来完成的【canwrite和write】 将字符串按照指定的编码来处理

设置了MIME类型之后就可以看到响应的contentType变化了

HTTP/1.1 200 
Content-Type: text/plain;charset=utf-8
Content-Length: 33
Date: Sat, 15 Jan 2022 10:43:02 GMT
Keep-Alive: timeout=20
Connection: keep-alive

浣犲ソ锛屾垜鏄疢VC锛屾杩庝娇鐢?

其实这个属性的作用就和之前的使用response对象的setContentType是相同的

DispatcherServlet的url-pattern 使用/

之前设置的url设置的是*.do后缀名的方式,可是实际开发中不一定有后缀,所以要采取另外的方式,除了这种扩展名的方式通配,还有就是/的方式

之前分析servlet的时候提过/*会匹配所有的请求,会覆盖所有的后缀,只用在filter中,包括动态和静态的资源;默认的default匹配静态资源请求和所有的未映射的请求;项目的/会覆盖default,导致静态资源访问出现问题,jsp还是正常的;/的优先级最低

Tomcat会处理静态资源【html、图片 — defalutservlet处理、jsp---- jspservlet处理】,之前动态资源是servlet处理,而现在使用MVC就是中央处理器处理

为社么Tomcat也可以处理这些静态的资源呢?这是因为Tomcat中有默认的servlet【这里只是截取小部分】

<servlet>
    <servlet-name>defaultservlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServletservlet-class>
     <load-on-startup>1load-on-startup>
    
    
  处理静态资源以及未映射的其他servlet的请求
<servlet-mapping>
    <servlet-name>defaultservlet-name>  
    <url-pattern>/url-pattern>
servlet-mapping>  
    
    
<servlet>
    <servlet-name>jspservlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServletservlet-class>
     <load-on-startup>3load-on-startup>
    

<servlet-mapping>
    <servlet-name>jspservlet-name>
    <url-pattern>*.jspurl-pattern>
    <url-pattern>*.jspxurl-pattern>
servlet-mapping>

可以看到有两个servlet,default匹配的就是/,就是所有的静态资源,服务器启动的时候就会创建;还有一个是jsp处理servlet,匹配所有的jsp请求,所有的jsp都是这个servlet处理,也是启动的时候创建对象

所以,说Tomcat可以处理请求不准确,因为其实真正处理请求的都是servlet;不管是静态还是动态;没有匹配上的servlet请求都是defaultservlet进行处理,比如访问项目中不存在的路径就是default处理的,返回的404

如果为/ ,【覆盖】无法访问静态资源

如果项目中的中央处理器的路径设置成 / , 那么就会自动替代defalut,静态资源本来应该是Tomcat的default处理,现在覆盖了,所以静态资源的访问就出现问题了;会出现404的错误

默认情况下Dispatcher是没有处理静态资源的能力的,没有控制器对象能处理静态资源的访问,所以html、js、图片等都是404; 但是动态资源是可以正常访问的

/ 和/*的区别

/ 和 /* 一般都可以认为是匹配所有 【都是全路径】

/ 优先级最低,只是比默认的default高,filter过滤器无用,只能用在servlet中;会覆盖default;default的路径就是/,其会处理所有的静态资源页面和未映射请求 【/是匹配所有,覆盖了default需要解决】

/* 优先级很高,比后缀方式高,会覆盖所有的后缀方式,所以伤害性高,如果中央处理器使用了,就真的会拦截所有的请求,包括tomcat中处理jsp的*.jsp也会覆盖,无法处理jsp的请求,一般只用在过滤器中

静态资源访问措施

解决上面的/无法访问静态资源,其实就是让Tomcat的default这个servlet发挥作用,不被覆盖

配置文件加上 default-servlet-handler标签

再springmvc中加上这个声明之后,springmvc就会再容器中创建DefaultServletHttpRequestHandler处理器对象,也就是一个检查的对象,会检查所有的url,识别出所有的静态资源,然后转交给Web默认的default对象进行处理;

 <!-- 解决静态资源覆盖问题,注册defaultServletHandler -->
<mvc:default-servlet-handler/>

可以看看这个类的内部的代码

public class DefaultServletHttpRequestHandler implements HttpRequestHandler, ServletContextAware {
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) 
        RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName);
         rd.forward(request, response);

就是请求转发的过程

default-servlet-handler和@RequestMapping注解有冲突,需要加入之前的annotation-driven来解决问题

在配置文件中加上resources标签

在spring中,spring专门定义了用来进行静态资源处理的ResourceHttpRequestHandler,添加了mvc:resource标签,专门解决静态资源无法访问的问题,也是需要在配置文件中配置【这种方式不依赖Tomcat服务器的default】

  • location: 代表静态资源所在的目录,目录不要使用WEB-INF下的【用户不能直接访问】
  • mapping : 表示对该资源的请求 /image/** 【两个】

因为在项目开发的时候静态资源很多,所以不是直接就放在webapp下面,而是想js一样,都有一个一级目录;比如/images/…… /html/…… /js/……;同时也可以表示多级目录,**代表的是匹配任意字符

上面的第一种方式,如果不在配置文件中加入handler,就会发生报错

警告 [http-nio-8080-exec-1] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /MVCtest/

这里就是找不到handler,不能转发请求,不能正常访问静态资源。这里将html文件都放到html包下面,所以这里修改一下欢迎页面

 <welcome-file-list>
     <welcome-file>html/index.htmlwelcome-file>
 welcome-file-list>

这里再配置文件中使用的resources标签用于处理静态资源的访问,location就是位置/XXX/即可,而mapping是映射就是访问的url的地址,其实就是前面的加上**就可以; /XXX/–

 <!-- 这里是后台路径,所以加上/,会带上根路径 location表示的是目录位置; mapping代表的是访问的url地址 -->
<mvc:resources mapping="/html/**" location="/html/"/>
<mvc:resources mapping="/js/**" location="/js/"/>

这样就成功访问,需要设置welcome-list

resources和@RequestMapping注解有冲突,需要加入之前的annotation-driven来解决问题

static直接指定静态资源,一个标签就可

这里其实还是有点繁琐,实际开发中,常常直接再webapp下面建一个static文件夹,然后将所有的静态资源放在static文件夹下面 static文件夹代表的就是静态资源文件,资源文件都是static开始的

框架技术----SpringMVC的返回值和url-pattern_第3张图片

这样就只用配置一个resources标签就可以了

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

换一下欢迎页面的位置 : < welcome-file>static/html/index.html

前台路径、后台路径和资源路径

这里之前分享过的,前台路径是以/开头的

没有/的就是直接资源开始的,是会发生变化的,与当前资源所在的位置有关系

这里就不再赘述

你可能感兴趣的:(JAVAweb,mvc,java,服务器,springmvc)