06-Response

  • 在Servlet中使用Response对象来完成响应
image-20240103090253847

1. 返回值void

package com.listao.mvc.controller;

@Controller
public class ResponseCtl {

    /**
     * 返回类型为void。@Controller必报错,@RestController不报错
     */
    @RequestMapping("/void")
    public void resp_void() {
        System.out.println("resp_void ===>>> ");
    }
}






 






  • void返回值,SpringMVC会去/servlet_war_exploded/void页面展示。如果没有配置视图解析器的前缀、后缀不会产生404
Type Exception Report

Message Circular view path [void]: would dispatch back to the current handler URL [/spring_mvc_war_exploded/void] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)

Description The server encountered an unexpected condition that prevented it from fulfilling the request.

Exception

javax.servlet.ServletException: Circular view path [void]: would dispatch back to the current handler URL [/spring_mvc_war_exploded/void] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
	org.springframework.web.servlet.view.InternalResourceView.prepareForRendering(InternalResourceView.java:210)
	org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:148)
	org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:316)
	org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1393)
	org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1138)
	org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1077)
	org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962)
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:529)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:623)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
	org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:94)
	org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
Note The full stack trace of the root cause is available in the server logs.








 


















2. 转发、重定向

1. Servlet实现

  • 单元方法的返回值类型设置void。因为使用Response对象在单元方法中直接对此次请求进行了响应,不需要再给DispatcherServlet返回值。形参声明HttpServletResponse,作为此次请求的Response对象
/**
 * Servlet请求转发
 */
@RequestMapping("/servlet/forward")
public void servlet_forward(HttpServletRequest req, HttpServletResponse resp) throws Exception {

    req.getRequestDispatcher("/forward.jsp").forward(req, resp);
}

/**
 * Servlet响应重定向
 */
@RequestMapping("/servlet/redirect")
public void servlet_redirect(HttpServletRequest req, HttpServletResponse resp) throws Exception {

    // `/spring_mvc_war_exploded`
    System.out.println("req.getContextPath() = " + req.getContextPath());

    // 相对路径:当前项目
    // 绝对路径:当前项目的上一级目录
    resp.sendRedirect(req.getContextPath() + "/redirect.jsp");
}






 













 

2. forward关键字

/*
 * 1. 返回字符串,通知DispatcherServlet跳转的路径
 * 2. 路径前`forward:`关键字,表示请求转发。可以省略不写
 */
@RequestMapping("/forward")
public String forward() {

    return "forward:/forward.jsp";
}







 

3. redirect关键字

/*
 * 1. 路径前`redirect:`关键字,表示重定向。不可以省略
 * 2. `/`表示当前项目下,不需要项目上下文路径
 */
@RequestMapping("/redirect")
public String redirect() {

    return "redirect:/redirect.jsp";
}







 

4. View视图

  • RedirectView最终实现链接的重定向,在renderMergedOutputModel()中完成,并且将数据保存到FlashMap中,在跳转后的链接中可以获取一些数据
@RequestMapping("/view")
public View view(HttpServletRequest req) {
    View view;
    // 请求转发
    view = new InternalResourceView("/forward.jsp");
    // 重定向
    view = new RedirectView(req.getContextPath() + "/redirect.jsp");
    return view;
}




 

 


5. ModelAndView

  • ModelAndView中:Model代表模型,View代表视图
@RequestMapping("/modelAndView")
public ModelAndView modelAndView(HttpServletRequest req) {
    ModelAndView mv = new ModelAndView();
    // 请求转发
    mv.setViewName("forward:/forward.jsp");
    mv.setView(new InternalResourceView("/forward.jsp"));
    // 重定向
    mv.setViewName("redirect:/redirect.jsp");
    mv.setView(new RedirectView(req.getContextPath() + "/redirect.jsp"));
    return mv;
}




 
 

 
 


3. 响应json

  • 通知DispatcherServlet单元方法的返回值不要按照请求转发、重定向处理,进行直接响应

