>  기사  >  웹 프론트엔드  >  경량 Ajax 컴포넌트 작성 02 - AjaxPro에 대한 간략한 분석

경량 Ajax 컴포넌트 작성 02 - AjaxPro에 대한 간략한 분석

亚连
亚连원래의
2018-05-24 15:01:021601검색

ajaxpro는 오래된 구성 요소이지만 구현 아이디어와 소스 코드는 여전히 훌륭한 참고 가치가 있습니다. 다음으로 이 글을 통해 경량 ajax 컴포넌트 작성 방법을 소개하겠습니다. 02 - AjaxPro에 대한 간략한 분석입니다. 관심 있는 친구들은 다음을 참고할 수 있습니다.

서문

 이전 글에서는 웹폼에서 ajax를 구현하는 몇 가지 방법을 소개했습니다. 플랫폼을 구축하고 Base 클래스를 구현했습니다. 이 기사에서는 오픈 소스 구성 요소인 ajaxpro를 살펴봅니다. 이는 오래된 구성 요소이지만 구현 아이디어와 소스 코드는 여전히 학습할 가치가 있습니다. 이전 기사의 소개를 통해 우리는 리플렉션을 통해 페이지 개체 메서드를 호출한다는 것을 알았습니다. 핵심은 리플렉션 호출 메서드, 매개변수 매핑 등을 포함한 전체 처리 프로세스입니다. Ajaxpro는 이 프로세스를 백그라운드에서 구현하는 데 도움이 될 뿐만 아니라 ajax 관련 메서드와 같은 요청 호출 메서드를 포그라운드에서 캡슐화합니다. ajaxpro 메서드를 사용하면 js를 직접 캡슐화하거나 js 라이브러리를 사용하지 않고도 비동기 요청을 보낼 수 있습니다. 다음으로 이 구성요소를 간략하게 분석하겠습니다.

1. ajaxpro 사용

먼저 이 컴포넌트를 어떻게 사용하는지 살펴보겠습니다.

  1. AjaxHandlerFactory 등록

web.config에서 다음과 같이 구성합니다.

<httpHandlers>
 <add verb="POST,GET" path="ajaxpro/*.ashx" type="AjaxPro.AjaxHandlerFactory, AjaxPro"/>
</httpHandlers>

