后端开发 \ SpringBoot \ SpringMVC——使用RequestDispatcher.include()和HttpServletResponseWrapper动态获取jsp输出内容

SpringMVC——使用RequestDispatcher.include()和HttpServletResponseWrapper动态获取jsp输出内容

总点击60
简介:版权声明:本文为博主-阿飞(dufyun)-原创文章,未经博主允许可转载,但请标明出处,谢谢!https://blog.csdn.net/u010648555/article/details/64922835

版权声明:本文为博主-阿飞(dufyun)-原创文章,未经博主允许可转载,但请标明出处,谢谢! https://blog.csdn.net/u010648555/article/details/64922835

介绍本篇内容前,先抛出我遇到的问题或者说是需求!(精读阅读本篇可能花费您15分钟,略读需5分钟左右)

一:需求说明

有一个Controller有两个方法

第一个方法通过指定的路径和参数去渲染jsp内容,并返回html数据

第二个方法获取第一个方法中的html进行封装

现在的做法是在第二个方法通过发送Http请求获取数据,然后返回进行封装!

问题:

需要优化的是 不通过Http请求,第二个方法可以拿到第一个方法中的Html数据

二:简化例子(待优化的例子)

注:使用的SpringMVC框架,使用贴出视图解析器 配置文件

<!-- 默认的视图解析器 在上边的解析错误时使用 (默认使用html)- -->

<bean id="defaultViewResolver"

class="org.springframework.web.servlet.view.InternalResourceViewResolver"

p:order="2">

<property name="viewClass"

value="org.springframework.web.servlet.view.JstlView" />

<property name="contentType" value="text/html" />

<property name="prefix" value="/WEB-INF/jsp/" />

<property name="suffix" value=".jsp" />

</bean>

1.简化请求图示说明

简单说明:一个Controller中有三个方法,访问/index 返回html输出到页面,


浏览器页面显示内容为:

hello

url = http://blog.csdn.net/u010648555

world

url = http://blog.csdn.net/u010648555

/index中通过Http去请求/hello ,渲染hello.jsp 返回 hello.jsp 对应的html代码,去请求/world ,渲染world.jsp 返回 world..jsp 对应的html代码!

2.简化代码说明

(1):Java代码

package com.dufy.web;

import org.apache.commons.lang3.StringUtils;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.PrintWriter;

import java.net.HttpURLConnection;

import java.net.URL;

import java.net.URLEncoder;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

/**

* Created by dufy on 2017/3/21.

*/

@Controller

public class JspController {

/**

* 跳转到WEB_INF/jsp/welcome/index.jsp

* @param request

* @param response

* @return

*/

@RequestMapping("/index")

public String index(HttpServletRequest request,HttpServletResponse response) {

List<String> list = new ArrayList<String>();

list.add("hello");

list.add("wrold");

String commonUrl = "http://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath();

StringBuilder pageHtml = new StringBuilder();//使用StringBuilder拼接字符串效率高

if (list != null && list.size() > 0) {

for (String str : list

) {

StringBuffer paramsBuffer = new StringBuffer();//线程安全

paramsBuffer .append(str + "=" + str);//hello=hello world=world

//如果参数中url 需要使用URLEncoder

paramsBuffer .append("url"+ "=" + URLEncoder.encode("http://blog.csdn.net/u010648555"));

//使用post请求 ,后台获取每个接口方法对应生成的html

String urlStr = commonUrl +"/" + str + "?jsppath=" + str;//reqest url 这里会调用 /hello /world

String urlSourceResult = getURLSourcePost(urlStr,paramsBuffer.toString());

pageHtml.append(urlSourceResult);

}

}

request.setAttribute("pageHtml",pageHtml);

return "welcome/index";

}

/**

* 跳转到WEB_INF/jsp/welcome/hello.jsp

* @param request

* @param response

* @return

*/

@RequestMapping(value = "/hello",method = RequestMethod.POST)

public String hello(HttpServletRequest request,HttpServletResponse response){

String jsppath = request.getParameter("jsppath");

//处理一些业务逻辑

Map<String,String> params = new HashMap<>();

for (Object p : request.getParameterMap().keySet()) {

try {

String s = request.getParameter(p.toString());

params.put(p.toString(),s);

} catch (Exception e) {

e.printStackTrace();

}

}

request.setAttribute("params",params);

return "welcome/" + jsppath;

}

/**

* 跳转到WEB_INF/jsp/welcome/world.jsp

* @param request

* @param response

* @return

*/

@RequestMapping(value = "/wrold",method = RequestMethod.POST)

public String world(HttpServletRequest request,params);

return "welcome/" + jsppath;

}

/**

* 通过网站域名URL使用POST方式 获取该网站的源码

* @param url 请求的地址

* @param param 请求的参数

* @return 返回请求的结果

*/

