ASP.NET에서 어떻게 더 많은 제어권을 가질 수 있습니까?


124

저는 아주 간단한 "마이크로 웹앱"을 만들려고 노력하고 있습니다.이 작업을 완료하면 몇 명의 Stack Overflow가 관심을 가질 것 같습니다. 나는 바닐라 ASP.NET 3.5 (즉 MVC가 아님) 인 Depth 사이트의 C #에서 호스팅하고 있습니다.

흐름은 매우 간단합니다.

  • 사용자가 모든 매개 변수를 지정하지 않는 URL을 사용하여 앱에 입력하면 (또는 매개 변수가 유효하지 않은 경우) 사용자 입력 컨트롤 만 표시하고 싶습니다. (두 개뿐입니다.)
  • 사용자가 URL을 사용하여 응용 프로그램을 입력하면 수행 에 필요한 모든 매개 변수가 나는 결과를 표시 할 입력 컨트롤을 (그들이 매개 변수를 변경할 수 있습니다)

다음은 자체적으로 부과 한 요구 사항입니다 (디자인과 구현의 혼합).

  • 제출시 POST 대신 GET을 사용하여 사용자가 페이지를 쉽게 북마크 할 수 있도록하고 싶습니다.
  • 나는 하지 않는 URL이 거기에 외부 비트와 조각, 제출 후 바보 찾고 결국합니다. 기본 URL과 실제 매개 변수 만 입력하세요.
  • 이상적으로는 JavaScript가 전혀 필요하지 않고 싶습니다. 이 앱에는 그럴만 한 이유가 없습니다.
  • 렌더링 시간 동안 컨트롤에 액세스하고 값을 설정하는 등의 기능을 원합니다. 특히 ASP.NET에서이 작업을 자동으로 수행 할 수없는 경우 전달 된 매개 변수 값으로 컨트롤의 기본값을 설정할 수 있기를 원합니다. 나를 위해 (다른 제한 내에서).
  • 모든 매개 변수 유효성 검사를 직접 수행 할 수있어 기쁘고 서버 측 이벤트 방식에 많은 것이 필요하지 않습니다. 버튼 등에 이벤트를 첨부하는 대신 페이지로드시 모든 것을 설정하는 것은 정말 간단합니다.

이것의 대부분은 괜찮지 만 viewstate 를 완전히 제거하고 나머지 유용한 기능을 유지하는 방법을 찾지 못했습니다 . 이 블로그 게시물 의 게시물을 사용하여 viewstate에 대한 실제 을 얻는 것을 피할 수 있었지만 여전히 URL의 매개 변수로 끝나기 때문에 정말보기 흉해 보입니다.

ASP.NET 형식 대신 일반 HTML 형식으로 만들면 (즉, take out runat="server") 마법의 viewstate를 얻지 못하지만 프로그래밍 방식으로 컨트롤에 액세스 할 수 없습니다.

내가 할 수 ASP.NET의 대부분을 무시하고 XML에 LINQ와 XML 문서를 구축하고 구현하여이 모든 작업을 수행 IHttpHandler. 그래도 약간 낮은 수준입니다.

제약 조건을 완화하거나 (예 : POST를 사용하고 잉여 매개 변수를 고려하지 않음) ASP.NET MVC를 사용하여 문제를 해결할 수 있다는 것을 알고 있지만 요구 사항이 정말 비합리적입니까?

어쩌면 ASP.NET은 확장되지 않습니다 아래로 응용 프로그램의 종류에? 그러나 가능성이 매우 높은 대안이 있습니다. 저는 단지 어리석은 일이고, 제가 찾지 못한 완벽하게 간단한 방법이 있습니다.

어떤 생각, 누구? (강인한 사람이 어떻게 타락했는지에 대한 큐 코멘트 등등. 괜찮습니다. 저는 ASP.NET 전문가라고 주장한 적이 없었기를 바랍니다. 진실은 정반대입니다.)


