springboot 跳转到网页上的两种实现方式(转发与重定向详细对比)

1.情景展示

虽然现在流行的是前后端分离,后端开发与前端往往只进行数据交互,不需要参与对网页跳转的控制及网页内容的开发。

但是,由服务器(后端)跳转到客户端(浏览器)或者从A服务器跳到B服务器是一项基本的能力。

在项目开发中,真正遇到的时候,该如何实现?

哪种实现方式更好?

2.具体分析

无论是springboot,SpringMVC,其本质都是Servlet。

由服务器跳转到网页上,有且只有两种方式:

一是转发。

二是重定向。

二者具体有什么区别,文章第4部分会讲。

3.解决方案

前提条件:

控制器的注解,需要使用@Controller,不可使用@RestController

@RestController相当于@Controller@ResponseBody合在一起的作用,如果使用@RestController注解Controller层的话,则返回的是return里面的内容,无法返回指定的页面。

在配置文件application.properties当中增加视图解析器配置。

#配置视图解析器
#路径后缀
spring.mvc.view.suffix=.jsp

方式一:使用SpringMVC实现

关键点:创建ModelAndView对象

在springboot出来之前,需要我们自己手动创建视图对象(ModelAndView)。

springboot只不过是帮我们做了这件事,所以,我们即使在springboot项目当中,也可以仍旧采用springMVC这种笨方法实现。

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
@ApiOperation("404页面")
@GetMapping("/error404Page")
public ModelAndView error404() {
    ModelAndView mv = new ModelAndView();
    mv.setViewName("/WEB-INF/jsp/error/404");
    return mv;
}

请求最终会去WEB-INF目录下,按照上述路径找到404.jsp,并将网页内容响应给浏览器。

方式二:使用springboot实现

@GetMapping("/404")
public String demo404() {
    return "/WEB-INF/jsp/error/404";
}

无论是SpringMVC还是在springboot当中,要响应的网页,默认使用的是转发:forward,该关键词可以省略

那如果我们加上forward会发生什么呢?

@GetMapping("/404")
public String demo404() {
    return "forward:/WEB-INF/jsp/error/404";
}

我们可以看到:

在加上关键词:"forward: "之后,服务器找不到这个网页了,为什么?

原因就在于,当我们在将要转发的页面,显式地声明"forward: "之后,在配置文件里面设置的视图解析器将会失效(包括路径前缀和后缀)。

也就是说,显式地声明"forward: "之后,配置文件关于视图解析器的设置对于它而言作废。

我们必须将要转发的界面的完整路径写出来(使用关键词:"redirect: "效果与关键词forward一样)

当然了,对于不使用forward关键字的转发路径,依然生效。

针对这一特性,我们可以这样做:

对于绝大多数相同的跳转路径前缀和后缀名,我们可以在配置文件当中配好。

对于少部分路径不同或者或者后缀名不同的转发界面,我们就可以使用forward的关键字来覆盖配置文件当中的配置。

这样一来,一举两得,互不影响。

方式三:通过原生Servlet实现

