SSH系列:(15)自定义Result返回类型(StrutsResultSupport)

总的来说,写一个自定义的Result Type有三个步骤:

(1)写一个实现了Result接口的类

(2)对该类进行注册

(3)使用该类

下面分成两个部分:第1个部分,只要是侧重于项目上的使用方式,第2部分是整理自Sturcts In Action书上的自定义返回Json类型的Result Tye。

1、对错误的特殊处理(项目中)

在有些特殊情况下,如果没有异常信息,但是有错误,并且有错误信息等内容,此时也需要进行友好的错误处理的话,那么可以借助StrutsResultSupport 返回结果类型来实现特定处理。此种方式先需要继承StrutsResultSupport ,然后可以在子类中获取本次请求的相关信息,再根据相关信息进行结果处理:

1.1、自定义实现了Result接口的类

package com.rk.core.action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.StrutsResultSupport;

import com.opensymphony.xwork2.ActionInvocation;

public class SysResult extends StrutsResultSupport {

	@Override
	protected void doExecute(String finalLocation, ActionInvocation invocation)
			throws Exception {
		HttpServletRequest request = ServletActionContext.getRequest();
		HttpServletResponse response = ServletActionContext.getResponse();
		BaseAction action = (BaseAction)invocation.getAction();

		// do something
		System.out.println("进入了SysResult...");
	}

}

1.2、注册和使用添加的Result Type类

	<!-- 配置全局结果及异常映射 -->
    <package name="base-default" extends="struts-default" abstract="true">
        <!-- 返回结果类型 -->
        <result-types>
        	<result-type name="sys-error" class="com.rk.core.action.SysResult"></result-type>
        </result-types>
        <!-- 全局返回结果 -->
        <global-results>
        	<result name="error" type="sys-error">/WEB-INF/jsp/error.jsp</result>
        	<result name="input">/WEB-INF/jsp/error.jsp</result>
        </global-results>
        <!-- 全局异常映射 -->
        <global-exception-mappings>
        	<exception-mapping result="input" exception="java.lang.Exception"></exception-mapping>
        </global-exception-mappings>
    </package>

其中注册的是

<result-type name="sys-error" class="com.rk.core.action.SysResult"></result-type>

其中使用的声明是

<result name="error" type="sys-error">/WEB-INF/jsp/error.jsp</result>

1.3、API知识 

1.3.1、Result接口

Result接口只定义了一个execute方法。

/**
 * All results (except for <code>Action.NONE</code>) of an {@link Action} are mapped to a View implementation.
 * Action所有result中,除了Action.NONE,都会对应一个View。 
 */
public interface Result extends Serializable {

    /**
     * Represents a generic interface for all action execution results.
     * Whether that be displaying a webpage, generating an email, sending a JMS message, etc.
     * Result接口唯一定义的方法的就是execute。
     */
    public void execute(ActionInvocation invocation) throws Exception;

}

1.3.2、StrutsResultSupport类

StrutsResultSupport类实现了Result接口,有三点需要注意:

(1)在配置<action>下的<result>时,它有一个默认参数location,是通过DEFAULT_PARAM来定义的

(2)在配置<action>下的<result>时,支持ONGL表达式,它是通过conditionalParse方法来处理的

(3)重点关注对Result接口下的execute方法的实现,它会调用抽象方法doExecute方法。这样做的好处就是各个子类可以提供自己对doExecute方法的实现。

public abstract class StrutsResultSupport implements Result, StrutsStatics {

    private static final Logger LOG = LoggerFactory.getLogger(StrutsResultSupport.class);

    /** The default parameter */
	/** 如果不指定参数,那么默认的参数就是location。 */
    public static final String DEFAULT_PARAM = "location";

    /** use UTF-8 as this is the recommended encoding by W3C to avoid incompatibilities. */
    public static final String DEFAULT_URL_ENCODING = "UTF-8";

    private boolean parse;
    private boolean encode;
    private String location;
    private String lastFinalLocation;

    public void setLocation(String location) {
        this.location = location;
    }
    
    public String getLocation() {
        return location;
    }

    /**
     * Implementation of the <tt>execute</tt> method from the <tt>Result</tt> interface. This will call
     * the abstract method {@link #doExecute(String, ActionInvocation)} after optionally evaluating the
     * location as an OGNL evaluation.
     * 
     */
    public void execute(ActionInvocation invocation) throws Exception {        
		lastFinalLocation = conditionalParse(location, invocation);/** 处理ONGL表达式 */
        doExecute(lastFinalLocation, invocation);/** 通过下面的方法可以得到doExecute是一个抽象方法 */
    }