16
"강자가 어떻게 타락했는지에 대한 단서 코멘트"-우리는 모두 무지하고 단지 다른 것들에 불과합니다. 나는 최근에 여기에 참여하기 시작했지만 모든 점보다 질문에 더 감탄했습니다. 당신은 분명히 여전히 생각하고 배우고 있습니다. 당신에게 명성.
duffymo

15
나는 배움을 포기한 사람에게주의를 기울이지 않을 것이라고 생각합니다. :)
Jon Skeet

1
일반적으로 사실입니다. 컴퓨터 과학에서 매우 사실입니다.
Mehrdad Afshari

3
다음 책은 "ASP.NET in Depth"가 될까요? :-P
chakrit

20
예, 2025 년 출시 예정입니다.)
Jon Skeet

답변:


76

이 솔루션은 컨트롤의 모든 속성을 포함하여 컨트롤 전체에 대한 프로그래밍 방식 액세스를 제공합니다. 또한 제출시 URL에 텍스트 상자 값만 표시되므로 GET 요청 URL이보다 "의미있는"상태가됩니다.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="JonSkeetForm.aspx.cs" Inherits="JonSkeetForm" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Jon Skeet's Form Page</title>
</head>
<body>
    <form action="JonSkeetForm.aspx" method="get">
    <div>
        <input type="text" ID="text1" runat="server" />
        <input type="text" ID="text2" runat="server" />
        <button type="submit">Submit</button>
        <asp:Repeater ID="Repeater1" runat="server">
            <ItemTemplate>
                <div>Some text</div>
            </ItemTemplate>
        </asp:Repeater>
    </div>
    </form>
</body>
</html>

그런 다음 코드 숨김에서 PageLoad에서 필요한 모든 작업을 수행 할 수 있습니다.

public partial class JonSkeetForm : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        text1.Value = Request.QueryString[text1.ClientID];
        text2.Value = Request.QueryString[text2.ClientID];
    }
}

이있는 양식을 원하지 않는 runat="server"경우 HTML 컨트롤을 사용해야합니다. 목적에 맞게 작업하는 것이 더 쉽습니다. 일반 HTML 태그를 사용 runat="server"하고 ID를 입력 하고 제공하십시오. 그럼 당신은 프로그래밍 방식으로 액세스 할 수 없이 코드ViewState .

유일한 단점은 GridViews 와 같은 "유용한"ASP.NET 서버 컨트롤에 액세스 할 수 없다는 것 입니다. Repeater결과와 동일한 페이지에 필드가 있고 (내가 아는 한) a Repeaterrunat="server"Form 태그 의 특성 없이 실행되는 유일한 DataBound 컨트롤 이라고 가정하기 때문에 내 예제에 a 를 포함했습니다 .


1
필드가 너무 적어서 수동으로 수행하는 것이 정말 쉽습니다. :) 핵심은 일반적인 HTML 컨트롤과 함께 runat = server를 사용할 수 있다는 사실을 몰랐습니다. 아직 결과를 구현하지 않았지만 쉬운 부분입니다. 거의 다 왔어!
Jon Skeet

실제로 <form runat = "server">는 페이지 수준에서 EnableViewState = "False"를 설정 한 경우에도 __VIEWSTATE (및 기타) 숨겨진 필드를 추가합니다. 페이지에서 ViewState를 풀고 싶다면 이것이 갈 방법입니다. Url 친화성에 관해서는 urlrewriting이 옵션 일 수 있습니다.
Sergiu Damian

1
다시 쓸 필요가 없습니다. 이 답변은 잘 작동합니다 (ID가 "사용자"인 컨트롤이 있음을 의미 함-어떤 이유로 텍스트 상자 컨트롤의 이름을 ID와 별도로 변경할 수 없음).
Jon Skeet

1
확인하기 위해 이것은 실제로 매우 잘 작동했습니다. 매우 감사합니다!
Jon Skeet

14
고전적인 ASP로 작성 했어야하는 것 같습니다!
ScottE

12

