• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

SpringMVC-2-Controller和RestFul风格

武飞扬头像
西西ANO
帮助1

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;

    <?xml version="1.0" encoding="UTF-8"?>
    <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>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
    
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    
    学新通
  4. 添加Spring MVC配置文件resources/springmvc-servlet.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"
           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" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>
    
    学新通
  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

    <form action="/encoding/test" method="post">
        <input type="text" name="name">
        <input type="submit">
    </form>
    
  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>encoding</filter-name>
    <filter-class>com.ano.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>encoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  1. 再次测试,成功解决乱码。

学新通

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

  1. 配置web.xml即可
<!--配置Spring框架提供的过滤器解决乱码-->
<filter>
    <filter-name>encoding</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>
</filter>
<filter-mapping>
    <filter-name>encoding</filter-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即可!

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhhagffe
系列文章
更多 icon
同类精品
更多 icon
继续加载