private String getURLSourcePost(String url,String param) {

PrintWriter out = null;

BufferedReader in = null;

StringBuilder htmlResult = new StringBuilder();

try

{

URL realUrl = new URL(url);

//打开和URL之间的连接

HttpURLConnection conn = (HttpURLConnection)realUrl.openConnection();

//设置请求的方式

conn.setRequestMethod("POST");

conn.setConnectTimeout(5 * 1000);

//设置通用的请求属性

conn.setRequestProperty("accept","*/*");

conn.setRequestProperty("connection","Keep-Alive");

conn.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");

// charset=UTF-8以防止乱码!

conn.setRequestProperty("content-type","application/x-www-form-urlencoded; charset=UTF-8");

//发送POST请求必须设置如下两行

conn.setDoOutput(true);

conn.setDoInput(true);

//获取URLConnection对象对应的输出流

out = new PrintWriter(conn.getOutputStream());

//发送请求参数

out.print(param);

//flush输出流的缓冲

out.flush();

//定义BufferedReader输入流来读取URL的响应

in = new BufferedReader(

new InputStreamReader(conn.getInputStream()));

String line;

while ((line = in.readLine())!= null)

{

if(StringUtils.isNotBlank(line)){

htmlResult.append("n" + line);

}

}

}

catch(Exception e)

{

throw new RuntimeException("发送POST请求出现异常!",e);

}

//使用finally块来关闭输出流、输入流

finally

{

try

{

if (out != null)

{

out.close();

}

if (in != null)

{

in.close();

}

}

catch (IOException ex)

{

ex.printStackTrace();

}

}

return htmlResult.toString();

}

}

(2):对于的JSP页面

a:index.jsp

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<title>welcome index</title>

</head>

<body>

${pageHtml}

</body>

</html>

b:hello.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ page import="java.util.*" %>

<%@ page import="java.net.URLDecoder" %>

<%

Map<String,String> params = (Map<String,String>)request.getAttribute("params");

String hello = params.get("hello");

String url = URLDecoder.decode(params.get("url")); //URl解码

out.print("<p> " + hello + " </p>");

out.print("<p> " + url + " </p>");

%>

c:wrold.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ page import="java.util.*" %>

<%@ page import="java.net.URLDecoder" %>

<%

Map<String,String>)request.getAttribute("params");

String world = params.get("world");

String url = URLDecoder.decode(params.get("url")); //URl解码

out.print("<p> " + world+ " </p>");

out.print("<p> " + url + " </p>");

%>

3.待解决(优化)的问题说明


如图是在上面的基础上要进行的优化图!

简单说明,优化方法不使用Http请求,调用/index 可以直接拿到hello.jsp和world.jsp的内容,有什么好的办法能够动态获取JSP的内容呢???思考ing……………

注:可能有人会说,不就是输出

hello

url = http://blog.csdn.net/u010648555

world

url = http://blog.csdn.net/u010648555


需要搞的这么复杂吗。直接使用 在方法里面返回对应的字符串不就好了,对不起,我这里只是举个例子,我实际情况就是需要从jsp中渲染动态获取html字符串!


三:优化后代码(使用RequestDispatcher.include()和HttpServletResponseWrapper获取JSP内容)

上面举例子只是为了说明这个需求,下面贴出解决方案,若看博文的你有其他好的办法,可以添加左侧QQ群和我进行讨论!


实现原理简单说明:

在不跳转下访问目标jsp。就是利用RequestDispatcher.include(ServletRequest request,

ServletResponse response)。 该方法把RequestDispatcher指向的目标页面写到response中。

利用HttpServletResponseWrapper封装HttpServletResponse,使HttpServletResponse采用我们自己定

义的输入流(OutputStream)。

这样,我们就可以通过这个OutputStream得到目标jsp页面内容。

代码如下:

a:优化Controller

package com.dufy.web;

import com.aebiz.pub.util.JspToHtmlUtil;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.net.URLEncoder;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

/**

* Created by dufy on 2017/3/21.

*/

@Controller

public class JsptoHtmlController {

@RequestMapping("/index")

public String testJsp(HttpServletResponse response,HttpServletRequest request){

StringBuilder pageHtml = new StringBuilder();//使用StringBuilder拼接字符串效率高

List<String> list = new ArrayList<>();

list.add("/WEB-INF/jsp/welcome/hello.jsp");

list.add("/WEB-INF/jsp/welcome/world.jsp");

try {

if (list != null && list.size() > 0) {

for (String str : list

) {

Map<String,String> params = new HashMap<String,String>();

params.put(str,str);//put(hello,hello);

//如果参数中url 需要使用URLEncoder

params.put("url",URLEncoder.encode("http://blog.csdn.net/u010648555"));

request.setAttribute("params",params);

String jspOutput = JspToHtmlUtil.getJspOutput(str,request,response);

pageHtml.append(jspOutput);

}

}

} catch (Exception e) {

e.printStackTrace();

}

request.setAttribute("pageHtml",pageHtml);

return "welcome/index";

}

}

b:JspToHtmlUtil