    /**
     * Parses the parameter for OGNL expressions against the valuestack
     * 处理ONGL表达式
     */
    protected String conditionalParse(String param, ActionInvocation invocation) {
        if (parse && param != null && invocation != null) {
            return TextParseUtil.translateVariables(
                param, 
                invocation.getStack(),
                new EncodingParsedValueEvaluator());
        } else {
            return param;
        }
    }

    /**
     * Executes the result given a final location (jsp page, action, etc) and the action invocation
     * (the state in which the action was executed). Subclasses must implement this class to handle
     * custom logic for result handling.
     *
     */
    protected abstract void doExecute(String finalLocation, ActionInvocation invocation) throws Exception;

}

1.3.3、ServletRedirectResult

在strutf-default.xml中定义了redirect类型的结果

<result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>

我们重点关注它的execute方法和doExecute方法

public class ServletRedirectResult extends StrutsResultSupport implements ReflectionExceptionHandler {
	//int javax.servlet.http.HttpServletResponse.SC_FOUND = 302 [0x12e]
	protected int statusCode = SC_FOUND;
    /**
     * Redirects to the location specified by calling
     * {@link HttpServletResponse#sendRedirect(String)}.
     * 在execute方法上,它还是调用父类(StrutsResultSupport类)的execute方法
     */
    public void execute(ActionInvocation invocation) throws Exception {
        if (anchor != null) {
            anchor = conditionalParse(anchor, invocation);
        }
        super.execute(invocation);
    }

    /**
     * Redirects to the location specified by calling
     * {@link HttpServletResponse#sendRedirect(String)}.
     * 在本方法的最后,我们可以看到:会调用sendRedirect(response, finalLocation)方法
     */
    protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
        ActionContext ctx = invocation.getInvocationContext();
        HttpServletRequest request = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
        HttpServletResponse response = (HttpServletResponse) ctx.get(ServletActionContext.HTTP_RESPONSE);

        if (isPathUrl(finalLocation)) {
            if (!finalLocation.startsWith("/")) {
                ActionMapping mapping = actionMapper.getMapping(request, Dispatcher.getInstance().getConfigurationManager());
                String namespace = null;
                if (mapping != null) {
                    namespace = mapping.getNamespace();
                }

                if ((namespace != null) && (namespace.length() > 0) && (!"/".equals(namespace))) {
                    finalLocation = namespace + "/" + finalLocation;
                } else {
                    finalLocation = "/" + finalLocation;
                }
            }

            // if the URL‘s are relative to the servlet context, append the servlet context path
            if (prependServletContext && (request.getContextPath() != null) && (request.getContextPath().length() > 0)) {
                finalLocation = request.getContextPath() + finalLocation;
            }
        }
        ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(invocation.getResultCode());
        if (resultConfig != null) {
            Map<String, String> resultConfigParams = resultConfig.getParams();

            List<String> prohibitedResultParams = getProhibitedResultParams();
            for (Map.Entry<String, String> e : resultConfigParams.entrySet()) {
                if (!prohibitedResultParams.contains(e.getKey())) {
                    Collection<String> values = conditionalParseCollection(e.getValue(), invocation, suppressEmptyParameters);
                    if (!suppressEmptyParameters || !values.isEmpty()) {
                        requestParameters.put(e.getKey(), values);
                    }
                }
            }
        }

        StringBuilder tmpLocation = new StringBuilder(finalLocation);
        urlHelper.buildParametersString(requestParameters, tmpLocation, "&");

        // add the anchor
        if (anchor != null) {
            tmpLocation.append(‘#‘).append(anchor);
        }

        finalLocation = response.encodeRedirectURL(tmpLocation.toString());

        sendRedirect(response, finalLocation);
    }

    /**
     * Sends the redirection. Can be overridden to customize how the redirect is
     * handled (i.e. to use a different status code)
     * 
     * @param response The response
     * @param finalLocation The location URI
     * @throws IOException
     */
    protected void sendRedirect(HttpServletResponse response, String finalLocation) throws IOException {
        if (SC_FOUND == statusCode) { 
            response.sendRedirect(finalLocation);
        } else {
            response.setStatus(statusCode);
            response.setHeader("Location", finalLocation);
            response.getWriter().write(finalLocation);
            response.getWriter().close();
        }

    }

}

1.3.4、ServletActionRedirectResult

在strutf-default.xml中定义了redirectAction类型的结果

<result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>

ServletActionRedirectResult需要注意的有3点:

(1)它继承自ServletRedirectResult类

(2)在<action>下写<result type="redirectAction">时,它的默认参数改写为actionName。

(3)关注对execute方法的实现,它还是调用了父类(ServletRedirectResult类)的execute方法,它的原理应该是将相应的actionName和namespace参数转换成目标url。

public class ServletActionRedirectResult extends ServletRedirectResult implements ReflectionExceptionHandler {

    /* The default parameter */
    public static final String DEFAULT_PARAM = "actionName";

    protected String actionName;
    protected String namespace;
    protected String method;

    /**
     * @see com.opensymphony.xwork2.Result#execute(com.opensymphony.xwork2.ActionInvocation)
     */
    public void execute(ActionInvocation invocation) throws Exception {
        actionName = conditionalParse(actionName, invocation);
        if (namespace == null) {
            namespace = invocation.getProxy().getNamespace();
        } else {
            namespace = conditionalParse(namespace, invocation);
        }
        if (method == null) {
            method = "";
        } else {
            method = conditionalParse(method, invocation);
        }

        String tmpLocation = actionMapper.getUriFromActionMapping(new ActionMapping(actionName, namespace, method, null));

        setLocation(tmpLocation);

        super.execute(invocation);
    }

}

1.3.5、ServletDispatcherResult

在strutf-default.xml中定义了dispatcher类型的结果

<result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>

在这里主要是关注对execute方法的实现:最后是调用dispatcher.forward或dispatcher.include方法

public class ServletDispatcherResult extends StrutsResultSupport {
    /**
     * Dispatches to the given location. Does its forward via a RequestDispatcher. If the
     * dispatch fails, a 404 error will be sent back in the http response.
     *
     */
    public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {

        PageContext pageContext = ServletActionContext.getPageContext();

        if (pageContext != null) {
            pageContext.include(finalLocation);
        } else {
            HttpServletRequest request = ServletActionContext.getRequest();
            HttpServletResponse response = ServletActionContext.getResponse();
            RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation);

            //add parameters passed on the location to #parameters
            // see WW-2120
            if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf("?") > 0) {
                String queryString = finalLocation.substring(finalLocation.indexOf("?") + 1);
                Map<String, Object> parameters = getParameters(invocation);
                Map<String, Object> queryParams = urlHelper.parseQueryString(queryString, true);
                if (queryParams != null && !queryParams.isEmpty())
                    parameters.putAll(queryParams);
            }

            // if the view doesn‘t exist, let‘s do a 404
            if (dispatcher == null) {
                response.sendError(404, "result ‘" + finalLocation + "‘ not found");
                return;
            }

            //if we are inside an action tag, we always need to do an include
            Boolean insideActionTag = (Boolean) ObjectUtils.defaultIfNull(request.getAttribute(StrutsStatics.STRUTS_ACTION_TAG_INVOCATION), Boolean.FALSE);

            // If we‘re included, then include the view
            // Otherwise do forward
            // This allow the page to, for example, set content type
            if (!insideActionTag && !response.isCommitted() && (request.getAttribute("javax.servlet.include.servlet_path") == null)) {
                request.setAttribute("struts.view_uri", finalLocation);
                request.setAttribute("struts.request_uri", request.getRequestURI());

                dispatcher.forward(request, response);
            } else {
                dispatcher.include(request, response);
            }
        }
    }
}

2、写一个处理返回Json数据的Result(Struts In Action整理)

Struts 2 frameworkMVC架构,而result就是其中的V(View)

Struts 2 framework提供了不同类型的result,其中常用的result type包括dispatcher、redirectredirectAction,默认使用的的是result type: dispatcher


By default, the framework uses a result type that works with JSPs to render these response pages. This result, the dispatcher result, makes all the ValueStack data available to the executing JSP page. With access to that data, the JSP can render a dynamic HTML page.

Thanks to the intelligent defaults, we’ve been happily using JSPs, all the while oblivious to the existence of a variety of result types. All you need to know is that a result is the element into which you stuff the location of your JSP page. And that’s about all you need to know as long as your project stays the JSP course.

Struts 2 framework的result有非常大的灵活性,我们既可以使用Struts框架提供了result type类型,我们也可以自定义一种新的result type。

#话题由result转至action#

Struts 2 的action是用来接收来自客户端的请求、处理业务逻辑,并将the resulting state of domain data保留在ValueStack上。接下来,action返回一个control string,用来告诉struts框架要使用哪一个result。

