SpringMVC框架作用在servlet层。
Spring MVC是基于Spring的一个模块,专门做web开发,可以理解为是Servlet的升级
M----model(模型)
一般情况下用于封装数据
如分页模型(Pagebean)封装了分页所需要的数据
V----view(视图)
通常视图是依据模型数据创建的
C------controller(控制器)
通常用于处理业务逻辑
引入依赖
<properties> <spring.verion>5.0.2.RELEASE</spring.verion> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.verion}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.verion}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.verion}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> </dependency> </dependencies>
- 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
配置springMVC.xml文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" 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.cq"></context:component-scan> <!-- 配置视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前缀 --> <property name="prefix" value="/page/"></property> <!--后缀 --> <property name="suffix" value=".jsp"></property> </bean> <!--开启 springmvc 注解支持--> <mvc:annotation-driven></mvc:annotation-driven> </beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
配置web.xml配置文件
<!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>SpringMVCDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 配置初始化参数, 用于读取 SpringMVC 的配置文件 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>SpringMVCDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
编写controller
@Controller//自动装配到spring容器中 public class HelloController { @RequestMapping("/demo01") public String sayHello(){ System.out.println(1); return "success"; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
物理视图: 前缀 + 逻辑视图(success) + 后缀
项目的目录结构
具体步骤
第一步: 发起请求到前端控制器(DispatcherServlet)
第二步: 前端控制器请求 HandlerMapping 查找 Handler (可以根据 xml 配置、 注解进
行查找)
第三步: 处理器映射器 HandlerMapping 向前端控制器返回 Handler, HandlerMapping 会
把请求映射为 HandlerExecutionChain 对象(包含一个 Handler 处理器(页面控制器) 对
象, 多个 HandlerInterceptor 拦截器对象), 通过这种策略模式, 很容易添加新的映射策
略
第四步: 前端控制器调用处理器适配器去执行 Handler
第五步: 处理器适配器 HandlerAdapter 将会根据适配的结果去执行 Handler
第六步: Handler 执行完成给适配器返回 ModelAndView
第七步: 处理器适配器向前端控制器返回 ModelAndView (ModelAndView 是 springmvc
框架的一个底层对象, 包括 Model 和 view)
第八步: 前端控制器请求视图解析器去进行视图解析 (根据逻辑视图名解析成真正的
视图(jsp)), 通过这种策略很容易更换其他视图技术, 只需要更改视图解析器即可
第九步: 视图解析器向前端控制器返回 View
第十步: 前端控制器进行视图渲染 ( 视图渲染将模型数据(在 ModelAndView 对象中)
填充到 request 域)
第十一步: 前端控制器向用户响应结果
用户请求到达前端控制器,它就相当于mvc模式中的c(controller),DispatcherServlet是整个流程控制的中心,由它调用其他组件处理用户的请求(好像一个代理),DispatcherServlet的存在降低了组件之间的耦合性
HandlerMappring负责根据用户请求找到Handler(处理器).springMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等
它就是我们开发中要编写的具体业务控制器。由DispatcherServlet把用户请求转发到Handler。由handler对具体的用户请求进行处理
通过HandlerAdapter对处理器进行执行,通过扩展适配器可以对更多类型的处理器进行执行
View Resolve负责将处理结果生成的View视图。View Resolve首先根据逻辑视图名解析成物理视图名(具体的页面地址)。在生成VIew视图对象。最后对View进行渲染将处理结果通过页面展示给用户
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户, 需要由程序员根据业务需求开发具体的页面
为了提供开发效率,springMVC提供了很多的注解
作用: 用于建立请求 URL 和处理请求方法之间的对应关系。
出现的位置:
1,可以放到方法上
2,可以放到类上
属性:
value: 用于指定请求的 URL。 它和 path 属性的作用是一样的。
method: 用于指定请求的方式。params: 用于指定限制请求参数的条件。
@RequestMapping(value = "demo04",method = RequestMethod.GET) public String test04(){ return "success"; }
- 1
- 2
- 3
- 4
- 5
- 6
当前台传过来的数据名称和后台的参数名称不一样时需要用到@RequestParam进行数据的绑定
当形参和前台传递的名称不一样时可以使用@RequestParam绑定 public String test02(@RequestParam("name") String names,@RequestParam("password") String passwords){ System.out.println(names); System.out.println(passwords); return "success"; }
- 1
- 2
- 3
- 4
- 5
- 6
前台传的数据可以用pojo里的类来接收
controller类
@RequestMapping("/demo02") public String test02(User user){ System.out.println(user); return "success"; }
- 1
- 2
- 3
- 4
- 5
- 6
前端页面
<a href="${pageContext.request.contextPath}/demo02?username=李四&password=123">[传参.....]</a>
- 1
对数据格式进行解析
以日期格式为例,用户在前端传递来的日期数据如果后端想用Date类型的值去接收,那么会出现400,因为SpringMVC无法自动解析Date类型的数据
方法一:通过使用注解进行前端日期格式的转换
/* * @Author CodeArts * @Description 用date类型来接收前端传来的日期数据,springmvc无法解析会出现400错误,所以springmvc提供了@DateTimeFormat进行日期数据格式的解析 * @Date 17:17 2022/6/9 * @Param [time] * @return java.lang.String **/ public String deleteUser(@DateTimeFormat(pattern = "yyyy-MM-dd") Date time){ System.out.println(time); return "success"; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
方法二:自定义转换器
第一步: 定义一个类, 实现 Converter 接口, 该接口有两个泛型。:第一个是当前数据的类型,第二个是要转换的目标数据类型
public class StringToDateConverter implements Converter<String, Date> { @Override public Date convert(String source) { DateFormat format = null; try { if(StringUtils.isEmpty(source)) { throw new NullPointerException("请输入要转换的日期"); } f ormat = new SimpleDateFormat("yyyy-MM-dd"); Date date = format.parse(source); return date; } catch (Exception e) { throw new RuntimeException("输入日期有误"); } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
第二步: 在 springmvc.xml 配置文件中配置类型转换器。
<!-- 配置类型转换器工厂 --> <bean id="converterService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <!-- 给工厂注入一个新的类型转换器 --> <property name="converters"> <array> <!-- 配置自定义类型转换器 --> <bean class="com.qf.convert.StringToDateConverter"></bean> </array> </property> </bean> <!--开启 springmvc 注解支持--> <mvc:annotation-driven conversion-service="converterService"></mvc:annotation-driven>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
第三步: 在 annotation-driven 标签中引用配置的类型转换服务。
<!--开启 springmvc 注解支持--> <mvc:annotation-driven conversion-service="converterService"></mvc:annotation-driven>
- 1
- 2
获取实体的内容(实体如表单等)
/* * @Author CodeArts * @Description @RequestBoy:获取实体内容:form表单的内容 * @Date 17:31 2022/6/9 * @Param [body] * @return java.lang.String **/ @RequestMapping("demo06") public String test06(@RequestBody String body){ System.out.println(body); return "success"; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
用于绑定url中的占位符。{}表示占位符
controller
/* * @Author CodeArts * @Description @PathVaribale 使用restFul 风格传参时必须使用该注解接收 * {}相当于占位符 * @Date 17:36 2022/6/9 * @Param [id] * @return java.lang.String **/ @RequestMapping("demo07/{id}") public String test07(@PathVariable("id") Integer id){ System.out.println(id); return "success"; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
前端
<a href="${pageContext.request.contextPath}/demo07/100">测试</a>
- 1
用户获取请求头的信息
可以修饰无返回值和有返回值的方法,表示在控制器方法之前执行
@ModelAttribute public void test(){ System.out.println("ModelAttribute执行"); }
- 1
- 2
- 3
- 4
在web.xml配置过滤器,将过滤的资源编码格式统一
<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
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
响应用户的请求
springmvc.xml的视图解析器配置
<!-- 配置视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/pages/"></property>//前缀 <property name="suffix" value=".jsp"></property>//后缀 </bean>
- 1
- 2
- 3
- 4
- 5
controller控制类
/* * @Author CodeArts * @Description 使用SpringMVC的视图解析器,首先返回一个逻辑视图,通过springmvc中的视图解析器进行解析 * 物理路径: 前缀 + 逻辑视图 + 后缀 ==> /pages/success.jsp @Date 17:38 2022/6/10 * @Param [] * @return java.lang.String **/ @RequestMapping("test01") public String test01(){ return "success"; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
使用serveltAPI进行页面跳转
1,请求转发
2,重定向
请求转发和重定向的比较
- 请求转发只执行了一次请求,而重定向执行了两次
- 请求转发地址栏不变,重定向地址栏发生变化(所以在表单数据提交时要用重定向在刷新时地址栏发生改变不会出现重复提交)
- 请求转发不能访问其他服务器中的资源,重定向可以
- 请求转发的性能要优于重定向
/* * @Author CodeArts * @Description 使用原生的Servlet的请求转发 * @Date 17:44 2022/6/10 * @Param [request, response] * @return void **/ @RequestMapping("test02") public void test02(HttpServletRequest request, HttpServletResponse response){ System.out.println(username); try { request.getRequestDispatcher("/pages/success.jsp").forward(request, response);//请求转发 } catch (Exception e) { e.printStackTrace(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
/* * @Author CodeArts * @Description 使用原生的Servlet的重回定向 * @Date 17:44 2022/6/10 * @Param [request, response] * @return void **/ @RequestMapping("test03") public void test03(HttpServletRequest request, HttpServletResponse response){ try { response.sendRedirect("/pages/success.jsp");//重定向 } catch (Exception e) { e.printStackTrace(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
controller方法在提供了String类型的返回值之后,默认就是请求转发
forward:表示转发
@RequestMapping("test08") public String test08(){ return "forward:/pages/success.jsp"; }
- 1
- 2
- 3
- 4
redirect:表示重定向
@RequestMapping("test09") public String test09(){ return "redirect:/pages/success.jsp"; }
- 1
- 2
- 3
- 4
ModelAndView是springmvc中的一个可以用于传递值的对象
/* * @Author CodeArts * @Description springMVC提供了ModelAndView对象,其也可以进行传参 * @Date 18:30 2022/6/10 * @Param [] * @return org.springframework.web.servlet.ModelAndView **/ @RequestMapping("test04") public ModelAndView test04(){ ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("name","李四");//要携带到物理视图的数据 modelAndView.setViewName("success");//设置逻辑视图 return modelAndView; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
使用原生的request共享域
@RequestMapping("test05") public void test05(HttpServletRequest request,HttpServletResponse response){ request.setAttribute("name","wanger"); try { request.getRequestDispatcher("/pages/success.jsp").forward(request,response); } catch (ServletException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
扩展知识
四个域对象
1.PageContext:代表一个jsp页面,范围就是当前的jsp页面
2.Request:代表一次请求,范围就是一次请求中有效
3.Session:代表一次会话,范围就是一次会话(会话:从浏览器到开启到浏览器结束)
4.ServletContext:代表当前的web程序,只要项目在,范围就在
域对象的常用方法setAttribute(String key,Object value)//以键值对的形式存值
getAttribute(String key)//通过键来取值
removeAttribute(String key)//清除对应键的值
使用map集合往前台传值
@RequestMapping("test06") public String test06(Map<String,Object> map){ map.put("name","cq"); return "success"; }
- 1
- 2
- 3
- 4
- 5
springMVC提供了Model对象进行传值
@RequestMapping("test07") public String test07(Model model){ model.addAttribute("name","cc"); return "success"; }
- 1
- 2
- 3
- 4
- 5
同 4.4
index.jsp
<%-- Created by IntelliJ IDEA. User: 86186 Date: 2022/6/10 Time: 17:16 To change this template use File | Settings | File Templates. --%> <%--${pageContext.request.contextPath} 当前的项目名称--%> <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html> <head> <title>Title</title> </head> <body> <a href="${pageContext.request.contextPath}/hello/test01">搭建SpringMVC环境</a> <br> <%--<a href="${pageContext.request.contextPath}/hello/test02">原生的servlet的请求转发跳转</a><br>--%> <form action="${pageContext.request.contextPath}/hello/test02" method="post"> 用户名:<input type="text" name="username"> <input type="submit" value="提交"> </form> <a href="${pageContext.request.contextPath}/hello/test03">原生的servlet的重定向</a><br> <a href="${pageContext.request.contextPath}/hello/test04">ModelAndView对象传参</a><br> <hr> <a href="${pageContext.request.contextPath}/hello/test05">requert域传值</a><br> <a href="${pageContext.request.contextPath}/hello/test06">使用map传值</a><br> <a href="${pageContext.request.contextPath}/hello/test07">使用model对象传值</a><br> <a href="${pageContext.request.contextPath}/hello/test08">使用springmvc的转发</a><br> <a href="${pageContext.request.contextPath}/hello/test09">使用springmvc的重定向</a><br> </body> </html>
- 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
success.jsp
<%-- Created by IntelliJ IDEA. User: 86186 Date: 2022/6/10 Time: 17:26 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html> <head> <title>Title</title> </head> <body> <h1>hello SpringMVC</h1> <h2>${name}</h2> </body> </html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
引入解析json数据以及响应json数据的依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.0</version> </dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
controller方法
@RequestMapping("test11") //接收前台的json数据 //将后台的pojo以json的格式传递给前台 public @ResponseBody User test11(@RequestBody User user){ System.out.println(user); user.setId(2); user.setName("理工"); return user; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
注意:
@RequestBody:会自动的将前台传递的json格式的数据转换成pojo对象。
@ResponseBody:会自动的将pojo对象以json格式的数据传递给前台。
前台代码:
需要引入JQuery
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.js"></script> <input type="button" value="测试ajax请求json和响应json" id="testJson"/> <script> $(function(){ $("#testJson").click(function () { $.ajax({ type:"post", url:"${pageContext.request.contextPath}/hello/test11", contentType:"application/json;charset=UTF-8", data:'{"id":1,"name":"lisi"}', success:function (data) { alert(data); } }) }) }) </script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
pojo实体的User类
package com.cq.pojo; /** * @BelongsProject: SpringMVC-demo * @BelongsPackage: com.cq.pojo * @Author: CodeArts * @CreateTime: 2022-06-10 19:58 * @Description: TODO * @Version: 1.0 */ public class User { private Integer id; private String name; public User() { } public User(Integer id, String name) { this.id = id; this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
- 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
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
注意:User类的属性要和json数据的key值一致
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
@Controller @RequestMapping("file") public class FileController { @RequestMapping("upload") public String upload(HttpServletRequest request, MultipartFile upload){ //获取要上传的文件目录 String path = "G:\\demo"; //创建文件对象 File file = new File(path); if(!file.exists()){ //创建文件 file.mkdir(); } //获取文件上传的名称 String fileName = upload.getOriginalFilename(); try { upload.transferTo(new File(path,fileName)); } catch (IOException e) { e.printStackTrace(); } return "success"; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="UTF-8" /> <property name="maxUploadSize" value="2097152" /> </bean>
- 1
- 2
- 3
- 4
在web.xml中配置异常处理也
<error-page> <error-code>500</error-code> <location>/error/500.jsp</location> </error-page>
- 1
- 2
- 3
- 4
@ExceptionHandler(value = {Exception.class})//Exception:表示所有的异常,可以改变比如写算数异常等 public ModelAndView Exce(Exception e){ ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("msg",e.toString()); modelAndView.setViewName("error"); return modelAndView; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
package com.cq.handler; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @BelongsProject: SpringMVC-demo * @BelongsPackage: com.cq.handler * @Author: CodeArts * @CreateTime: 2022-06-10 21:24 * @Description: TODO * @Version: 1.0 */ @Component public class MyExce implements HandlerExceptionResolver { @Override //ModelAndView传参 public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("msg",e.toString()); modelAndView.setViewName("error"); return modelAndView; } }
- 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