FORM 태그에 runat = "server"를 사용하지 않음으로써 확실히 (IMHO) 올바른 길을 가고 있습니다. 이는 다음 예제에서와 같이 Request.QueryString에서 직접 값을 추출해야 함을 의미합니다.

.aspx 페이지 자체에서 :

<%@ Page Language="C#" AutoEventWireup="true" 
     CodeFile="FormPage.aspx.cs" Inherits="FormPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>ASP.NET with GET requests and no viewstate</title>
</head>
<body>
    <asp:Panel ID="ResultsPanel" runat="server">
      <h1>Results:</h1>
      <asp:Literal ID="ResultLiteral" runat="server" />
      <hr />
    </asp:Panel>
    <h1>Parameters</h1>
    <form action="FormPage.aspx" method="get">
    <label for="parameter1TextBox">
      Parameter 1:</label>
    <input type="text" name="param1" id="param1TextBox" value='<asp:Literal id="Param1ValueLiteral" runat="server" />'/>
    <label for="parameter1TextBox">
      Parameter 2:</label>
    <input type="text" name="param2" id="param2TextBox"  value='<asp:Literal id="Param2ValueLiteral" runat="server" />'/>
    <input type="submit" name="verb" value="Submit" />
    </form>
</body>
</html>

그리고 코드 숨김에서 :

using System;

public partial class FormPage : System.Web.UI.Page {

        private string param1;
        private string param2;

        protected void Page_Load(object sender, EventArgs e) {

            param1 = Request.QueryString["param1"];
            param2 = Request.QueryString["param2"];

            string result = GetResult(param1, param2);
            ResultsPanel.Visible = (!String.IsNullOrEmpty(result));

            Param1ValueLiteral.Text = Server.HtmlEncode(param1);
            Param2ValueLiteral.Text = Server.HtmlEncode(param2);
            ResultLiteral.Text = Server.HtmlEncode(result);
        }

        // Do something with parameters and return some result.
        private string GetResult(string param1, string param2) {
            if (String.IsNullOrEmpty(param1) && String.IsNullOrEmpty(param2)) return(String.Empty);
            return (String.Format("You supplied {0} and {1}", param1, param2));
        }
    }

여기서 비결은 텍스트 입력의 value = ""속성 내에서 ASP.NET Literal을 사용하고 있으므로 텍스트 상자 자체가 runat = "server"일 필요가 없다는 것입니다. 그런 다음 결과는 ASP : Panel 내부에 래핑되고 결과를 표시할지 여부에 따라 페이지로드시 Visible 속성이 설정됩니다.


꽤 잘 작동하지만 URL은 예를 들어 StackOverflow만큼 친숙하지 않습니다.
Mehrdad Afshari

1
URL은 매우 친숙 할 것입니다. 제 생각에는 ... 이건 정말 좋은 솔루션 인 것 같습니다.
Jon Skeet

아아, 나는 이전에 당신의 트윗을 읽고 연구했다 그것을, 지금은 욕조 내 ittle 애들을 준비 ... :-) 질문을 놓친
splattne

2

좋아 Jon, 먼저 viewstate 문제 :

2.0 이후 내부 코드 변경이 있는지 확인하지 않았지만 몇 년 전에 viewstate를 제거하는 방법은 다음과 같습니다. 실제로 숨겨진 필드는 HtmlForm 내부에 하드 코딩되어 있으므로 새 필드를 파생하고 직접 호출을하는 렌더링 단계로 들어가야합니다. 평범한 이전 입력 컨트롤을 고수하는 경우 __eventtarget 및 __eventtarget을 남겨 둘 수도 있습니다 (클라이언트에서 JS를 요구하지 않는 데 도움이되기 때문에 원할 것 같습니다).

