【问题标题】:Struts2 ActionContext and Response for chaining actionsStruts2 ActionContext 和 Response 用于链接动作
【发布时间】:2013-04-28 16:47:01
【问题描述】:

我有一个关于 struts2 链接操作的非常复杂的问题,提前感谢您耐心阅读我的问题。我会尽量描述清楚。

下面是我的struts.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <constant name="struts.enable.DynamicMethodInvocation" value="false" />
    <constant name="struts.enable.SlashesInActionNames" value="true" />
    <constant name="struts.devMode" value="false" />


    <package name="default" extends="struts-default" namespace="/">
        <action name="test" class="com.bv.test.TestAction1" >
            <result name="success" type="chain">y</result>
        </action>

        <action name="x">
            <result name="success">/index.jsp</result>
        </action>

        <action name="y" class="com.bv.test.TestAction2">
            <result name="success">/index.jsp</result>
        </action>
    </package>
</struts>

我的逻辑是这样的: 访问/myapp/test时,TestAction1会处理请求; 在 TestAction1 中,我像这样“包含”动作 x(我的配置中的第二个动作):

ResponseImpl myResponse = new ResponseImpl(response);
RequestDispatcher rd = request.getRequestDispatcher("/x.action");
rd.include(request, myResponse); 

重要的是我在包含“x.action”时使用了自定义的 ResponseIml。

包含后,我返回“成功”,因此结果链接到操作 y(我的配置中的第三个操作);
最后,TestAction2 继续处理请求,它会进入成功结果,并且应该渲染jsp,但是我看到的是一个空白页面

jsp文件很简单: index.jsp

<h1>Test!</h1>

我的问题/谜题是:

  1. 在 TestAction1 中,如果我从 ServletActionContext 获得响应,我 在包括之前和之后得到不同的;前 包括是默认响应,但包括后我得到了 我的自定义 ResponseImpl 的实例;我希望得到同样的结果 一:即:默认响应;
  2. 在 TestAction2 中,我得到了来自 ServletActionContext 的响应,我得到了什么 是我自定义的 ResponseIml 的实例。这是我最重要的事情,我想我应该在这里得到一个默认的响应实例,即:org.apache.catalina.connector.Response,我在JBoss上运行;
  3. 我在 TestAction2 中得到了不同的 ActionContext(与 我在 TestAction1) 中得到的 ActionContext。

这个问题真的把我逼疯了,我花了好几天的时间。
任何建议将不胜感激!
太感谢了!!

我的代码:

TestAction1:

public class TestAction1 {
  public String execute() {
    ActionContext ac = ActionContext.getContext();
    System.out.println("Before including: the action context is : " + ac);
    HttpServletRequest request = ServletActionContext.getRequest();
    HttpServletResponse response = ServletActionContext.getResponse();
    System.out.println("Before including: the response is : " + response);

    ResponseImpl myResponse = new ResponseImpl(response);
    RequestDispatcher rd = request.getRequestDispatcher("/x.action");
    try {
      rd.include(request, myResponse); 
      String s = myResponse.getOutput();  
      System.out.println("get from response: " + s);
    }
    catch (Exception e) {
      e.printStackTrace();
    }

    ac = ActionContext.getContext();
    System.out.println("After including : the action context is : " + ac);
    response = ServletActionContext.getResponse();
    System.out.println("After including : the response is : " + response);
    return "success";
  }
}

ResponseImpl:

import java.util.Locale;
import java.io.StringWriter;
import java.io.PrintWriter;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.Cookie;
import javax.servlet.jsp.JspWriter;

/**
 * 
 * 
 */
public class ResponseImpl extends HttpServletResponseWrapper  {

  //=========================================================
  // Private fields.
  //=========================================================

  private ServletOutputStream outputStream = null;

  private ByteArrayOutputStream byteArrayOutputStream = null;

  private StringWriter stringWriter = null;

  private PrintWriter printWriter = null;

  private HttpServletResponse _response = null;

  private String contentType= "text/html";

  private String encoding = "UTF-8";

  /**
   *
   */
  class ServletOutputStream extends javax.servlet.ServletOutputStream {

    private OutputStream outputStream = null;

    /**
     *
     */
    ServletOutputStream(ByteArrayOutputStream outputStream) {
      super();
      this.outputStream = outputStream;
    }

    /**
     *
     */
    public void write(int b) throws IOException {
      this.outputStream.write(b);
    }
  }

  //=========================================================
  // Public constructors and methods.
  //=========================================================

  /**
   *
   */
  public ResponseImpl(HttpServletResponse response) {
    super(response);
    this._response = response;
  }

