저는 아주 간단한 “마이크로 웹앱”을 만들려고 노력하고 있습니다.이 작업을 완료하면 몇 명의 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 전문가라고 주장한 적이 없었기를 바랍니다. 진실은 정반대입니다.)
답변
이 솔루션은 컨트롤의 모든 속성을 포함하여 컨트롤 전체에 대한 프로그래밍 방식 액세스를 제공합니다. 또한 제출시 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
.
유일한 단점은 GridView
s 와 같은 “유용한”ASP.NET 서버 컨트롤에 액세스 할 수 없다는 것 입니다. Repeater
결과와 동일한 페이지에 필드가 있고 (내가 아는 한) a Repeater
가 runat="server"
Form 태그 의 특성 없이 실행되는 유일한 DataBound 컨트롤 이라고 가정하기 때문에 내 예제에 a 를 포함했습니다 .
답변
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 속성이 설정됩니다.
답변
좋아 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을 봉인 할 수 있습니다.
답변
POST를 제거하지 않고 양식이 POST 될 때 적절한 GET URL로 리디렉션하는 것에 대해 생각해 보셨습니까? 즉, GET과 POST를 모두 수락하지만 POST에서 GET 요청을 생성하고 리디렉션합니다. 이것은 페이지에서 또는 페이지 독립적으로 만들고 싶다면 HttpModule을 통해 처리 할 수 있습니다. 나는 이것이 일을 훨씬 쉽게 만들 것이라고 생각합니다.
편집 : 페이지에 EnableViewState = “false”가 설정되어 있다고 가정합니다.
답변
라우팅을 처리하는 HTTP 모듈 (MVC와 비슷하지만 정교하지는 않지만 몇 개의 if
문)을 만들어 aspx
또는 ashx
페이지에 전달합니다. aspx
페이지 템플릿을 수정하는 것이 더 쉽기 때문에 선호됩니다. 그러나 나는 사용하지 않을 것 WebControls
입니다 aspx
. 그냥 Response.Write
.
그건 그렇고, 단순화하기 위해 모듈에서 매개 변수 유효성 검사를 수행하고 (아마도 라우팅과 코드를 공유하므로) 저장 HttpContext.Items
한 다음 페이지에서 렌더링 할 수 있습니다. 이것은 모든 종소리와 휘파람없이 MVC와 거의 비슷하게 작동합니다. 이것은 내가 ASP.NET MVC 이전에 많이했던 일입니다.
답변
나는 페이지 클래스를 완전히 포기하고 URL을 기반으로 한 큰 스위치 케이스로 모든 요청을 처리하게되어 정말 기뻤습니다. Evey “페이지”는 html 템플릿 및 ac # 개체가됩니다. 템플릿 클래스는 키 컬렉션과 비교하는 일치 대리자가있는 정규식을 사용합니다.
혜택:
- 재 컴파일 후에도 거의 지연이 없습니다 (페이지 클래스가 커야 함).
- 제어는 정말 세분화되어 있습니다 (SEO에 적합하고 JS와 잘 작동하도록 DOM을 제작)
- 프레젠테이션은 논리와 분리되어 있습니다.
- jQuery는 html을 완전히 제어합니다.
버머 :
- 간단한 작업은 단일 텍스트 상자에 여러 위치에 코드가 필요하다는 점에서 시간이 조금 더 걸리지 만 정말 잘 확장됩니다.
- 내가 viewstate (urgh)를 볼 때까지 페이지 보기로 만하는 것이 항상 유혹적이며 현실로 돌아갑니다.
Jon, 토요일 아침에 우리는 무엇을하고 있는가 :)?
답변
나는 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 요청 / 응답도 조작 할 수 있습니다.