protected override void RenderChildren(System.Web.UI.HtmlTextWriter writer)
{
    System.Web.UI.Page page = this.Page;
    if (page != null)
    {
        onFormRender.Invoke(page, null);
        writer.Write("<div><input type=\"hidden\" name=\"__eventtarget\" id=\"__eventtarget\" value=\"\" /><input type=\"hidden\" name=\"__eventargument\" id=\"__eventargument\" value=\"\" /></div>");
    }

    ICollection controls = (this.Controls as ICollection);
    renderChildrenInternal.Invoke(this, new object[] {writer, controls});

    if (page != null)
        onFormPostRender.Invoke(page, null);
}

따라서 3 개의 정적 MethodInfo를 가져 와서 viewstate 부분을 건너 뛰고 호출합니다.)

static MethodInfo onFormRender;
static MethodInfo renderChildrenInternal;
static MethodInfo onFormPostRender;

다음은 양식의 유형 생성자입니다.

static Form()
{
    Type aspNetPageType = typeof(System.Web.UI.Page);

    onFormRender = aspNetPageType.GetMethod("OnFormRender", BindingFlags.Instance | BindingFlags.NonPublic);
    renderChildrenInternal = typeof(System.Web.UI.Control).GetMethod("RenderChildrenInternal", BindingFlags.Instance | BindingFlags.NonPublic);
    onFormPostRender = aspNetPageType.GetMethod("OnFormPostRender", BindingFlags.Instance | BindingFlags.NonPublic);
}

질문이 맞다면 POST를 양식의 작업으로 사용하지 않기를 원하므로 다음 방법을 사용하십시오.

protected override void RenderAttributes(System.Web.UI.HtmlTextWriter writer)
{
    writer.WriteAttribute("method", "get");
    base.Attributes.Remove("method");

    // the rest of it...
}

나는 이것이 거의 그것이라고 생각한다. 어떻게되는지 알려주세요.

편집 : 페이지 viewstate 메서드를 잊어 버렸습니다.

따라서 사용자 정의 Form : HtmlForm은 새로운 추상을 얻거나 얻습니다. Page : System.Web.UI.Page : P

protected override sealed object SaveViewState()
{
    return null;
}

protected override sealed void SavePageStateToPersistenceMedium(object state)
{
}

protected override sealed void LoadViewState(object savedState)
{
}

protected override sealed object LoadPageStateFromPersistenceMedium()
{
    return null;
}

이 경우에는 페이지를 봉인 할 수 없기 때문에 메서드를 봉인합니다 (추상적이지 않더라도 Scott Guthrie가이를 또 다른 항목으로 래핑합니다. : P). Form을 봉인 할 수 있습니다.


이것에 대해 감사합니다-다소 많은 일처럼 들리지만. Dan의 솔루션은 저에게 잘 맞았지만 항상 더 많은 옵션이있는 것이 좋습니다.
Jon Skeet

1

POST를 제거하지 않고 양식이 POST 될 때 적절한 GET URL로 리디렉션하는 것에 대해 생각해 보셨습니까? 즉, GET과 POST를 모두 수락하지만 POST에서 GET 요청을 생성하고 리디렉션합니다. 이것은 페이지에서 또는 페이지 독립적으로 만들고 싶다면 HttpModule을 통해 처리 할 수 ​​있습니다. 나는 이것이 일을 훨씬 쉽게 만들 것이라고 생각합니다.

편집 : 페이지에 EnableViewState = "false"가 설정되어 있다고 가정합니다.


좋은 생각. 음, ...의 측면에서 끔찍한 아이디어는 그것을 강요 받고 있지만 :) 아마 작업 그것의 측면에서 좋은 시도 것이다
존 소총

그리고 예, 저는 EnableViewState = false를 모든 곳에서 시도했습니다. 그것은 그것을 완전히 비활성화하는 것이 아니라 단지 줄입니다.
Jon Skeet

Jon : 저주받은 서버 컨트롤 (runat = "server"없음)을 사용하지 않고 <form runat = "server">가 전혀없는 경우 ViewState는 문제가되지 않습니다. 그래서 서버 컨트롤을 사용하지 말라고했습니다. 언제든지 Request.Form 컬렉션을 사용할 수 있습니다.
Mehrdad Afshari