  /**
   *
   */
  public String getOutput() {
    if (this.stringWriter != null) {
      return this.stringWriter.toString();
    }

    if (this.byteArrayOutputStream != null) {
      try {
        return this.byteArrayOutputStream.toString(this.encoding);
      }
      catch (UnsupportedEncodingException e) {
      }
      return this.byteArrayOutputStream.toString();
    }

    return null;
  }

  //=========================================================
  // Implements HttpServletResponse interface.
  //=========================================================

  public void addCookie(Cookie cookie) {
  }

  public void addDateHeader(String name, long date) {
  }

  public void addHeader(String name, String value) {
  }

  public void addIntHeader(String name, int value) {
  }

  public boolean containsHeader(String name) {
    return false;
  }

  public String encodeRedirectURL(String url) {
    if (null != this._response) {
      url = this._response.encodeRedirectURL(url);
    }
    return url;
  }

  public String encodeURL(String url) {
    if (null != this._response) {
      url = this._response.encodeURL(url);
    }
    return url;
  }

  public void sendError(int sc) {
  }

  public void sendError(int sc, String msg) {
  }

  public void sendRedirect(String location) {
  }

  public void setDateHeader(String name, long date) {
  }

  public void setHeader(String name, String value) {
  }

  public void setIntHeader(String name, int value) {
  }

  public void setStatus(int sc) {
  }

  public void resetBuffer() {
  }

  //=========================================================
  // Implements deprecated HttpServletResponse methods.
  //=========================================================

  public void setStatus(int sc, String sm) {
  }

  //=========================================================
  // Implements deprecated HttpServletResponse methods.
  //=========================================================

  public String encodeRedirectUrl(String url) {
    return encodeRedirectURL(url);
  }

  public String encodeUrl(String url) {
    return encodeURL(url);
  }

  //=========================================================
  // Implements ServletResponse interface.
  //=========================================================

  public void flushBuffer() {
  }

  public int getBufferSize() {
    return 0;
  }

  public String getCharacterEncoding() {
    return this.encoding;
  }

  public String getContentType() {
    return this.contentType;
  }

  public Locale getLocale() {
    return null;
  }

  public javax.servlet.ServletOutputStream getOutputStream() {
    if (this.outputStream == null) {
      this.byteArrayOutputStream = new ByteArrayOutputStream();
      this.outputStream =
        new ServletOutputStream(this.byteArrayOutputStream);
    }
    return this.outputStream;
  }

  public PrintWriter getWriter() {
    if (this.printWriter == null) {
      this.stringWriter = new StringWriter();
      this.printWriter = new PrintWriter(this.stringWriter);
    }
    return this.printWriter;
  }

  public boolean isCommitted() {
    return true;
  }

  public void reset() {
  }

  public void setBufferSize(int size) {
  }

  public void setCharacterEncoding(String charset) {
  }

  public void setContentLength(int len) {
  }

  public void setContentType(String type) {
    int needle = type.indexOf(";");
    if (-1 == needle) {
      this.contentType = type;
    }
    else {
      this.contentType = type.substring(0, needle);
      String pattern = "charset=";
      int index = type.indexOf(pattern, needle);
      if (-1 != index) {
        this.encoding = type.substring(index + pattern.length());
      }
    }
  }

  public void setLocale(Locale locale) {
  }
}

TestAction2:

public class TestAction2 {

  public String execute() {
    ActionContext ac = ActionContext.getContext();
    System.out.println("In TestAction 2 : the action context is : " + ac);

    HttpServletResponse response = ServletActionContext.getResponse();
    System.out.println("In TestAction 2 : the response is : " + response);
    return "success";
  }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Struts2 Application</display-name>

     <filter>
        <filter-name>struts2</filter-name>
        <filter-class>
            org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>INCLUDE</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>
</web-app>

这是我的调试信息。

  • 在包括之前:动作上下文是 :com.opensymphony.xwork2.ActionContext@c639ce
  • 在包括之前:响应是: org.apache.catalina.connector.ResponseFacade@8b677f
  • 从回复中获取:&lt;h1&gt;Test!&lt;/h1&gt;
  • 包括后:动作上下文是: com.opensymphony.xwork2.ActionContext@2445d7
  • 包含后:响应为:com.bv.test.ResponseImpl@165547d
  • 在 TestAction 2 中:动作上下文是 :com.opensymphony.xwork2.ActionContext@19478c7
  • 在 TestAction 2 中:响应为:com.bv.test.ResponseImpl@165547d

所以,在包含之前和之后我有不同的 ActionContext 实例!!

【问题讨论】:

  • 如果您需要更多信息,请告诉我,谢谢!!
  • 你的struts.xml是什么?
  • @RomanC,感谢您的评论,我添加了我的 struts.xml,如果您需要更多信息,请告诉我。再次感谢!
  • this的问题,可能是它为黑暗领域带来了一丝曙光。
  • @RomanC,感谢您的评论。我的 web.xml 中有&lt;dispatcher&gt;INCLUDE&lt;/dispather&gt;。因此可以执行包含的操作。你能帮我多点忙吗?非常感谢!

标签: struts2 include action httpresponse actioncontext


【解决方案1】:

当您执行 rd.include 时,Web 服务器内部会触发另一个请求。因此,从 struts 的角度来看,它看到了一个全新的请求,并因此创建了一个新的操作上下文。 (这就是为什么你需要在 struts2 过滤器中包含 'INCLUDE' 的东西。这样它也能看到包含的请求)。可能是因为线程局部变量用于跟踪操作上下文,当您执行 ActionContext.getContext() 时,将检索与新请求(与包含相关)相关的上下文。

您是否尝试在如下所示的 finally 块中将响应重置为初始响应

try {
      rd.include(request, myResponse); 
      String s = myResponse.getOutput();  
      System.out.println("get from response: " + s);
    }
    catch (Exception e) {
      e.printStackTrace();
    } finally {
       ServletActionContext.setResponse(response);
    }

如果这解决了响应问题.. 您可以将字符串 's' 作为变量存储在操作上下文中并在 Action2 中检索它

【讨论】:

  • 非常感谢您的回答!我确实尝试过这种方法,它解决了我的部分问题。但我仍然对这种方法不满意。具体来说,include 动作执行完成后,会返回到TestAction1,此时,如果我试图通过调用ActionContext.getContext() 来获取ActionContext,它会返回与之前include 执行不同的一个。所以response 也不同,在TestAction2 中,即使我将原始响应设置回ServletActionContext,我也得到了不同的response。恐怕我无法让自己理解这个复杂的问题。谢谢
  • 因为包含后,如果我从 ServletActionContext 得到响应,它是一个不同的,即它是我自定义的 ResponseImpl 的一个实例。此外,当我从 TestAction2 获得响应时,我还获得了我自定义的 ResponseImpl 的实例。这导致页面无法显示,如果我在TestAction2中可以得到原始响应,则可以显示页面。
  • 我认为在包含完成后,它会返回到 TestAction1,ActionConext.getContext() 在包含之前应该是同一个实例,但它不是同一个实例。这是怎么回事? include 之前和之后应该在同一个请求和线程中,我认为它们应该是同一个,为什么不呢?非常感谢您的耐心等待!
  • 现在我认为如果我能在包含之后得到原始的Response和ActionContext实例,这个问题就可以解决,并且在TestAction2中,除了我自定义的ResponseImpl的实例之外,我还能得到原始的Response。这是我现在的方向。我还在为这个问题而奋斗。谢谢!!任何建议!
  • @LiuWenbin_NO.所以 setResponse(originalResponse) 没用是吗?
【解决方案2】:

您也可以尝试以下方法。而不是使用链接.. 在您的 TestAction1 中包含带有原始响应的 TestAction2 。从操作中返回“无”作为返回值。

public class TestAction1 {
  public String execute() {
    ActionContext ac = ActionContext.getContext();
    System.out.println("Before including: the action context is : " + ac);
    HttpServletRequest request = ServletActionContext.getRequest();
    HttpServletResponse response = ServletActionContext.getResponse();
    System.out.println("Before including: the response is : " + response);

    ResponseImpl myResponse = new ResponseImpl(response);
    RequestDispatcher rd = request.getRequestDispatcher("/x.action");
    try {
      rd.include(request, myResponse); 
      String s = myResponse.getOutput();  
      System.out.println("get from response: " + s);
    }
    catch (Exception e) {
      e.printStackTrace();
    }



      RequestDispatcher rd = request.getRequestDispatcher("/y.action");
        try {
          rd.include(request, response); 
        }
        catch (Exception e) {
          e.printStackTrace();
        } finally {
          return "none";
        }
      }
    }

【讨论】:

  • @LiuWenbin_NO.你试过用这种方法吗
  • 感谢您的方法。我没有尝试这种方法,因为即使这对我有用,我也会对我当前的应用程序进行非常多的更改。但是现在,我解决了!只需在TestAction1 的execute() 末尾添加ActionContext.setContext(actionContext)。它看起来像:ActionContext ac = ActionContext.getContext();...rd.include(..); ... ActionContext.setConext(ac);,它现在对我有用。
猜你喜欢
  • 1970-01-01
  • 2014-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多