When the framework determines that a given request should be handled by a given action, the action receives the request data, runs its business logic, and leaves the resulting state of domain data exposed on the ValueStack. The last thing the action does is return a control string that tells the framework which of the available results should render the view. Typically, the chosen result uses the data on the ValueStack to render some sort of dynamic response to the client.

If you want, however, to see how you can adapt results to nonstandard patterns of usage, such as the nonclassic patterns of Ajax applications, then stick around.

classic web application与Ajax web application的区别

A classic web application architecture uses the HTTP request and response cycle to submit requests to the server, and receive dynamically created HTML page responses back from that server. This architecture can most notably be distinguished from Ajax web applications that generally receive HTML fragments, XML, or JSON responses from the server, rather than full HTML pages.


how to use custom results to build Ajax applications with Struts 2

JavaScript Object Notation (JSON) provides a succinct text-based serialization of data objects. JSON can be a powerfully succinct and lightweight means of communication between a web application server and an Ajax client. By using JSON, we can serialize our Java-side data objects and send that serialization in a response to the client. On the client side, we can easily deserialize this JSON into runtime JavaScript objects and make  targeted  updates  to  our  client-side  view  without  requiring  a  page  refresh. Sounds pretty clean in theory, and it’s even cleaner in practice.

CODING THE JSONRESULT

First things first. If we plan to have Ajax clients demanding JSON responses, we need to build a result that can do that. This leads us to our custom JSONResult. Making a custom result is, in some ways, just as easy as writing an action. The only requirement imposed  on  a  result  implementation  is  that  it  implement  the  com.opensymphony.xwork2.Result interface, as shown:

public interface Result extends Serializable {
    public void execute(ActionInvocation invocation) throws Exception;
}

As with most framework components, a simple interface defines a highly decoupled and nonrestrictive contract for the component implementation. In this case, we need only  provide  an  execute()  method  for  an  entry  point.  This  method  receives  the ActionInvocation, which gives it access to all the relevant information for the current request. As we’ve learned, this ActionInvocation provides access to such things as the action  itself,  the  ActionContext, and the ValueStack.  This  is  how  results get their hands on the data.

Let’s dive in and have a look at our JSONResult.

2.1、自定义JsonResult类,实现Result接口

注意:这里使用到了Gson的jar包

package com.rk.core.action;

import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts2.ServletActionContext;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.util.ValueStack;

public class JsonResult implements Result {
	public static final String DEFAULT_PARAM = "jsonModelName";
	private String jsonModelName = "jsonModel";

	public String getJsonModelName() {
		return jsonModelName;
	}
	public void setJsonModelName(String jsonModelName) {
		this.jsonModelName = jsonModelName;
	}

	public void execute(ActionInvocation invocation) throws Exception {
		//1、得到JSON字符串
		//1.1、获取目标对象
		ValueStack valueStack = invocation.getStack();
		Object jsonModel = valueStack.findValue(jsonModelName);
		//1.2、将目标对象转换为JSON
		Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create();
		String jsonStr = gson.toJson(jsonModel);

		//2、将JSON进行输出
		HttpServletResponse response = ServletActionContext.getResponse();
		response.setContentType("application/json;charset=utf-8");
		PrintWriter out = response.getWriter();
		out.println(jsonStr);
	}

}

2.2、注册新增的Result Type

注意的有两点:

(1)声明base-default包,继承struts-default包,且abstract="true"

(2)定义json类型的Result

	<!-- 配置全局结果及异常映射 -->
    <package name="base-default" extends="struts-default" abstract="true">
        <!-- 返回结果类型 -->
        <result-types>
        	<result-type name="json" class="com.rk.core.action.JsonResult"></result-type>
        </result-types>
    </package>

2.3、使用新增的Result Type

值得注意的有2方面:

(1)继承了base-default包

(2)指明type="json"

     <package name="user_package" namespace="/tax" extends="base-default">
        <action name="user_*" class="userAction" method="{1}">
            <result name="{1}">/WEB-INF/jsp/tax/user/{1}.jsp</result>
            <result name="list" type="redirectAction">
            	<param name="actionName">user_listUI</param>
            	<param name="namespace">/tax</param>
            </result>
            <result name="listJson" type="json">userList</result>
        </action>
    </package>
时间: 2024-12-19 06:02:03

SSH系列:(15)自定义Result返回类型(StrutsResultSupport)的相关文章

redis 系列15 数据对象的(类型检查,内存回收,对象共享)和数据库切换