package com.dufy.util;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

/**

* Created by dufy on 2017/3/22.

*/

public class JspToHtmlUtil {

/**

* 根据JSP所在的路径获取JSP的内容<br/>

* 在不跳转下访问目标jsp。就是利用RequestDispatcher.include(ServletRequest request,ServletResponse response)。

* 该方法把RequestDispatcher指向的目标页面写到response中。

* @param jspPath jsp路径

* @param request HttpServletRequest对象

* @param response HttpServletResponse对象

* @return

* @throws Exception

*/

public static String getJspOutput(String jspPath,HttpServletRequest request,HttpServletResponse response)

throws Exception

{

WrapperResponse wrapperResponse = new WrapperResponse(response);

request.getRequestDispatcher(jspPath).include(request,wrapperResponse);

return wrapperResponse.getContent();

}

}

注: RequestDispatcher接口中定义了两个方法forward和include,这两个方法都可以将第一个Servlet的请求转发给第二个Servlet,所不同的是forward方法在转发请求后,第一个Servlet的响应终止而由第二个Servlet负责响应输出,而include方法则是在转发请求并且第二个Servlet响应后将响应并入第一个Servlet中,仍然由第一个Servlet负责响应输出(注意调用include方法时两个Servlet的响应输出编码最好保持一致否则可能会出现乱码.

c:WrapperResponse .java

package com.dufy.util;

import org.apache.log4j.Logger;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpServletResponseWrapper;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.PrintWriter;

import java.io.UnsupportedEncodingException;

/**

* Created by dufy on 2017/3/21.<p/>

*利用HttpServletResponseWrapper封装HttpServletResponse,使HttpServletResponse采用我们自己定义的输入流(OutputStream)。<p/>

* 这样,我们就可以通过这个OutputStream得到目标jsp页面内容。

*/

public class WrapperResponse extends HttpServletResponseWrapper {

private Logger log = Logger.getLogger(WrapperResponse.class);

private MyPrintWriter tmpWriter;

private ByteArrayOutputStream output;

public WrapperResponse(HttpServletResponse httpServletResponse) {

super(httpServletResponse);

output = new ByteArrayOutputStream();;// 真正存储数据的返回流(保存数据返回的结果)

tmpWriter = new MyPrintWriter(output);

}

public String getContent() {

String str = "";

try {

//刷新该流的缓冲,详看java.io.Writer.flush()

tmpWriter.flush();

str = tmpWriter.getByteArrayOutputStream().toString("utf-8");

} catch (UnsupportedEncodingException e) {

e.printStackTrace();

log.error("不支持的编码异常,Unsupported Encoding Exception!");

}finally {

try {

output.close();

tmpWriter.close();

} catch (IOException e) {

e.printStackTrace();

log.error("释放资源异常,release resource error!");

}

}

return str;

}

//覆盖getWriter()方法,使用我们自己定义的Writer

public PrintWriter getWriter() throws IOException {

return tmpWriter;

}

public void close() throws IOException {

tmpWriter.close();

}

//自定义PrintWriter,为的是把response流写到自己指定的输入流当中

//而非默认的ServletOutputStream

private static class MyPrintWriter extends PrintWriter {

ByteArrayOutputStream myOutput;

//此即为存放response输入流的对象

public MyPrintWriter(ByteArrayOutputStream output) {

super(output);

myOutput = output;

}

public ByteArrayOutputStream getByteArrayOutputStream() {

return myOutput;

}

}

}

d:Jsp保持不变,和之前一样,不需要进行修改

四:why write this article,why not use http request ?

1:使用Http发起请求返回数据,这是最容易想到的方法,实现起来也简单!(只是感觉简单而已)


2:使用Http发起请求,涉及到网络层,此时就会有网络问题出现,网络策略有问题的话,可能导致无法访问数据,假若网络是通的,http请求也是有延时(延时在网络中存在很常见,可能导致代码因为超时出错)。


3:通过上面两点,也就是为什么不适用Http请求返回数据,也就是这篇博文为了实现(优化)功能需要而进行的思考!

4:建议 系统内部调用,最好不要用Http,如果可以的话,请使用rpc的方式,效果和使用起来更好!

五:参考文章

用HttpServletResponseWrapper 获取jsp输出内容:http://fjohnny.iteye.com/blog/713929

利用HttpServletResponseWrapper操作Response对象:http://www.itzk.com/b/1108/581313.shtml

RequestDispathcher中forward()和include()的区别:http://blog.csdn.net/huo2007201019/article/details/7584241


如果帅气(美丽)、睿智(聪颖),和我一样简单善良的你看到本篇博文中存在问题,请指出,我虚心接受你让我成长的批评,谢谢阅读!


祝你今天开心愉快!

欢迎访问我的csdn博客,我们一同成长!

不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!

博客首页:http://blog.csdn.net/u010648555

登录后自动展开

意见反馈 常见问题 官方微信 返回顶部