关键点:使用ServletReques和ServletResponse对象

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@GetMapping("/404")
public void demo404(HttpServletRequest request, HttpServletResponse response) {
    try {
        request.getRequestDispatcher("/WEB-INF/jsp/error/404.jsp").forward(request, response);
    } catch (ServletException e) {
        throw new RuntimeException(e);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

4.转发与重定向

二者有何区别,我们结合springboot通过示例来一起认识一下。

本质:转发是服务器行为,重定向是客户端行为。

第一点:地址栏变化与请求次数

forward是服务器请求资源,服务器直接访问目标地址的URL(return url),把那个URL的响应内容读取过来,然后把这些内容再发给浏览器。

(或者理解为:服务器根据文件路径读取网页,并将网页内容响应给浏览器)

浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址。(转发,浏览器只发送一次请求)

@GetMapping("/demo")
public String demo() {
    return "/demo";
}

结果展示:

浏览器只发送一次请求,地址栏URL不变。

redirect是服务端根据逻辑,发送一个状态码(302)和新地址,浏览器去请求新地址,所以地址栏显示的是新的URL。(重定向,浏览器发送两次请求)

服务器根据此请求寻找资源并发送给客户。(浏览器发送二次请求,地址栏会发送改变,暴露请求真实地址。)

@GetMapping("/demo")
public String demo() {
    return "redirect:/demo.jsp";
}

结果展示:

浏览器发送两次请求,地址栏URL为第二次请求地址。

第一次请求状态为302。

请求地址,http://192.168.57.148:8099/PHRPLAT/zhouKou/demo,返回状态码为302;

并且返回重定向地址,即:响应头(Response Headers)里面的Location的值(http://192.168.57.148:8099/PHRPLAT/demo.jsp)就是要重定向的地址。

当浏览器检测到响应状态码为302,会自动去响应头拿到需要重新访问的请求地址,然后再次发送请求到服务器。

并且后退按钮可用。

点击后退按钮,地址栏内容为空。(没有什么卵用,拿不到第一次请求的地址)

虽然浏览器历史记录里面找不到,但是,我们可以从地址栏中拿到。

所以,从效率上来讲,转发要优于重定向。

第二点:访问限制不同

转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去(转发只能转发到自己的web应用内)。

假如,我们用转发调转到外部网站(非本Web服务器),会发生什么?

@GetMapping("/test")
public String test() {
    return "http://xxx.yy.m/health_m/a.html";
}

最终返回给页面的是404。

测试URL为:

http://192.168.57.148:8099/PHRPLAT/zhouKou/test

最后,经过打断点一步步走下去,发现:

转发,给请求路径前面加了"/请求上下文/请求路径/",这样一来,请求当然会404啦。

这就是,转发为什么不能跳转到外部网站的原因。 

重定向可以访问自己web应用以外的资源(在javaWeb中,重定向无法访问WEB-INF目录下的文件)。

@GetMapping("/test")
public String test() {
    return "redirect:https://www.baidu.com";
}

请求可以正常跳转

由此可见,当我们想要访问非本程序网址时,return URL的URL直接返回URL的全路径即可,即:以http或https开头。

当重定向在内部处理的时候,与转发不同:

检测到是以http或者https开头时,就不会再给URL前面添加项目路径啦。

那,如果重定向到WEB-INF目录下呢,会发生什么?

@GetMapping("/test")
public String test() {
    return "redirect:/WEB-INF/jsp/error/404.jsp";
}

会出现404。

这是为什么呢?

因为在javaWeb开发当中,设计者出于安全考虑,tomcat是被禁止访问WEB-INF目录的,而我们的程序又依赖于tomcat容器,所以非法请求自然就会被拦截啦。

第三点:数据传输

转发可以携带数据,因为:

请求响应调用者和被调用者之间是同一个request对象和response对象。

如果你用的是jsp开发前端页面,我们就能拿到这些数据。

在重定向的过程中,传输的信息会被丢失。

重定向调用者和被调用者属于两个独立访问请求和响应过程 。

第四点:页面字符集

经转发响应的页面的字符集,不受页面本身的字符集控制,而是取决于转发时所设置的字符集,默认为:ISO-8859-1。

换句话说,即使你的JSP或者HTML文件里面设置的字符集是UTF-8,也无济于事,最终是由服务器告诉浏览器以哪种字符集来解析响应网页。

(如果页面由服务器转发而来,则页面的字符集由转发决定,默认返回的是ISO-8859-1,所以需要我们手动设定页面的字符集为UTF-8,否则页面当中的中文将会显示乱码。)

所以,为了保证响应内容不乱码,我们需要指定响应内容的字符集。

response.setCharacterEncoding("utf- 8");

经过我的测试及实证,发现springboot在响应内容时,设置的字符集是:UTF-8。

这样一来,这一步我们就可以省略啦。

重定向的网页的字符集由网页里面设置的字符集决定,与重定向无关。(当访问页是通过服务器重定向到指定的界面时,字符集由HTML页面本身的声明的字符集决定!)

response.sendRedirect("success.jsp");

就算,重定向设置返回数据的编码集,也无效,因为:不是同一个Response对象。

// 对于重定向,此设置无效
//response.setCharacterEncoding("utf-8");

关于转发与重定向,网页字符集的自主权实验及论证,见文末推荐文章。

5.拓展

关于跳转路径前是否要加"/"的分情况讨论

先看转发

初始请求:

http://192.168.57.148:8099/PHRPLAT/zhouKou/demo

上面的例子证明了,无论是转发,还是重定向,return URL的路径前面都带着"/",那如果不带"/",会发生什么呢?

不带"/"

@GetMapping("/demo")
public String demo() {
    return "demo";
}

浏览器显示结果如下:

 为什么会出现404?

原因是:

http://192.168.57.148:8099/PHRPLAT/zhouKou/demo的请求路径是:

项目名后面(/zhouKou/demo)一直到最后一级的前一级(/zhouKou/),也就是demo的上一级,即:/zhouKou。

由于转发的页面没有加"/",其转发页面的完整路径就变成了:http://192.168.57.148:8099/PHRPLAT/zhouKou/demo.jsp

而demo.jsp所在路径是:

/demo.jsp,其完整访问路径是:http://192.168.57.148:8099/PHRPLAT/demo.jsp

由于转发的页面没有加"/",其转发页面的完整路径就变成了:http://192.168.57.148:8099/PHRPLAT/zhouKou/demo.jsp

这句话,对不对呢?我们来验证一下。

把demo.jsp移到zhouKou目录下。

再来访问一次。

事实果真如此。

再看重定向

我们把demo.jsp还放到项目的根目录下。

@GetMapping("/demo")
public String demo() {
    return "redirect:demo";
}

我们可以看到:

实际重定向的页面路径是:

http://192.168.57.148:8099/PHRPLAT/zhouKou/demo.jsp

应该访问的路径是:

http://192.168.57.148:8099/PHRPLAT/demo.jsp

小结:

无论是转发还是重定向,在return URL时,路径前面最好加"/",这样一来,我们直接设置好要跳转的完整路径就可以啦。

当请求路径与页面所在路径完全一致时,我们可以不在路径前面"/",照样可以正常跳转到网页上面,不会导致网页404。

当return URL的URL是WEB-INF目录下面时,只能使用转发,不能使用重定向。

当return URL的URL是非本系统内的请求时,只能使用重定向,不能使用转发。

 

写在最后

  哪位大佬如若发现文章存在纰漏之处或需要补充更多内容,欢迎留言!!!

 相关推荐:

posted @ 2023-05-25 17:06  Marydon  阅读(9137)  评论(0编辑  收藏  举报