给Ajax请求加上".do"

来源:互联网 发布:矩阵中diag什么意思 编辑:程序博客网 时间:2024/05/17 21:59

场景:一个基于Ajax技术的Web应用,采用的是多页面方式 ,每个页面内部使用Ajax实现复杂业务逻辑之间的无刷新切换,使用了Struts来实现MVC。

问题:Ajax的请求对象是Servlet。如果一个Ajax请求对应一个Servlet服务,那么就需要创建很多个Servlet,然后还要在web.xml中添加很多的Servlet定义。我想看到的是一个比较干净的web.xml文件,就像Struts那样,只保留一个ActionServlet的配置是多么令人愉快的事情啊!

分析:Struts使用struts-config.xml文件来定义请求的Servlet的路径(不妨将Action看成是Servlet的包装)。由哪个具体的Action去执行客户端的请求,则是由org.apache.struts.action.RequestProcessor的processActionCreate()方法来决定的。具体而言,则是根据struts-config.xml文件中action的配置属性type来决定。 

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

<struts-config>
...
  
<action-mappings>
    
<action 
        
path="/A1" 
        type
="my.test.action.A1Action">
    
<forward name="SUCCESS" path="/search/A1.jsp" />
    
</action>
  
</action-mappings>
...
</struts-config>

 参照Struts的源代码:

/**
     * <p>Return an <code>Action</code> instance that will be used to process
     * the current request, creating a new one if necessary.</p>
     *
     * 
@param request  The servlet request we are processing
     * 
@param response The servlet response we are creating
     * 
@param mapping  The mapping we are using
     * 
@return An <code>Action</code> instance that will be used to process
     *         the current request.
     * 
@throws IOException if an input/output error occurs
     
*/

    
protected Action processActionCreate(HttpServletRequest request,
        HttpServletResponse response, ActionMapping mapping)
        
throws IOException {
        
// Acquire the Action instance we will be using (if there is one)
        String className = mapping.getType(); //这里就是获取action的type属性

        
if (log.isDebugEnabled()) {
            log.debug(
" Looking for Action instance for class " + className);
        }


        
// If there were a mapping property indicating whether
        
// an Action were a singleton or not ([true]),
        
// could we just instantiate and return a new instance here?
        Action instance;

        
synchronized (actions) {
            
// Return any existing Action instance of this class
            instance = (Action) actions.get(className);//根据属性选择相应的Action对象

            
if (instance != null{
                
if (log.isTraceEnabled()) {
                    log.trace(
"  Returning existing Action instance");
                }


                
return (instance);
            }


            
// Create and return a new Action instance
            if (log.isTraceEnabled()) {
                log.trace(
"  Creating new Action instance");
            }


            
try {
                instance 
= (Action) RequestUtils.applicationInstance(className);

                
// Maybe we should propagate this exception
                
// instead of returning null.
            }
 catch (Exception e) ...

            actions.put(className, instance);
        }


        
if (instance.getServlet() == null{
            instance.setServlet(
this.servlet);
        }


        
return (instance);
    }

从Action中可以获取ActionServlet(javax.servlet.http.HttpServlet的子类)。其实,程序执行时所需的客户端参数是从Request中获取的。从Action基类给出的抽象方法execute()的参数列表可以看出:

public ActionForward execute(
    ActionMapping mapping,
    ActionForm actionForm, 
    HttpServletRequest request,
    HttpServletResponse response) 
throws Exception {
。。。
}

继承此方法可以获取到足够的信息来处理客户端的请求。

为Ajax服务的Servlet与Action的处理流程不同之处在于,前者会直接将处理的结果数据以一定的方式(比如xml或JSON格式的数据流)发送给客户端;后者会返回一个ActionForward对象,根据Action配置的forward列表来决定向客户端发送哪一个页面。因此,如果用Action来为Ajax提供服务,那么在逻辑处理结束发送结果数据的时候,应该直接通过Response将数据发送出去,而不是返回到Action的基类。Rqeuest对象可以很容易得到,因此,创建一个称为AjaxAction的类,它是Action的子类,但不返回ActionForward,而是直接输出数据流:

package my.test.common.ajax;

import java.io.IOException;
import java.util.Map;

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

import org.apache.struts.action.ActionForward;

public abstract class CommonAjaxAction extends Action {    
    
    
public final ActionForward execute(
            ActionMapping mapping,
            ActionForm actionForm, 
            HttpServletRequest request,
        HttpServletResponse response)
throws Exception {
        
        
try {

             
//对请求的逻辑处理,获得返回数据
              String data = "";
              data 
= ajaxExecute(request);
            
              
//直接输出数据流
              response.setContentType("application/x-javascript; charset=utf-8");            
              response.getWriter().write(data);
            
             
return null;

        }
 finally {
                                                                ...
        }

    }

    
    
/**
     * Ajax逻辑处理
     * 
     * 由子类执行
     * 
     * 
@param request
     * 
@throws IOException 
     
*/

    
protected abstract String ajaxExecute(HttpServletRequest request) throws IOException;
    
}

使用实例:

创建一个名为A1AjaxAction的Action,它是CommonAjaxAction的子类: 

public class A1AjaxAction extends CommonAjaxAction {

    
public String ajaxExecute(HttpServletRequest request) throws IOException {

        
//执行业务逻辑,返回处理结果数据
        A1Service service = new A1ServiceImpl();
        
        
return service.getXxx();
    }

}

由于不返回ActionForward对象,因此struts-config.xml中相应的Action的forward部分就不需要了:

....
    
<action 
        
path="/A1Ajax" 
        type
="my.test.ajax.A1AjaxAction">
    
</action>
....

相应地,页面中的Ajax请求看起来就和Struts的请求一致了:

var callback = ...
   
var data = ...
var url = "A1Ajax.do";
var async = true;  //异步
   
var method = "GET";
var oj = createHttpRequest();
...
oj.open(
method, url, async);
   
oj.send(data);

现在想想要添加一个Ajax的服务是多么简单!只要继承CommonAjaxAction类并实现ajaxExecute方法,返回你想给客户端的数据就行了。但别忘记在struts-config.xml中添加对应的action定义!再看看web.xml是多么的干净整洁 ^_^

结论:通过继承Struts的Action并作小小改造,不返回ActionForward,而直接将处理结果直接输出,形式上统一了Ajax请求和页面提交(两者都是".do"形式的请求),并且还简化了Servlet的配置。