• SpringMVC-2-Controller和RestFul风格


    SpringMVC --> 二、Controller和RestFul风格

    狂神说

    4. 控制器和RestFul风格

    4.1 控制器Controller

    • Controller负责提供访问应用程序的行为,通常通过接口定义注解定义两种方法实现;
    • Controller负责解析用户的请求,并将其转换为一个Model;
    • 在SpringMVC中,一个Controller类可以包含多个方法;
    • 在SpringMVC中,对于Controller的配置方法有很多种。

    4.2 实现Controller接口

    Controller接口在org.springframework.web.servlet.mvc包下,该接口只有一个方法:处理请求并返回一个模型与视图对象ModelAndView

    @FunctionalInterface
    public interface Controller {
        @Nullable
        ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
    }
    

    测试:

    1. 新建一个Moudle,和之前一样,添加web配置;

    2. 确定导入相关依赖,并刷新;

    3. 配置web.xml;

      
      <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
               version="4.0">
      
          <servlet>
              <servlet-name>springmvcservlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
      
              <init-param>
                  <param-name>contextConfigLocationparam-name>
                  <param-value>classpath:springmvc-servlet.xmlparam-value>
              init-param>
      
              <load-on-startup>1load-on-startup>
          servlet>
      
          <servlet-mapping>
              <servlet-name>springmvcservlet-name>
              <url-pattern>/url-pattern>
          servlet-mapping>
      web-app>
      
    4. 添加Spring MVC配置文件resources/springmvc-servlet.xml

      
      <beans xmlns="http://www.springframework.org/schema/beans"
             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">
          
          
          <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
                id="internalResourceViewResolver">
              
              <property name="prefix" value="/WEB-INF/jsp/"/>
              
              <property name="suffix" value=".jsp"/>
          bean>    
      
      beans>
      
    5. 编写一个Controller类,ControllerTest1.java;

      public class ControllerTest1 implements Controller {
          @Override
          public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
              ModelAndView mv = new ModelAndView();
              mv.addObject("msg","Hello ControllerTestOne");
              mv.setViewName("test");
              return mv;
          }
      }
      
    6. 在配置文件resources/springmvc-servlet.xml中注册请求的bean;

      <bean name="/t1" class="com.ano.controller.ControllerTest1">bean>
      
    7. 编写前端页面WEB-INF/jsp/test.jsp;

      <%--
        Created by IntelliJ IDEA.
        User: wangjiao
        Date: 2022/9/13
        Time: 20:22
        To change this template use File | Settings | File Templates.
      --%>
      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      
      
          Title
      
      
      ${msg}
      
      
      
    8. 配置Tomcat运行测试OK!
      在这里插入图片描述

    实现Controller接口定义控制器是较老的方法;

    缺点:一个控制器中只能有一个方法,如果多个方法则需要定义多个控制器,比较麻烦。

    4.3 使用注解@Controller

    • @Controller注解类型用于声明Spring类的实例是一个控制器;

    • Spring可以使用扫描机制来找到应用程序中基于注解的控制类;

    • 为了保证Spring能找到相应控制器,需要在配置文件中声明组件扫描,即在springmvc-servlet.xml中声明组件扫描:

      <context:component-scan base-package="com.ano.controller">context:component-scan>
      
    • 增加一个控制器类,使用注解实现,ControllerTest2.java

      @Controller
      public class ControllerTest2 {
      
          @RequestMapping("/t2")
          public String index(Model model) {
              model.addAttribute("msg","ControllerTest2");
              return "test";
          }
      }
      
    • 运行tomcat测试OK!

    在这里插入图片描述

    可以发现,两个请求都可以指向同一个视图,但是页面结果的结果是不一样的,可以看出视图是被复用的,而控制器与视图之间是弱偶合关系;

    注解方式是平时使用的最多的方式。

    4.4 @RequestMapping

    • 注解@RequestMapping用于映射url到控制器类或一个特定的处理程序方法;
    • 用于类上时,表示类中所有响应请求的方法都是以该地址作为父路径。
    1. 只注解在方法上,

      @Controller
      public class ControllerTest3 {
          @RequestMapping("/t3")
          public String test3(Model model) {
              model.addAttribute("msg","ControllerTest3");
              return "test";
          }
      }
      

      访问路径为:ip:port/项目名/t3 该测试示例的访问路径即 http://localhost:8080/t3

    2. 同时注解在类和方法上

      @Controller
      @RequestMapping("/admin")
      public class ControllerTest3 {
          @RequestMapping("/t3")
          public String test3(Model model) {
              model.addAttribute("msg","ControllerTest3");
              return "test";
          }
      }
      

      访问路径为:ip:port/项目名/admin/t3 该测试示例的访问路径即http://localhost:8080/admin/t3

    4.5 RestFul风格

    • Restful是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁、更有层次、更易于实现缓存等机制。

    • 功能:互联网所有的事物都可以被抽象为资源,一般使用POST、DELETE、PUT、GET等不同方法对资源进行添加、删除、修改、查询操作。

    • 传统方式操作资源:通过不同参数来实现不同的操作效果。方法比较单一,如下所示:

      http://127.0.0.1/item/queryUser.action?id=1   查询,GET 
      http://127.0.0.1/item/saveUser.action         新增,POST 
      http://127.0.0.1/item/updateUser.action       更新,POST 
      http://127.0.0.1/item/deleteUser.action?id=1  删除,GET或POST
      
    • 使用RestFul风格操作资源,可以通过不同的请求方式来实现不同的效果,请求地址可能相同,但功能可以不同,如下所示:

      http://127.0.0.1/item/1 查询,GET
      http://127.0.0.1/item   新增,POST
      http://127.0.0.1/item   更新,PUT
      http://127.0.0.1/item/1 删除,DELETE
      
    • 测试示例

      @Controller
      public class RestFulController {
          /**
           * 原生的URL:http://localhost:8080/add?a=1&b=2
           */
          @RequestMapping("/add")
          public String addNumTest1(int a, int b, Model model) {
              model.addAttribute("msg",a+b);
              return "test";
          }
      
          /**
           * RestFul方式1:请求方式 method = GET
           * @GetMapping() = RequestMapping("/add/{a}/{b}",method=requestMethod.GET)
           * http://localhost:8080/add/2/3
           */
          @GetMapping("/add/{a}/{b}")
          public String addNumTest2(@PathVariable int a, @PathVariable int b, Model model) {
              model.addAttribute("msg",a+b);
              return "test";
          }
      
          /**
           * RestFul方式2:请求方式 method = POST
           * 复用了相同的URL
           * http://localhost:8080/add/2/3
           */
          @PostMapping("/add/{a}/{b}")
          public String addNumTest3(@PathVariable int a, @PathVariable int b, Model model) {
              model.addAttribute("msg",a+b);
              return "test";
          }
      }
      

    5. 重定向与转发(结果跳转方式)

    5.1 ModelAndView

    • 通过设置ModelAndView对象,并根据view的名称和视图解析器跳转到指定的页面;

    • 页面:{视图解析器前缀} + viewName +{视图解析器后缀}

      
      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
            id="internalResourceViewResolver">
          
          <property name="prefix" value="/WEB-INF/jsp/"/>
          
          <property name="suffix" value=".jsp"/>
      bean>
      
    • Controller类通过实现Controller接口的方式实现

      public class ControllerTest1 implements Controller {
          @Override
          public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
              ModelAndView mv = new ModelAndView();
              mv.addObject("msg","Hello ControllerTestOne");
              mv.setViewName("test");
      
              return mv;
          }
      }
      

    5.2 ServletAPI

    • 通过设置ServletAPI的方式,不需要视图解析器。

    • 示例:

      @Controller
      public class ResultServletApi {
          /**
           * 输出
           */
          @RequestMapping("/result/t1")
          public void test1(HttpServletRequest request, HttpServletResponse response) throws IOException {
              response.getWriter().print("Hello, SpringMVC result by ServletAPI");
          }
          /**
           * 重定向
           */
          @RequestMapping("/result/t2")
          public void test2(HttpServletRequest request, HttpServletResponse response) throws IOException {
              response.sendRedirect("/index.jsp");
          }
          /**
           * 转发
           */
          @RequestMapping("/result/t3")
          public void test3(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
              request.setAttribute("msg","result/t3");
              request.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(request,response);
          }
      }
      
    • 通过HttpServletResponse进行输出

    • 通过HttpServletResponse实现重定向

    • 通过HttpServletRequest实现转发

    5.3 SpringMVC

    无需视图解析器的情况

    • 默认为转发,forward可加可不加;
    • 重定向redirect必须加上。
    @Controller
    public class ResultSpringMvc {
        /**
         * 转发
         */
        @RequestMapping("/resM/t1")
        public String test1(Model model) {
            model.addAttribute("msg","/resM/t1");
            return "/WEB-INF/jsp/test.jsp";
        }
        /**
         * 转发
         */
        @RequestMapping("/resM/t2")
        public String test2(Model model) {
            model.addAttribute("msg","/resM/t2");
            return "forward:/WEB-INF/jsp/test.jsp";
        }
        /**
         * 重定向
         */
        @RequestMapping("/resM/t3")
        public String test2() {
            return "redirect:/index.jsp";
        }
    }
    

    有视图解析器的情况

    • 默认为转发,forward不可以加;
    • 重定向redirect必须加上。
    @Controller
    public class ResultSpringMvc2 {
        /**
         * 转发
         */
        @RequestMapping("/resM2/t1")
        public String test1(Model model) {
            model.addAttribute("msg","resM2/t1");
            return "test";
        }
    
        /**
         * 重定向
         */
        @RequestMapping("/resM2/t2")
        public String test2() {
            return "redirect:/index.jsp";
        }
    }
    

    6. 接受请求参数及数据回显

    6.1 数据处理三种情况

    • 提交的域名称和处理方法的参数名一致;
    • 提交的域名称和处理方法的参数名不一致;
    • 提交的数据是一个对象;

    测试程序

    @Controller
    public class DataHandle {
        /**
         * 提交的域名称和处理方法的参数名一致
         * http://localhost:8080/test?name=ano
         */
        @RequestMapping("/test")
        public String test(String name, Model model){
            model.addAttribute("msg",name);
            return "test";
        }
    
        /**
         * 提交的域名称和处理方法的参数名不一致
         * http://localhost:8080/test2?username=ano2
         */
        @RequestMapping("/test2")
        public String test2(@RequestParam("username") String name, Model model){
            model.addAttribute("msg",name);
            return "test";
        }
        
        /**
         * 提交的参数是一个对象时,前端传的参数必须和对象名称一致,否则会null
         * http://localhost:8080/user?name=ano&id=1001&age=18
         * @param user
         * @param model
         * @return
         */
        @RequestMapping("/user")
        public String userInfo(User user, Model model) {
            System.out.println(user);
            model.addAttribute("msg",user.toString());
            return "test";
        }
    }
    

    实体类User

    package com.ano.pojo;
    
    /**
     * @author wangjiao
     * @version 1.0
     * @date 2022/9/21 17:45
     */
    public class User {
        private int id;
        private String name;
        private int age;
    
        public User() {
        }
    
        public User(int id, String name, int age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    

    6.2 数据回显到前端三种方式

    • 第一种方式:ModelAndView

    • 第二种方式:Model

    • 第三种方式:ModelMap

    简单对比:

    ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转;

    Model 只有寥寥几个方法只适合用于储存数据,简化了对于Model对象的操作和理解;

    ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性。

    7. 乱码问题

    一个简单的测试程序

    1. 在首页编写一个表单index.jsp

    2. 编写响应的Controller类及处理方法

      @RequestMapping("/encoding/test")
      public String test(String name, Model model) {
          model.addAttribute("msg",name);
          return "test";
      }
      
    3. localhost:8080进入首页表单输入中文测试,发现会乱码

    在这里插入图片描述
    在这里插入图片描述

    一般通过配置过滤器解决乱码问题。

    7.1 自定义过滤器解决乱码

    1. 编写过滤器类EncodingFilter.java;
    public class EncodingFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            servletRequest.setCharacterEncoding("utf-8");
            servletResponse.setCharacterEncoding("utf-8");
            filterChain.doFilter(servletRequest,servletResponse);
        }
    
        @Override
        public void destroy() {
    
        }
    }
    
    
    1. 在web.xml种配置乱码过滤器EncodingFilter;
    
    <filter>
        <filter-name>encodingfilter-name>
        <filter-class>com.ano.filter.EncodingFilterfilter-class>
    filter>
    <filter-mapping>
        <filter-name>encodingfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>
    
    1. 再次测试,成功解决乱码。

    在这里插入图片描述

    7.2 Spring框架提供的过滤器解决乱码

    1. 配置web.xml即可
    
    <filter>
        <filter-name>encodingfilter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
        <init-param>
            <param-name>encodingparam-name>
            <param-value>utf-8param-value>
        init-param>
    filter>
    <filter-mapping>
        <filter-name>encodingfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>
    
    1. 再次测试,成功解决乱码。

    一般情况下,Spring框架默认的这个过滤器就已经能够很好的解决乱码问题,但是有一些极端情况,Spring框架提供的过滤器对GET请求的支持不友好,处理方法如下:

    • 修改Tomcat配置文件,设置编码
    <Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    
    • 自定义过滤器
    package com.ano.filter;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.util.Map;
    
    /**
     * 通用乱码过滤器:解决get和post请求全部乱码的过滤器
     */
    public class GenericEncodingFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
            // 处理response的字符编码
            HttpServletResponse myResponse=(HttpServletResponse) response;
            myResponse.setContentType("text/html;charset=UTF-8");
            // 转型为与协议相关对象
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            // 对request包装增强
            HttpServletRequest myrequest = new MyRequest(httpServletRequest);
            filterChain.doFilter(myrequest, response);
        }
    
        @Override
        public void destroy() {
        }
    }
    /**
     * 自定义request对象,HttpServletRequest的包装类
     */
    class MyRequest extends HttpServletRequestWrapper {
    
        private HttpServletRequest request;
        /**
         * 是否编码的标记
         */
        private boolean hasEncode;
        /**
         * 定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
         * @param request
         */
        public MyRequest(HttpServletRequest request) {
            // super必须写
            super(request);
            this.request = request;
        }
        /**
         * 对需要增强方法 进行覆盖
         * @return
         */
        @Override
        public Map getParameterMap() {
            // 先获得请求方式
            String method = request.getMethod();
            if (method.equalsIgnoreCase("post")) {
                // post请求
                try {
                    // 处理post乱码
                    request.setCharacterEncoding("utf-8");
                    return request.getParameterMap();
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            } else if (method.equalsIgnoreCase("get")) {
                // get请求
                Map<String, String[]> parameterMap = request.getParameterMap();
                // 确保get手动编码逻辑只运行一次
                if (!hasEncode) {
                    for (String parameterName : parameterMap.keySet()) {
                        String[] values = parameterMap.get(parameterName);
                        if (values != null) {
                            for (int i = 0; i < values.length; i++) {
                                try {
                                    // 处理get乱码
                                    values[i] = new String(values[i]
                                            .getBytes("ISO-8859-1"), "utf-8");
                                } catch (UnsupportedEncodingException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                    hasEncode = true;
                }
                return parameterMap;
            }
            return super.getParameterMap();
        }
    
        /**
         * 取一个值
         * @param name
         * @return
         */
        @Override
        public String getParameter(String name) {
            Map<String, String[]> parameterMap = getParameterMap();
            String[] values = parameterMap.get(name);
            if (values == null) {
                return null;
            }
            // 取回参数的第一个值
            return values[0];
        }
    
        /**
         * 取所有值
         * @param name
         * @return
         */
        @Override
        public String[] getParameterValues(String name) {
            Map<String, String[]> parameterMap = getParameterMap();
            String[] values = parameterMap.get(name);
            return values;
        }
    }
    
    
    • 然后在web.xml中配置这个通用过滤器GenericEncodingFilter即可!
  • 相关阅读:
    政府指导89元保330万 “聊惠保”2024年度正式上线!
    某游戏公司Java面试八股文总结
    JDBC-day02(使用PreparedStatement实现CRUD操作)
    flink 1.15.1集群安装部署及测试
    Golang 并发机制 CSP模型
    杀戮空间2游戏开服架设好后怎么查找自己服务器
    强化学习 多臂赌博机
    LeetCode //C - 129. Sum Root to Leaf Numbers
    BOM操作——window对象(一)
    SpringMVC03、HelloSpring
  • 原文地址:https://blog.csdn.net/baidu_38126306/article/details/126979233