Programming

ASP.NET에서 더 많은 제어권을 얻으려면 어떻게해야합니까?

procodes 2020. 7. 10. 21:30
반응형

ASP.NET에서 더 많은 제어권을 얻으려면 어떻게해야합니까?


나는 매우 간단한 "마이크로 웹앱"을 만들려고 노력하고 있는데, 나는 그것이 완료되면 몇 가지 스택 오버플로에 관심이있을 것으로 생각됩니다. 나는 C #에서 Depth 사이트, 즉 바닐라 ASP.NET 3.5 (즉 MVC 아님)에서 호스팅하고 있습니다.

흐름은 매우 간단합니다.

  • 사용자가 모든 매개 변수를 지정하지 않는 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.

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


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));
        }
    }

The trick here is that we're using ASP.NET Literals inside the value="" attributes of the text inputs, so the text-boxes themselves don't have to runat="server". The results are then wrapped inside an ASP:Panel, and the Visible property set on page load depending whether you want to display any results or not.


Okay Jon, the viewstate issue first:

I haven't checked if there's any kind of internal code change since 2.0 but here's how I handled getting rid of the viewstate a few years ago. Actually that hidden field is hardcoded inside HtmlForm so you should derive your new one and step into its rendering making the calls by yourself. Note that you can also leave __eventtarget and __eventtarget out if you stick to plain old input controls (which I guess you'd want to since it also helps not requiring JS on the client):

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);
}

So you get those 3 static MethodInfo's and call them out skipping that viewstate part out ;)

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

and here's your form's type constructor:

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);
}

If I'm getting your question right, you also want not to use POST as the action of your forms so here's how you'd do that:

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

    // the rest of it...
}

I guess this is pretty much it. Let me know how it goes.

EDIT: I forgot the Page viewstate methods:

So your custom Form : HtmlForm gets its brand new abstract (or not) 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;
}

In this case I seal the methods 'cause you can't seal the Page (even if it isn't abstract Scott Guthrie will wrap it into yet another one :P) but you can seal your Form.


Have you thought about not eliminating the POST but rather redirecting to a suitable GET url when the form is POSTed. That is, accept both GET and POST, but on POST construct a GET request and redirect to it. This could be handled either on the page or via an HttpModule if you wanted to make it page-independent. I think this would make things much easier.

EDIT: I assume that you have EnableViewState="false" set on the page.


I would create an HTTP module that handles routing (similar to MVC but not sophisticated, just a couple if statements) and hand it to aspx or ashx pages. aspx is preferred since it's easier to modify the page template. I wouldn't use WebControls in the aspx however. Just Response.Write.

By the way, to simplify things, you can do parameter validation in the module (as it shares code with routing probably) and save it to HttpContext.Items and then render them in the page. This will work pretty much like the MVC without all the bell and whistles. This is what I did a lot before ASP.NET MVC days.


I've really been happy to totally abandon the page class altogether and just handler every request with a big switch case based on url. Evey "page" becomes a html template and a c# object. The template class uses a regex with a match delegate that compares against a key collection.

benefits:

  1. It's really fast, even after a recompile, there is almost no lag (the page class must be big)
  2. control is really granular (great for SEO, and crafting the DOM to play well with JS)
  3. the presentation is separate from logic
  4. jQuery has total control of the html

bummers:

  1. simple stuff takes a bit longer in that a single text box requires code in several places, but it does scale up really well
  2. it's always tempting to just do it with page view until i see a viewstate (urgh) then i snap back to reality.

Jon, what are we doing on SO on a Saturday morning:) ?


I thought the asp:Repeater control was obsolete.

The ASP.NET template engine is nice but you can just as easily accomplish repeating with a for loop...

<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 is sort of okay, there's decent support from Visual Studio but this runat="server" thing, that's just wrong. ViewState to.

I suggest you take a look at what makes ASP.NET MVC so great, who it moves away from the ASP.NET Forms approach without throwing it all away.

You can even write your own build provider stuff to compile custom views like NHaml. I think you should look here for more control and simply relying on the ASP.NET runtime for wrapping HTTP and as a CLR hosting environment. If you run integrated mode then you'll be able to manipulate the HTTP request/response as well.

참고URL : https://stackoverflow.com/questions/431296/how-can-i-take-more-control-in-asp-net

반응형