1. jackson的jar

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.14.0-rc1</version>
</dependency>
import com.fasterxml.jackson.databind.ObjectMapper;


@Test
public void toJson() throws Exception {
    User pet = new User(1, "cat", "ooxx");
    ObjectMapper om = new ObjectMapper();
    String asString = om.writeValueAsString(pet);
    System.out.println(asString);
}

// {"uid":1,"uname":"cat","password":"ooxx"}

2. @ResponseBody

/*
 * @ResponseBody
 * 1. 方法返回值不在作为界面跳转依据,直接作为返回数据
 * 2. 返回数据自动使用 ObjectMapper 转换为 JSON
 */
@ResponseBody
@RequestMapping("/json")
public Pet json(Person person) {
    System.out.println("person = " + person);
    Pet pet = new Pet("Tom", "cat");
    return pet;
}





 






3. ajax回调函数

  • 无需再次使用eval函数将响应数据转换为json对象,直接使用即可
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta charset="UTF-8">
    <title>response.jsp</title>

    <script src="static/js/jquery.min.js"></script>
    <script>
        $(function () {
            $("#btn").click(function () {
                $.get("json", {pname: "晓明", page: "10"}, function (data) {
                    console.log(data.petName)
                    console.log(data.petType)
                }, "json")
            })
        })
    </script>
</head>
<body>
<input id="btn" type="button" value="JSON">
</body>
</html>










 
 
 
 








4. @RestController

  • 相当于@Controller + @ResponseBody两个注解结合
  • 返回json数据不需要在方法前面加@ResponseBody注解了。视图解析器也无法再解析jsp、html页面
@RestController
public class ResponseCtl {

    /*
     * @ResponseBody
     * 1. 方法返回值不在作为界面跳转依据,直接作为返回数据
     * 2. 返回数据自动使用ObjectMapper转换为JSON
     */
    @RequestMapping("/json")
    public Pet json(Person person) {
        System.out.println("person = " + person);
        Pet pet = new Pet("Tom", "cat");
        return pet;
    }
}
 














4. scope

名称作用域特点作用
Application(ServletContext)在应用程序中有效整个项目内有效解决了不同用户的数据共享问题
Session在当前会话中有效一次会话内有效解决了一个用户不同请求的数据共享问题
Request在当前请求中有效一次请求内解决了一次请求内的资源的数据共享问题
PageContext在当前页面有效

1. 传统方式传递数据

  1. http://localhost:8080/ssm_war_exploded/scope 接口访问,所有域有数据
  2. http://localhost:8080/ssm_war_exploded/scope.jsp 刷新页面访问,req无数据
  3. 重开浏览器,req、session无数据
/**
 * 1. HttpServletRequest, HttpSession可直接形参获取
 * 2. ServletContext不能形参直接获取,通过HttpServletRequest获取
 */
@RequestMapping("/http")
public String scope(HttpServletRequest req, HttpSession httpSession) {

    // 域获取
    HttpSession session = req.getSession();
    ServletContext servletContext = req.getServletContext();

    // 域中放入数据
    req.setAttribute("key", "reqMsg");
    session.setAttribute("key", "sessionMsg");
    servletContext.setAttribute("key", "appMsg");

    return "/scope.jsp";
    // return "redirect:/scope.jsp";
}








 
 


 
 
 




<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>scope.jsp</title>
</head>
<body>
<%-- 域中的数据 --%>
requestScope => ${requestScope.key} <br/>
sessionScope => ${sessionScope.key} <br/>
applicationScope => ${applicationScope.key} <br/>
<%-- 请求参数 --%>
param => ${param.key} <br/>
</body>
</html>

2. Model传递数据

  • 请求转发:数据置于RequestScope
  • 重定向:Model将域中数据,置于url请求参数(Scope中无数据)
/**
 * 1. 请求转发:RequestScope
 * 2. 重定向:Model将域中数据,置于url请求参数(Scope中无数据)
 */