그러나 컨트롤에 runat = server가 없으면 렌더링 할 때 컨트롤에 값을 다시 전파하는 것이 어렵습니다. 다행히 runat = server를 사용하는 HTML 컨트롤이 잘 작동합니다.
Jon Skeet

1

라우팅을 처리하는 HTTP 모듈 (MVC와 비슷하지만 정교하지는 않지만 몇 개의 if문)을 만들어 aspx또는 ashx페이지에 전달합니다. aspx페이지 템플릿을 수정하는 것이 더 쉽기 때문에 선호됩니다. 그러나 나는 사용하지 않을 것 WebControls입니다 aspx. 그냥 Response.Write.

그건 그렇고, 단순화하기 위해 모듈에서 매개 변수 유효성 검사를 수행하고 (아마도 라우팅과 코드를 공유하므로) 저장 HttpContext.Items한 다음 페이지에서 렌더링 할 수 있습니다. 이것은 모든 종소리와 휘파람없이 MVC와 거의 비슷하게 작동합니다. 이것은 내가 ASP.NET MVC 이전에 많이했던 일입니다.


1

나는 페이지 클래스를 완전히 포기하고 URL을 기반으로 한 큰 스위치 케이스로 모든 요청을 처리하게되어 정말 기뻤습니다. Evey "페이지"는 html 템플릿 및 ac # 개체가됩니다. 템플릿 클래스는 키 컬렉션과 비교하는 일치 대리자가있는 정규식을 사용합니다.

혜택:

  1. 재 컴파일 후에도 거의 지연이 없습니다 (페이지 클래스가 커야 함).
  2. 제어는 정말 세분화되어 있습니다 (SEO에 적합하고 JS와 잘 작동하도록 DOM을 제작)
  3. 프레젠테이션은 논리와 분리되어 있습니다.
  4. jQuery는 html을 완전히 제어합니다.

버머 :

  1. 간단한 작업은 단일 텍스트 상자에 여러 위치에 코드가 필요하다는 점에서 시간이 조금 더 걸리지 만 정말 잘 확장됩니다.
  2. 내가 viewstate (urgh)를 볼 때까지 페이지 보기로 만하는 것이 항상 유혹적이며 현실로 돌아갑니다.

Jon, 토요일 아침에 우리는 무엇을하고 있는가 :)?


1
여기는 토요일 저녁입니다. 괜찮아요? (내 게시 시간 / 일, btw에 대한 분산 그래프를보고 싶습니다 ...)
Jon Skeet

1

나는 asp : Repeater 컨트롤이 쓸모 없다고 생각했습니다.

ASP.NET 템플릿 엔진은 훌륭하지만 for 루프를 사용하여 쉽게 반복 할 수 있습니다.

<form action="JonSkeetForm.aspx" method="get">
<div>
    <input type="text" ID="text1" runat="server" />
    <input type="text" ID="text2" runat="server" />
    <button type="submit">Submit</button>
    <% foreach( var item in dataSource ) { %>
        <div>Some text</div>   
    <% } %>
</div>
</form>

ASP.NET Forms는 괜찮습니다. Visual Studio의 적절한 지원이 있지만이 runat = "server"는 잘못된 것입니다. ViewState to.

ASP.NET MVC가 왜 그렇게 훌륭하게 만들어 졌는지, 누가 ASP.NET Forms 접근 방식에서 모든 것을 버리지 않고 멀어지게하는지 살펴 보는 것이 좋습니다.

NHaml과 같은 사용자 지정 뷰를 컴파일하기 위해 자체 빌드 공급자 항목을 작성할 수도 있습니다. 여기에서 더 많은 제어를 확인하고 단순히 HTTP를 래핑하고 CLR 호스팅 환경으로 ASP.NET 런타임에 의존해야한다고 생각합니다. 통합 모드를 실행하면 HTTP 요청 / 응답도 조작 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.