경량 Ajax 컴포넌트 작성 01 - 웹폼 플랫폼의 다양한 구현 방법과 비교

亚连
풀어 주다: 2018-05-24 14:57:19
원래의
1388명이 탐색했습니다.

이 글은 주로 경량 Ajax 컴포넌트 작성 01을 소개합니다. 웹폼 플랫폼의 다양한 구현 방식과 비교해서 도움이 필요한 친구들이 참고할 수 있습니다.

Foreword

 Asp.net WebForm 및 Asp.net MVC(MVC라고도 함) )는 둘 다 Asp.net을 기반으로 하는 웹 개발 프레임워크입니다. 그 중 하나는 MVC가 http의 본질에 더 많은 관심을 기울이는 반면 WebForm은 이를 위해 대규모의 http를 제공합니다. 개발자가 Windows Form 응용 프로그램을 개발하는 것처럼 이벤트 모델을 기반으로 프로그래밍할 수 있도록 하는 서버 컨트롤 및 ViewState 메커니즘의 수입니다. 둘 다 고유한 장점, 단점 및 적용 가능한 시나리오가 있지만 MVC는 이제 많은 Asp.net 개발자가 가장 먼저 선택하는 것입니다.

 WebForm은 Asp.net을 기반으로 합니다. Asp.net은 이를 사용하여 WebForm에서 MVC와 같은 프레임워크를 작성할 수도 있습니다. WebForm의 경우 많은 사람들이 서버 컨트롤(드래그 컨트롤!!!)을 생각할 것입니다. 실제로는 서버 컨트롤을 전혀 사용할 수 없으며 MVC와 같은 HTML에만 집중할 수도 있습니다. WebForm이 서버 컨트롤을 포기하고 HTML에 집중하려면 먼저

태그를 제거해야 합니다. 이 runat 서버 양식은 PostBack 메커니즘의 기초입니다. html+css+js로 돌아가기 때문에 Ajax 요청 처리와 같은 많은 작업을 직접 구현해야 한다는 의미입니다. MVC와 달리 WebForm의 초기 디자인에서는 서버 컨트롤을 주요 구성 요소로 사용합니다. 이를 사용하지 않으면 확장성을 사용하여 이를 달성할 수 있습니다.

 본 시리즈는 WebForm 플랫폼을 기반으로 경량의 Ajax 컴포넌트를 구현하는 시리즈로 크게 세 부분으로 나누어집니다:

 1. WebForm에서의 다양한 구현 방법을 소개합니다.

 2. ajaxpro 구성 요소를 분석합니다.

 3. 자신만의 Ajax 컴포넌트를 작성해 보세요.

1. Ajax 소개

비동기식을 사용하면 전체 페이지를 새로 고치지 않고도 서버에 데이터를 요청하거나 제출할 수 있습니다. 복잡한 페이지의 경우 단지 약간의 데이터를 요청하기 위해 전체 페이지를 다시 로드하는 것은 확실히 비효율적입니다. Ajax는 이 문제를 해결하도록 설계되었습니다. Ajax의 핵심은 요청이 텍스트 형식으로 서버에 제출되는 XmlHttpRequest 개체입니다. XmlHttpRequest2.0 이후에는 바이너리 데이터 제출도 지원됩니다.

  Ajax 보안: 보안상의 이유로 Ajax는 동일 출처 정책에 의해 제한됩니다. 즉, 동일한 도메인 및 동일한 포트의 요청에만 액세스할 수 있으며 교차 도메인 요청은 거부됩니다. 물론 요구 사항에 따라 도메인 간에 요청을 보내야 하는 경우도 있습니다. 일반적으로 사용되는 도메인 간 처리 방법에는 CORS(교차 도메인 리소스 공유) 및 JSONP(매개변수 JSON)가 있습니다.

  Ajax 데이터 상호 작용 형식: Ajax 핵심 개체 XmlHttpRequest에는 "XML"이라는 단어가 있지만 클라이언트와 서버 간의 데이터 교환 형식은 xml로 제한되지 않습니다. 예를 들어 이제 json 형식이 더 자주 사용됩니다. ​

​ Ajax에도 단점이 있습니다. 예를 들어, 검색 엔진에 대한 지원은 그다지 좋지 않습니다. 때로는 URL 리소스 위치 지정의 원래 의도를 위반합니다.

2. Asp.net MVC 플랫폼에서 ajax 사용

MVC에서는 Ajax가 백그라운드 메서드를 호출하는 것이 매우 편리합니다. Action의 이름만 지정하면 됩니다.

 프런트 엔드 코드:

<body>
  <h1>index</h1>
  <input type="button" value="GetData" onclick="getData()" />
  <span id="result"></span>