原文:redis 系列15 数据对象的(类型检查,内存回收,对象共享)和数据库切换 一.  概述 对于前面的五章中,已清楚了数据对象的类型以及命令实现,其实还有一种数据对象为HyperLogLog,以后需要用到再了解.下面再了解类型检查,内存回收,对象共享,对象的空转时长. 1.1   类型检查与命令多态 redis中用于操作键的命令基本上可以分为两种类型,一种是可以对任何的键执行,如:del, expire,rename,type,object 这些命令等,对于这些命令属于多态命令.另一种命令

C# WebApi之接口返回类型详解

转自:https://www.cnblogs.com/hnsongbiao/p/9375888.html Webapi的接口返回值主要有四种类型 void无返回值 IHttpActionResult HttpResponseMessage 自定义类型 void无返回值 大家都知道void声明的是一个无返回值的方法,声明一个api控制器方法,例如: public class ValuesController : ApiController { [HttpGet] public void Get()

SSH学习-struts2的result类型

在学习struts2整合spring的过程中,以前result类型只有name属性,后面发现struts-default.xml中定义了10种result类型,在result标签中可以使用type属性来指定是哪种类型,接下来对常用的几种类型做案例进行理解. result常用类型 result类型参考struts-default.xml中对result的配置,如下所示: <result-types> <result-type name="chain" class=&qu

PHP自定义函数返回多个值

PHP自定义函数只允许用return语句返回一个值,当return执行以后,整个函数的运行就会终止. 有时要求函数返回多个值时,用return是不可以把值一个接一个地输出的. return语句可以返回任何类型的变量,这就是使自定义函数返回多个值的关键. 代码:  <?php function results($string) {      $result = array();      $result[] = $string;//原字符串     $result[] = strtoupper($

SpringMVC经典系列-15对SpringMVC的总结---【LinusZhu】

注意:此文章是个人原创,希望有转载需要的朋友们标明文章出处,如果各位朋友们觉得写的还好,就给个赞哈,你的鼓励是我创作的最大动力,LinusZhu在此表示十分感谢,当然文章中如有纰漏,请联系[email protected],敬请朋友们斧正,谢谢. 到此为止,关于SpringMVC部分已经基本讲述完了,我所讲述的知识点都是在项目中能经常使用到的,所讲述项目代码都是经过验证的,可能个人的表述不是很到位,希望大家海涵,如果大家发现其中有问题可以随时联系我的邮箱,还是那句话,我发博文主要是为了分享自己的

异步的返回类型

异步的返回类型 异步方法具有三个可能的返回类型:Task<TResult>.Task和 void. Task(TResult) 返回类型  Task<TResult> 返回类型可用于某种异步方法,其中操作数含有类型 TResult. 在下面的示例中,GetDateTimeAsync 异步方法包含返回整数的 return 语句. 因此,方法声明必须指定 Task<int>. async Task<DateTime> GetDateTimeAsync() { /

C# web api 返回类型设置为json的两种方法

每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客!当然,希望将来的一天,某位老板看到此博客,给你的程序员职工加点薪资吧!因为程序员的世界除了苦逼就是沉默.我眼中的程序员大多都不爱说话,默默承受着编程的巨大压力,除了技术上的交流外,他们不愿意也不擅长和别人交流,更不乐意任何人走进他们的内心! 悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来.我们都知道计算机技术发展日新月异,速度惊人的快,你我稍不留神,就会被慢慢淘汰!因此:每日不间断的

ASP.NET Web API 2:Action的返回类型

Web API控制器中的Action方法有如下几种返回类型: void HttpResponseMessage IHttpActionResult 其它类型 基于上面几种不同的返回类型,Web API创建HTTP响应消息的机制也不同. 返回类型 Web API创建HTTP响应消息的机制 void 返回HTTP状态码204(无内容) HttpResponseMessage 直接转换成HTTP响应消息 IHttpActionResult 调用接口的ExecuteAsync方法创建一个HttpResp

算法系列15天速成——第四天 五大经典查找【上】

原文:算法系列15天速成--第四天 五大经典查找[上] 在我们的生活中,无处不存在着查找,比如找一下班里哪个mm最pl,猜一猜mm的芳龄....... 对的这些都是查找. 在我们的算法中,有一种叫做线性查找. 分为:顺序查找. 折半查找. 查找有两种形态: 分为:破坏性查找,   比如有一群mm,我猜她们的年龄,第一位猜到了是23+,此时这位mm已经从我脑海里面的mmlist中remove掉了. 哥不找23+的,所以此种查找破坏了原来的结构. 非破坏性查找, 这种就反之了,不破坏结构. 顺序查找