간단히 말해서 요청된 URL이 ajaxpro/*.ashx 형식을 준수하면 구현체인 AjaxHandlerFactory에 의해 처리됩니다. IHandler 핸들러를 가져오는 데 사용되는 IHandlerFactory 인터페이스 Factory 클래스입니다. 유형의 형식은 "이름 제어. 클래스 이름, 어셈블리 이름"입니다.

 2. 페이지 클래스에 등록 Page_Load event

protected void Page_Load(object sender, EventArgs e)
{
 AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxProPage));
}

  이 페이지 개체의 Type을 ResisterTypoForAjax 메서드에 전달합니다. 특히, 현재 페이지 개체의 RegisterClientScriptBlock을 사용합니다. 등록을 위해 호출되므로 aspx 파일에 9da45565527026e0988f6215b7b6a235f5a47148e367a6035fd7a2faa965022e이 있어야 합니다. 그렇지 않으면 스크립트가 등록되지 않습니다. (Type은 여기에 전달되지만 실제로는 전달하지 않고도 수행할 수 있습니다. 이 유형은 HttpContext.Current.Handler.GetType().BaseType을 통해 내부적으로 얻을 수도 있습니다.)

 3 메서드를 AjaxMethod 

[AjaxMethod]
public List<string> GetList(string input1,string input2)
{
 return new List<string> { input1, input2 };
}

로 표시합니다. AjaxMethod는 이 메소드가 ajax 요청을 처리하는 데 사용되며 궁극적으로 리플렉션을 통해 실행된다는 것을 나타내는 Mark 속성입니다. 여기에는 여러 생성자 쌍이 있으며 캐시해야 하는 일부 데이터의 경우 캐시 시간을 설정할 수 있습니다. 요청은 Session을 사용할 필요가 없으므로 HttpSessionStateRequirement를 설정할 수 있습니다. 시간이 많이 걸리는 웹 서비스를 요청하는 등 요청이 비동기적이어야 하는 경우 핸들러를 비동기 상태로 설정할 수도 있습니다.

 메서드의 반환 값은 단순 유형이거나 복합 유형일 수 있습니다. 예를 들어 포그라운드에서 얻은 컬렉션 유형은 배열입니다.

 4. 프런트 엔드 호출

백엔드의 구성과 사용은 매우 간단합니다. 다음으로 프런트 엔드가 요청을 시작하는 방법을 살펴보겠습니다.

function GetList() {
 //var result = AjaxProNamespace.AjaxProPage.GetList("a", "b").value;
 //console.log(result);
 AjaxProNamespace.AjaxProPage.GetList("a", "b", function (result) {
  console.log(result);
 });  
}

  여기서 AjaxProNamespace는 페이지 클래스가 위치한 네임스페이스이고, AjaxProPage는 페이지 클래스의 이름이며, GetList는 표시된 메서드입니다. 왜 이렇게 쓸 수 있나요? 앞서 언급했듯이 ajaxpro는 스크립트를 포그라운드에 등록하고 페이지 개체의 관련 정보를 기반으로 다음 스크립트를 생성하므로 js를 직접 작성하거나 jquery 라이브러리 메서드를 사용하지 않고도 이와 같이 호출할 수 있습니다.

if(typeof AjaxProNamespace == "undefined") AjaxProNamespace={};
if(typeof AjaxProNamespace.AjaxProPage_class == "undefined") AjaxProNamespace.AjaxProPage_class={};
AjaxProNamespace.AjaxProPage_class = function() {};
Object.extend(AjaxProNamespace.AjaxProPage_class.prototype, Object.extend(new AjaxPro.AjaxClass(), {
 GetList: function(input1, input2) {
  return this.invoke("GetList", {"input1":input1, "input2":input2}, this.GetList.getArguments().slice(2));
 },
 url: &#39;/ajaxpro/AjaxProNamespace.AjaxProPage,TestAjaxProSourceCode.ashx&#39;
}));
AjaxProNamespace.AjaxProPage = new AjaxProNamespace.AjaxProPage_class();

 GetList의 매개변수는 백그라운드 메소드의 매개변수에 해당합니다. 유형은 변환 가능해야 합니다. 그렇지 않으면 호출이 실패합니다. 마지막 매개변수는 콜백 함수입니다. 콜백 함수의 매개변수는 반환 결과를 캡슐화하는 객체입니다. 해당 값 속성은 성공적인 실행 후 반환되는 값입니다. 예를 들어 위에서 반환된 값은 배열 객체입니다. 해당 오류에는 실패 정보가 포함됩니다.

위에서 주석 처리한 부분은 동기 요청 방식인데, 이는 종종 우리가 원하는 방식이 아닙니다. 누군가가 이를 잘못 사용하는 것을 본 적이 있습니다.

2. ajaxpro의 요청 처리 원리

여기서는 주로 ajax 요청을 처리하는 구성 요소의 프로세스에 중점을 두고 있으며 기타 보조 기능은 소개하지 않습니다.

  1. 보조 스크립트 생성

 Page_Load 이벤트에서 AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxProPage));를 호출하여 필요한 스크립트를 등록했습니다. 첫 페이지에 다음 스크립트가 도입된 것을 확인했습니다.

즉, 각 페이지에서 이러한 요청을 시작합니다. 이것들은 모두 .ashx로 끝나는 파일이지만 실제로는 모두 js 코드입니다. 이러한 js 중 일부는 dll 내에 리소스로 중첩되어 있으며 일부는 주로 ajax 요청 관련 메서드를 캡슐화하고 다음을 사용할 수 있습니다. 페이지 클래스 이름. 태그 메소드 이름은 메소드를 호출하는 데 사용됩니다. .js 대신 .ashx를 사용하는 이유는 무엇입니까? 왜냐하면 .js 파일은 컴포넌트 내부의 리소스 파일이기 때문에 외부에서 직접 요청할 수는 없지만, .ashx를 가로채서 Response.Write를 이용해서 내용을 출력하게 되기 때문입니다.

  如果每次都生成和发送这些脚本的效率是很低的,ajaxpro内部的处理是判断请求头的If-None-Math和If-Modified-Since,如果两个都和缓存的一样,就返回一个304状态码。所以,客户端只有首次请求服务端会返回文件的内容,后续的都只返回304表示使用本地缓存。我们刷新页面可以验证这个过程:

  2. 拦截请求

  HttpHandler(IHttpHandler) 和 HttpModule(IHttpModule) 是asp.net 两个重要的组件,让我们可以在asp.net的基础上很方便的进行扩展。HttpHandler对应某种具体的请求,例如.ashx,.aspx等;HttpModule是一个拦截器,可以在管道的某个事件对所有请求进行拦截。简单的说,在管道中,HttpApplication会触发一系列事件,我们在通过HttpModule对某个事件进行注册,例如我们可以在处理程序对象生成前拦截请求,然后映射到自己的处理程序;而实际处理请求返回结果的是HttpHandler,例如Page用来生成html。

  以asp.net mvc框架为例,它是建立在asp.net 路由机制的基础上的,asp.net 路由系统通过一个UrlRoutingModule对请求进行拦截,具体是在PostResolveRequestCache事件进行拦截,对url进行解析,封装相应的路由数据后,最终将请求交给一个MvcHandler进行处理,MvcHandler实现了IHttpHandler接口。

  前面我们进行了如下配置:cf06240b37eddaeaaa5ffa482fe7b198 这表明了任何的以 ajaxpro/任意名称.ashx结尾的 Post/Get 请求,都交给AjaxPro.AjaxHandlerFactory进行处理,它是一个实现了IHandlerFactory的处理程序工厂,用来生成具体的IHttpHandler。组件内部定义了多个实现IHttpHandler的类,有的是为了生成js脚本的,对于处理ajax请求,主要分为两类:异步(IHttpAsyncHandler)和非异步(IHttpHandler);在这两类的基础上,对于Session的状态的支持又分为三种:支持读写(实现IRequiresSessionState标记接口)的Handler、只读(实现IReadOnlySessionState标记接口)的Handler和不支持Session的Handler。具体生成什么样的Handler是通过AjaxMethod进行判断的。

  IHttpHandler的ProcessRequest(异步就是BeginProcessRequest)就用来执行请求返回输出结果的。如果只需要一种处理程序我们也可以实现IHttpHandler。IHandlerFactory的定义如下:

public interface IHttpHandlerFactory
{
 IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated);
 void ReleaseHandler(IHttpHandler handler);
} 

  所以,ajaxpro的所有请求都会符合ajaxpro/*.ashx格式,然后在GetHandler方法,就可以进行具体的处理,返回结果是IHttpHandler;以非异步状态为例,如果我们配置了需要Session,就会生成一个实现IHttpHandler和IRequiresSessionState的Handler,如果需要只读的Session,就会生成一个实现IHttpHandler和IReadOnlySessionState的Handler;这些信息可以通过反射从AjaxMethod标记属性获得。AjaxHandlerFactory的主要代码如下:

public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
 string filename = Path.GetFileNameWithoutExtension(context.Request.Path);
 Type t = null;
 Exception typeException = null;
 bool isInTypesList = false;
 switch (requestType)
 {
  //Get请求,获取前面的那4个脚本
  case "GET": 
   switch (filename.ToLower())
   {
    case "prototype":
     return new EmbeddedJavaScriptHandler("prototype");
    case "core":
     return new EmbeddedJavaScriptHandler("core");
    case "ms":
     return new EmbeddedJavaScriptHandler("ms");
    case "prototype-core":
    case "core-prototype":
     return new EmbeddedJavaScriptHandler("prototype,core");
    case "converter":
     return new ConverterJavaScriptHandler();
    default:
     return new TypeJavaScriptHandler(t);
   }
  case "POST":
   IAjaxProcessor[] p = new IAjaxProcessor[2];
   p[0] = new XmlHttpRequestProcessor(context, t);
   p[1] = new IFrameProcessor(context, t);
   for (int i = 0; i < p.Length; i++)
   {
    if (p[i].CanHandleRequest)
    {
     //获取标记方法的AjaxMethod属性
     AjaxMethodAttribute[] ma = (AjaxMethodAttribute[])p[i].AjaxMethod.GetCustomAttributes(typeof(AjaxMethodAttribute), true);
     bool useAsync = false;
     HttpSessionStateRequirement sessionReq = HttpSessionStateRequirement.ReadWrite;
     if (ma.Length > 0)
     {
      useAsync = ma[0].UseAsyncProcessing;
      if (ma[0].RequireSessionState != HttpSessionStateRequirement.UseDefault)
       sessionReq = ma[0].RequireSessionState;
     }
     //6种Handler,根据是否异步,session状态返回指定的Handler
     switch (sessionReq)
     {
      case HttpSessionStateRequirement.Read:
       if (!useAsync)
        return new AjaxSyncHttpHandlerSessionReadOnly(p[i]);
       else
        return new AjaxAsyncHttpHandlerSessionReadOnly(p[i]);
      case HttpSessionStateRequirement.ReadWrite:
       if (!useAsync)
        return new AjaxSyncHttpHandlerSession(p[i]);
       else
        return new AjaxAsyncHttpHandlerSession(p[i]);
      case HttpSessionStateRequirement.None:
       if (!useAsync)
        return new AjaxSyncHttpHandler(p[i]);
       else
        return new AjaxAsyncHttpHandler(p[i]);
      default:
       if (!useAsync)
        return new AjaxSyncHttpHandlerSession(p[i]);
       else
        return new AjaxAsyncHttpHandlerSession(p[i]);
     }
    }
   }
   break;
 }
 return null;
}

  3. 反射执行方法

  当获得一个处理本次请求的Handler后,就可以在其ProcessRequest(异步为BeginProcessRequest)执行指定的方法。要执行一个页面对象的方法,我们必须知道指定页面所在的程序集,名称空间,页面类的名称以及方法的名称。这似乎符合我们前面:名称空间.类名称.方法名称的调用方式。为了与一般请求区分开,让组件具有足够的独立性,ajaxpro只拦截符合"ajaxpro/*.ashx格式的请求,这说明我们的ajax请求也要符合这个格式。如:http://localhost:50712/ajaxpro/AjaxProNamespace.AjaxProPage,TestAjaxProSourceCode.ashx,这个格式由前台脚本自动生成,并不需要我们去构造。仔细观察,会发现AjaxProNamespace.AjaxProPage,TestAjaxProSourceCode 就是页面类的完全限定名:名称空间.类名称,程序集名称,通过这个我们就可以生成具体的Type,然后进行反射获取信息。那么方法的名称呢?ajaxpro将其放在http header 中,名称为:X-AjaxPro-Method。有了这些信息,就可以反射执行方法了。这里核心代码为:

internal void Run()
{
 try
 {
  //设置输出结果不缓存(这不一定是我们想要的)
  p.Context.Response.Expires = 0;
  p.Context.Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
  p.Context.Response.ContentType = p.ContentType;
  p.Context.Response.ContentEncoding = System.Text.Encoding.UTF8;
  //验证ajax请求
  if (!p.IsValidAjaxToken())
  {
   p.SerializeObject(new System.Security.SecurityException("The AjaxPro-Token is not valid."));
   return;
  }
  //方法参数对象数组
  object[] po = null;
  //请求处理结果
  object res = null;
  try
  {
   //获取参数
   po = p.RetreiveParameters();
  }
  catch (Exception ex){}
  //获取缓存的Key
  string cacheKey = p.Type.FullName + "|" + p.GetType().Name + "|" + p.AjaxMethod.Name + "|" + p.GetHashCode();
  if (p.Context.Cache[cacheKey] != null)
  {
   //如果缓存存在,则直接使用缓存
   p.Context.Response.AddHeader("X-" + Constant.AjaxID + "-Cache", "server");
   p.Context.Response.Write(p.Context.Cache[cacheKey]);
   return;
  }
  try
  {
   if (p.AjaxMethod.IsStatic)
   {
    //使用反射调用静态方法
    try
    {
     res = p.Type.InvokeMember(
      p.AjaxMethod.Name,
      System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.InvokeMethod,
      null, null, po);
    }
    catch (Exception ex){}
   }
   else
   {
    try
    {
     //创建实例对象,反射调用实例方法
     object c = (object)Activator.CreateInstance(p.Type, new object[] { });
     if (c != null)
     {
      res = p.AjaxMethod.Invoke(c, po);
     }
    }
    catch (Exception ex){}
   }
  }
  catch (Exception ex){}
  try
  {
   //判断结果是不是xml,如是设置ContentType
   if (res != null && res.GetType() == typeof(System.Xml.XmlDocument))
   {
    p.Context.Response.ContentType = "text/xml";
    p.Context.Response.ContentEncoding = System.Text.Encoding.UTF8;
    ((System.Xml.XmlDocument)res).Save(p.Context.Response.OutputStream);
    return;
   }
   string result = null; ;
   System.Text.StringBuilder sb = new System.Text.StringBuilder();
   try
   {
    result = p.SerializeObject(res);
   }
   catch (Exception ex){}
   //如果需要缓存,则将结果写入缓存
   if (p.ServerCacheAttributes.Length > 0)
   {
    if (p.ServerCacheAttributes[0].IsCacheEnabled)
    {
     p.Context.Cache.Add(cacheKey, result, null, DateTime.Now.Add(p.ServerCacheAttributes[0].CacheDuration), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
    }
   }
  }
  catch (Exception ex){}
 }
 catch (Exception ex){}
}

三、总结

  我们总结一下ajaxpro的核心处理流程,它通过一个IHttpHandlerFactory拦截指定格式的url,然后从中获取类型的完全限定名生成类型对象,接着通过反射获取标记方法的特性,生成一个自定义的实现IHttpHandler接口的对象;在其ProcessRequest方法中,从http headers获取方法名称,通过反射进行参数映射并执行函数。

 ajaxpro에는 다음과 같은 장점이 있습니다.

 1. 간단한 구성.

 2. 다른 구성 요소와 함께 사용할 수 있습니다.

  3. 프런트엔드 스크립트를 캡슐화합니다. 직접 캡슐화하거나 다른 스크립트 라이브러리를 사용할 필요가 없습니다.

 4. 반환 값 처리를 위해 단순 유형 또는 복합 유형을 반환할 수 있으며 자동으로 직렬화됩니다. ​

단점은:

​ 1. 페이지에 요청이 4개 더 있습니다. 304 캐싱이 활용되더라도 요청은 여전히 ​​서버로 전송되어야 합니다.

 2. Ajax는 Get 요청을 사용할 수 없습니다. URL 형식은 사용자 정의되어 있으므로 이 형식을 사용하면 Get 요청을 사용할 수 없습니다. Yahoo의 프런트 엔드 최적화 제안 중 하나는 더 많은 가져오기 요청을 사용하는 것입니다. 실제로 http 헤더에 네임스페이스, 클래스 이름, 어셈블리를 넣은 다음 자유롭게 선택할 수 있도록 유형 매개변수를 제공해야 합니다.

  3. 9da45565527026e0988f6215b7b6a235로 바인딩합니다. 목적은 우리를 위해 프런트 엔드 스크립트를 생성하는 것이지만 .html 파일 + .aspx.cs 방법을 사용하려는 경우 사용할 수 없습니다(블로그 파크의 일부 페이지에서는 이 방법을 사용함). 이동해야 합니다. 끝에서 끝까지 사용하면 이러한 편리함이 제한됩니다.

 4. 반성. 이는 상대적으로 비효율적이며 이전 페이지 클래스처럼 MethodInfo를 캐시하지도 않습니다.

 효율성을 고려하지 않는다면 이 구성 요소는 여전히 사용할 가치가 있음을 알 수 있습니다. 여기에는 핵심 소개가 포함되어 있으며, 여기에는 다른 많은 기능이 포함되어 있습니다. 이것은 관심 있는 친구들이 공부할 수 있는 소스 코드입니다.

위 내용은 모두를 위해 제가 정리한 내용입니다. 앞으로 모든 사람에게 도움이 되기를 바랍니다.

관련 기사:

ajax를 통해 json 데이터를 얻은 후 json과 jsonp의 차이점과 형식 변환에 대한 간략한 분석

Django 프레임워크는 ajax를 사용하여 데이터 일괄 가져오기 기능을 구현합니다

AJAX XMLHttpRequest 객체 상세설명

위 내용은 경량 Ajax 컴포넌트 작성 02 - AjaxPro에 대한 간략한 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.