</body>
<script type="text/javascript">
  function getData() {
    $.get("GetData", function (data) {
      $("#result").text(data);
    });
  }
</script>
로그인 후 복사

 백엔드 코드:

public class AjaxController : Controller
{
  public ActionResult GetData()
  {
    if(Request.IsAjaxRequest())
    {
      return Content("data");
    }
    return View();
  }
}
로그인 후 복사

3. WebForm 플랫폼에서 ajax 사용

 3.1 서버 제어 패키지 또는 타사 기반 파티 컴포넌트

 이것은 ajax 툴킷과 같은 서버 컨트롤이나 FineUI와 같은 컴포넌트를 기반으로 합니다. 웹 프론트엔드는 항상 html+css+js로 구성되는데 문제는 어떻게 생성하느냐 입니다. 기본 플러그인을 직접 작성하거나 일부 프런트엔드 플러그인을 사용할 수 있습니다. 서버 컨트롤 기반 플러그인은 백그라운드에서 생성되며 일반적으로 효율성이 떨어집니다. 서버 구성 요소는 전경에서 일련의 프록시를 생성합니다. 본질은 여전히 ​​동일하지만 컨트롤은 이 프로세스를 캡슐화하므로 직접 작성할 필요가 없습니다. 컨트롤 또는 타사 구성 요소를 기반으로 하는 모델은 일부 관리 시스템에서 매우 유용하며 방문 횟수가 그리 많지 않으며 빠르게 개발할 수 있습니다.

 3.2 ICallbackEventHandler 인터페이스를 기반으로

  .net은 콜백 요청 처리를 위한 ICallbackEventHandler 인터페이스를 제공합니다. 이 인터페이스는 요청을 보내고 받기 위해 포그라운드에서 프록시 스크립트를 생성하기 위해 ClientScriptManager를 사용해야 하므로

태그가 필요합니다.

 프론트엔드 코드:

<body>
  <form id="form1" runat="server">
  <p>    
    <input type="button" value="获取回调结果" onclick="callServer()" />
    <span id="result" style="color:Red;"></span>
  </p>
  </form>
</body>
<script type="text/javascript">
  function getCallbackResult(result){
    document.getElementById("result").innerHTML = result;
  }
</script>
로그인 후 복사

백엔드 코드:

public partial class Test1 : System.Web.UI.Page, ICallbackEventHandler
{    
  protected void Page_Load(object sender, EventArgs e)
  {
    //客户端脚本Manager
    ClientScriptManager scriptMgr = this.ClientScript;
 
    //获取回调函数,getCallbackResult就是回调函数
    string functionName = scriptMgr.GetCallbackEventReference(this, "", "getCallbackResult", "");
 
    //发起请求的脚本,callServer就是点击按钮事件的执行函数
    string scriptExecutor = "function callServer(){" + functionName + ";}";
 
    //注册脚本
    scriptMgr.RegisterClientScriptBlock(this.GetType(), "callServer", scriptExecutor, true);
  }
 
  //接口方法
  public string GetCallbackResult()
  {
    return "callback result";
  }
 
  //接口方法
  public void RaiseCallbackEvent(string eventArgument)
  {
  }
}
로그인 후 복사

 이 방법에는 다음과 같은 단점이 있습니다:

 1. 구현이 더 복잡하고 페이지마다 로드 이벤트는 그에 따라 스크립트로 등록되어야 합니다.

  2. 프런트엔드는 프록시용 스크립트 파일을 생성합니다.

  3. 복잡한 페이지 상호작용의 경우 구현하기가 매우 번거롭습니다.

  4. 콜백이기는 하지만 이때에도 여전히 페이지 개체가 생성됩니다.

 3.3 일반 처리 절차 사용

  一般处理程序其实是一个实现了IHttpHandler接口类,与页面类一样,它也可以用于处理请求。一般处理程序通常不用于生成html,也没有复杂的事件机制,只有一个ProcessRequest入口用于处理请求。我们可以将ajax请求地址写成.ashx文件的路径,这样就可以处理了,而且效率比较高。

  要输出文本内容只需要Response.Write(data)即可,例如,从数据库获取数据后,序列化为json格式字符串,然后输出。前面说到,一般处理程序不像页面一样原来生成html,如果要生成html,可以通过加载用户控件生成。如:

