경 량 ajax 구성 요소 01-webform 플랫폼 의 다양한 구현 방식 과 비교

머리말
Asp.net WebForm 과 Asp.net MVC(MVC 로 약칭)는 모두 Asp.net 의 웹 개발 프레임 워 크 를 바탕 으로 하 는 것 으로 이들 은 큰 차이 가 있다.그 중 하 나 는 MVC 가 http 의 본질 을 더욱 중시 하 는 것 이다.한편,WebForm 은 http 를 차단 하려 고 하기 때문에 대량의 서버 컨트롤 과 ViewState 체 제 를 제공 하여 발신 자 들 이 Windows Form 응용 프로그램 을 개발 하 는 것 처럼 이벤트 모델 을 바탕 으로 프로 그래 밍 할 수 있 도록 한다.두 가 지 는 각각 장단 점 과 적용 상황 이 있 지만 MVC 는 현재 많은 Asp.net 개발 자 들 의 최 우선 선택 이다.
WebForm 은 Asp.net 을 바탕 으로 하 는 것 입 니 다.Asp.net 은 충분 한 확장 성 을 제공 합 니 다.우 리 는 이 를 이용 하여 WebForm 에서 MVC 와 같은 프레임 워 크 를 만 들 수 있 습 니 다.이것 은 다시 쓸 기회 가 있 습 니 다.WebForm 하면 많은 사람들 이 서버 컨트롤(컨트롤 끌 기!!)을 연상 합 니 다.그렇지 않 으 면 서버 컨트롤 을 전혀 사용 하지 않 고 MVC 처럼 html 에 관심 을 가 질 수 있 습 니 다.WebForm 은 서버 컨트롤 을 버 리 고 html 에 집중 하려 면 먼저
탭 을 제거 해 야 합 니 다.이 runat server 의 form 은 PostBack 메커니즘 의 기초 입 니 다.html+css+js 로 돌아 가 려 면 많은 것 이 스스로 이 루어 져 야 한 다 는 것 을 의미 합 니 다.예 를 들 어 Ajax 요청 을 처리 하 는 것 입 니 다.MVC 처럼 WebForm 이 시작 한 디자인 은 서버 컨트롤 을 주요 구성 부분 으로 하고 이 를 사용 하지 않 으 면 확장 성 을 이용 할 수 밖 에 없다.
이 시 리 즈 는 WebForm 플랫폼 을 바탕 으로 하 는 경량급 ajax 구성 요 소 를 실현 하 는 것 으로 주로 세 부분 으로 나 뉜 다.
1.WebForm 아래 의 다양한 실현 방식 을 소개 합 니 다.
2.ajax pro 구성 요 소 를 분석 합 니 다.
3.자신의 ajax 구성 요 소 를 작성 합 니 다.
1.Ajax 소개
비동기 화 는 서버 요청 이나 데 이 터 를 제출 하 는 것 처럼 전체 페이지 를 새로 고치 지 않 고 사용 할 수 있 습 니 다.복잡 한 페이지 에 대해 약간의 데 이 터 를 요청 하기 위해 전체 페이지 를 다시 불 러 오 는 것 은 분명히 비효 율 적 입 니 다.ajax 는 이 문 제 를 해결 하기 위해 서 입 니 다.ajax 의 핵심 은 XmlHttpRequest 대상 입 니 다.이 대상 을 통 해 서버 에 텍스트 로 요청 합 니 다.XmlHttpRequest 2.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 서버 컨트롤 패키지 또는 제3자 구성 요소 기반
이것 은 ajax toolkit 패키지 나 FineUI 와 같은 서버 컨트롤 을 기반 으로 한 구성 요소 입 니 다.웹 전단 은 html+css+js 로 구성 되 어 있 으 며,어떻게 생 성 되 는 지 에 불과 합 니 다.원생 의 우 리 는 스스로 작성 하거나 전단 플러그 인 을 사용 할 수 있 습 니 다.서버 컨트롤 을 기반 으로 한 것 은 모두 배경 에서 생 성 되 며 효율 도 낮 습 니 다.서버 구성 요 소 는 프론트 데스크 톱 에서 일련의 프 록 시 를 생 성 합 니 다.본질 은 똑 같 습 니 다.컨트롤 이 이 과정 을 봉 인 했 을 뿐 우리 가 직접 작성 할 필요 가 없습니다.컨트롤 이나 제3자 구성 요 소 를 바탕 으로 하 는 모델 은 일부 관리 시스템 에서 매우 유용 하고 방문 량 이 많 지 않 아 신속하게 개발 할 수 있 습 니 다.
3.2 ICallbackEventHandler 인터페이스 기반
.net 은 리 셋 요청 을 처리 하기 위해 ICallbackEventHandler 인 터 페 이 스 를 제공 합 니 다.이 인 터 페 이 스 는 클 라 이언 트 ScriptManager 로 프론트 데스크 톱 에 프 록 시 스 크 립 트 를 생 성하 여 요청 을 보 내 고 받 을 수 있 도록 해 야 하기 때문에
태그 가 필요 합 니 다.
프론트 코드:

<body>
  <form id="form1" runat="server">
  <div>    
    <input type="button" value="      " onclick="callServer()" />
    <span id="result" style="color:Red;"></span>
  </div>
  </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.실현 이 복잡 하 므 로 모든 페이지 Load 이벤트 에 해당 하 는 스 크 립 트 를 등록 해 야 합 니 다.
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:XML Httl Request 를 통 해 대부분의 브 라 우 저의 비동기 요청 에 이 요청 헤더 가 포함 되 어 있 음 을 판단 할 수 있 습 니 다.사용자 정의 요청 헤드 를 통 해서 도 가능 합 니 다.예 를 들 어 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);
    }
  });
}
4.최적화 판 페이지 기본 클래스
위의 페이지 의 기본 기능 이 매우 적 고 반 사 를 통 해 이렇게 호출 하 는 효율 이 매우 낮다.여기 최적화:
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.효율 문제.반사 효율 은 매우 낮다.특히 웹 과 같은 응용 프로그램 에 서 는 더욱 신중하게 사용 해 야 한다.동적 실행 함 수 를 예 로 들 면 효율 은 주로 a.문자열 에 따라 동적 으로 함 수 를 찾 는 과정 이다.b.함 수 를 실행 할 때 반사 내 부 는 매개 변 수 를 하나의 배열 로 포장 한 다음 에 매개 변 수 를 스 레 드 스 택 에 분석 해 야 합 니 다.호출 하기 전에 CLR 은 매개 변수의 정확성 을 검사 하고 실행 권한 이 있 는 지 판단 해 야 한다.위의 최 적 화 는 사실 절반 만 최적화 시 켰 다.즉,검색 과정 을 최적화 시 켰 고 Invoke 역시 성능 손실 이 있 을 수 있다.물론.net 버 전이 높 을 수록 반사 효율 도 향상 되 지만 이런 동태 적 인 것 은 효율 로 유연성 을 바꾼다.
3.복잡 한 매개 변 수 를 지원 할 수 없습니다.때때로 매개 변수 가 비교적 많 고 함수 매개 변 수 는 일반적으로 하나의 대상 유형 으로 봉 인 됩 니 다.
4.AjaxMethodAttribute 는 빈 태그 속성 일 뿐 입 니 다.함수 의 이름 을 표시 하거나 Session 을 사용 할 지,캐 시 설정 을 사용 할 지 등 기능 을 추가 할 수 있 습 니 다.
WebForm 을 사용 한 친 구 는 Ajax Pro 구성 요 소 를 언급 할 수 있 습 니 다.이것 은 오픈 소스 구성 요소 입 니 다.다음 편 은 소스 코드 를 통 해 이 구성 요 소 를 알 고 처리 과정 을 참고 하여 장단 점 을 분석 합 니 다.

좋은 웹페이지 즐겨찾기