@RequestMapping("/model")
public String model(Model model) {

    model.addAttribute("key", "modelMsg");
    System.out.println("model = " + model);

    return "/scope.jsp";

    // http://localhost:8080/spring_mvc_war_exploded/scope.jsp?modelKey=modelMsg
    return "redirect:/scope.jsp";
}

3. ModelAndView传递数据

/**
 * 1. model数据
 * 2. view视图
 */
@RequestMapping("/mv")
public ModelAndView modelAndView(ModelAndView mv) {
    System.out.println("mv = " + mv);

    Map<String, Object> model = mv.getModel();
    model.put("key", "mvValue");

    // mv.setViewName("/scope.jsp");

    // http://localhost:8080/spring_mvc_war_exploded/scope.jsp?mvKey=mvValue
    mv.setViewName("redirect:/scope.jsp");

    System.out.println("mv = " + mv);
    return mv;
}









 




 




4. FlashMap


  1. 形参 RedirectAttributes
  2. FlashMap attribute = (FlashMap) request.getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE);
  3. FlashMap outputFlashMap = RequestContextUtils.getOutputFlashMap(request);
  4. FlashMapManager flashMapManager = new SessionFlashMapManager();
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.servlet.support.SessionFlashMapManager;


String redirectJsp = "redirect:/scope.jsp";
String redirectPath = "redirect:/scope/redirect";

@RequestMapping("/flashMap1")
public String flashMap1(RedirectAttributes redirectAttributes) {
    Map<String, String> map = Map.of("mk1", "mv1", "mk2", "mv2");
    // 重定向,值放入session中,重定向到目标页面之后,从session中清除
    redirectAttributes.addFlashAttribute("key", map);
    // url中携带(k-v)
    redirectAttributes.addAttribute("key2", "value2");
    return redirectPath;
}

@RequestMapping("/redirect")
public String redirect(
        Model model,                                                // 效果和 ModelMap 一致
        ModelMap mm,                                                // 接收 addFlashAttribute() 中放入的数据
        @ModelAttribute("key") Map<String, String> key,             // 接收 addFlashAttribute() 中放入的数据
        @RequestParam(value = "key2", required = false) String key2 // addAttribute() 中放入的数据
) {
    System.out.println("model = " + model);
    System.out.println("mm = " + mm);
    Map<String, String> map = (Map<String, String>) mm.getAttribute("key");
    System.out.println("map = " + map);

    System.out.println("key = " + key);
    System.out.println("key2 = " + key2);

    return "/scope.jsp";
}

@RequestMapping("/flashMap2")
public String flashMap2(HttpServletRequest request) {
    Map<String, Map<String, String>> map = Map.of("key", Map.of("mk1", "mv1", "mk2", "mv2"));
    FlashMap attribute = (FlashMap) request.getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE);
    attribute.putAll(map);
    HttpSession session = request.getSession();
    session.setAttribute("key", "ooxx");  // 无法重定向传递

    return redirectPath;
}

@RequestMapping("/flashMap3")
public String flashMap3(HttpServletRequest request) {
    Map<String, Map<String, String>> map = Map.of("key", Map.of("mk1", "mv1", "mk2", "mv2"));
    FlashMap outputFlashMap = RequestContextUtils.getOutputFlashMap(request);
    outputFlashMap.putAll(map);

    // 重定向后获取数据
    // Map<String, ?> inputFlashMap = RequestContextUtils.getInputFlashMap(request);

    return redirectPath;
}

@RequestMapping("/flashMap4")
public String flashMap4(HttpServletRequest request, HttpServletResponse response) {
    FlashMap flashMap = new FlashMap();
    flashMap.put("key", Map.of("mk1", "mv1", "mk2", "mv2"));
    FlashMapManager flashMapManager = new SessionFlashMapManager();
    flashMapManager.saveOutputFlashMap(flashMap, request, response);

    return redirectPath;
}