public void ProcessRequest(HttpContext context)
{
  Page page = new Page();
  Control control = page.LoadControl("~/PageOrAshx/UserInfo.ascx");
  if (control != null)
  {
    StringWriter sw = new StringWriter();
    HtmlTextWriter writer = new HtmlTextWriter(sw);
    control.RenderControl(writer);
    string html = sw.ToString();
    context.Response.Write(html);        
  }
}
로그인 후 복사

  这种方式的优点是轻量、高效;缺点是对于交互多的需要定义许多ashx文件,加大了管理和维护成本。

  3.4 页面基类

  将处理ajax请求的方法定义在页面对象内,这样每个页面就可以专注处理本页面相关的请求了。这里有点需要注意。

  1.如何知道这个请求是ajax请求?

    通过请求X-Requested-With:XMLHttlRequest 可以判断,大部份浏览器的异步请求都会包含这个请求头;也可以通过自定义请求头实现,例如:AjaxFlag:XHR。

  2.在哪里统一处理?

    如果在每个页面类里判断和调用是很麻烦的,所以将这个处理过程转到一个页面基类里处理。

  3.如何知道调用的是哪个方法?

    通过传参或者定义在请求头都可以,例如:MethodName:GetData。

  4.知道方法名称了,如何动态调用?

    反射。

  5.如何知道该方法可以被外部调用?

    可以认为public类型的就可以被外部调用,也可以通过标记属性标记。

  通过上面的分析,简单实现如下  

  页面基类:

public class PageBase : Page
{
  public override void ProcessRequest(HttpContext context)
  {
    HttpRequest request = context.Request;
    if (string.Compare(request.Headers["AjaxFlag"],"AjaxFlag",0) == 0)
    {
      string methodName = request.Headers["MethodName"];
      if (string.IsNullOrEmpty(methodName))
      {
        EndRequest("MethodName标记不能为空!");
      }
      Type type = this.GetType().BaseType;
      MethodInfo info = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
      if (info == null)
      {
        EndRequest("找不到合适的方法调用!");
      }        
      string data = info.Invoke(this, null) as string;
      EndRequest(data);
    }
    base.ProcessRequest(context);
  }
  private void EndRequest(string msg)
  {
    HttpResponse response = this.Context.Response;
    response.Write(msg);
    response.End();
  }
}
로그인 후 복사

  页面类:

public partial class Test1 : PageBase
{
  protected void Page_Load(object sender, EventArgs e)
  {
  }
  public string GetData()
  {
    return "213";
  }
}
로그인 후 복사

  前台代码:

function getData(){
  $.ajax({
    headers:{"AjaxFlag":"XHR","MethodName":"GetData"},
    success:function(data){
      $("#result").text(data);
    }
  });
}
로그인 후 복사

四、优化版页面基类

  上面的页面基类功能很少,而且通过反射这样调用的效率很低。这里优化一下:

  1.可以支持简单类型的参数。

    例如上面的GetData可以是:GetData(string name),通过函数元数据可以获取相关的参数,再根据请求的参数,就可以设置参数了。

  2.加入标记属性。

    只有被AjaxMethodAttribute标记的属性才能被外部调用。

  3.优化反射。

    利用缓存,避免每次都根据函数名称去搜索函数信息。

  标记属性:

public class AjaxMethodAttribute : Attribute
{
}
로그인 후 복사


  缓存对象:  

public class CacheMethodInfo
{
  public string MethodName { get; set; }
  public MethodInfo MethodInfo { get; set; }
  public ParameterInfo[] Parameters { get; set; }
}
로그인 후 복사


  基类代码:

public class PageBase : Page
{
  private static Hashtable _ajaxTable = Hashtable.Synchronized(new Hashtable());
  public override void ProcessRequest(HttpContext context)
  {      
    HttpRequest request = context.Request;
    if (string.Compare(request.Headers["AjaxFlag"],"XHR",true) == 0)
    {
      InvokeMethod(request.Headers["MethodName"]);
    }
    base.ProcessRequest(context);
  }
  /// <summary>
  /// 反射执行函数
  /// </summary>
  /// <param name="methodName"></param>
  private void InvokeMethod(string methodName)
  {
    if (string.IsNullOrEmpty(methodName))
    {
      EndRequest("MethodName标记不能为空!");
    }
    CacheMethodInfo targetInfo = TryGetMethodInfo(methodName);
    if (targetInfo == null)
    {
      EndRequest("找不到合适的方法调用!");
    }
    try
    {
      object[] parameters = GetParameters(targetInfo.Parameters);
      string data = targetInfo.MethodInfo.Invoke(this, parameters) as string;
      EndRequest(data);
    }
    catch (FormatException)
    {
      EndRequest("参数类型匹配发生错误!");
    }
    catch (InvalidCastException)
    {
      EndRequest("参数类型转换发生错误!");
    }
    catch (ThreadAbortException)
    {
    }
    catch (Exception e)
    {
      EndRequest(e.Message);
    }
  }
  /// <summary>
  /// 获取函数元数据并缓存
  /// </summary>
  /// <param name="methodName"></param>
  /// <returns></returns>
  private CacheMethodInfo TryGetMethodInfo(string methodName)
  {
    Type type = this.GetType().BaseType;
    string cacheKey = type.AssemblyQualifiedName;
    Dictionary<string, CacheMethodInfo> dic = _ajaxTable[cacheKey] as Dictionary<string, CacheMethodInfo>;
    if (dic == null)
    {
      dic = new Dictionary<string, CacheMethodInfo>();
      MethodInfo[] methodInfos = (from m in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
                    let ma = m.GetCustomAttributes(typeof(AjaxMethodAttribute), false)
                    where ma.Length > 0
                    select m).ToArray();
      foreach (var mi in methodInfos)
      {
        CacheMethodInfo cacheInfo = new CacheMethodInfo();
        cacheInfo.MethodName = mi.Name;
        cacheInfo.MethodInfo = mi;
        cacheInfo.Parameters = mi.GetParameters();
        dic.Add(mi.Name, cacheInfo);
      }
      _ajaxTable.Add(cacheKey, dic);
    }
    CacheMethodInfo targetInfo = null;
    dic.TryGetValue(methodName, out targetInfo);
    return targetInfo;
  }
  /// <summary>
  /// 获取函数参数
  /// </summary>
  /// <param name="parameterInfos"></param>
  /// <returns></returns>
  private object[] GetParameters(ParameterInfo[] parameterInfos)
  {
    if (parameterInfos == null || parameterInfos.Length <= 0)
    {
      return null;
    }
    HttpRequest request = this.Context.Request;
    NameValueCollection nvc = null;
    string requestType = request.RequestType;
    if (string.Compare("GET", requestType, true) == 0)
    {
      nvc = request.QueryString;
    }
    else
    {
      nvc = request.Form;
    }
    int length = parameterInfos.Length;
    object[] parameters = new object[length];
    if (nvc == null || nvc.Count <= 0)
    {
      return parameters;
    }
    for (int i = 0; i < length; i++)
    {
      ParameterInfo pi = parameterInfos[i];
      string[] values = nvc.GetValues(pi.Name);
      object value = null;
      if (values != null)
      {
        if (values.Length > 1)
        {
          value = String.Join(",", values);
        }
        else
        {
          value = values[0];
        }
      }
      if (value == null)
      {
        continue;
      }
      parameters[i] = Convert.ChangeType(value, pi.ParameterType);
    }      
    return parameters;
  }
  private void EndRequest(string msg)
  {
    HttpResponse response = this.Context.Response;
    response.Write(msg);
    response.End();
  }
}
로그인 후 복사

  页面类:

public string GetData3(int i, double d, string str)
{
  string[] datas = new string[] { i.ToString(), d.ToString(), str };
  return "参数分别是:" + String.Join(",", datas);
}
로그인 후 복사


  前台代码:

function getData3(){
  $.ajax({
    headers:{"AjaxFlag":"XHR","MethodName":"GetData3"},
    data:{"i":1,"d":"10.1a","str":"hehe"},
    success:function(data){
      $("#result").text(data);
    }
  });
}
로그인 후 복사

五、总结

  上面的页面基类已经具备可以完成基本的功能,但它还不够好。主要有:

  1. 依附在页面基类。对于本来有页面基类的,无疑会变得更加复杂。我们希望把它独立开来,变成一个单独的组件。

  2. 效率问题。反射的效率是很低的,尤其在web这类应用程序上,更应该慎用。以动态执行函数为例,效率主要低在:a.根据字符串动态查找函数的过程。b.执行函数时,反射内部需要将参数打包成一个数组,再将参数解析到线程栈上;在调用前CLR还要检测参数的正确性,再判断有没有权限执行。上面的优化其实只优化了一半,也就是优化了查找的过程,而Invoke同样会有性能损失。当然,随着.net版本越高,反射的效率也会有所提升,但这种动态的东西,始终是用效率换取灵活性的。

  3.不能支持复杂参数。有时候参数比较多,函数参数一般会封装成一个对象类型。

  4. AjaxMethodAttribute只是一个空的标记属性。我们可以为它加入一些功能,例如,标记函数的名称、是否使用Session、缓存设置等都可以再这里完成。

  用过WebForm的朋友可能会提到AjaxPro组件,这是一个开源的组件,下一篇就通过源码了解这个组件,借鉴它的处理过程,并且分析它的优缺点。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

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

Ajax 비동기 로딩 이미지 예시 분석

Ajax 요청 송수신

위 내용은 경량 Ajax 컴포넌트 작성 01 - 웹폼 플랫폼의 다양한 구현 방법과 비교의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