tutorial programacion pages net mvc lenguaje framework definicion asp active .net asp.net .net-3.5 viewstate

programacion - asp.net tutorial



¿Cómo puedo tomar más control en ASP.NET? (7)

Estoy intentando construir una "microwebapp" muy, muy simple que sospecho que será de interés para algunos Stack Overflow''rs si alguna vez lo hago. Lo estoy alojando en mi sitio C # en profundidad, que es ASP.NET 3.5 vainilla (es decir, no MVC).

El flujo es muy simple:

  • Si un usuario ingresa la aplicación con una URL que no especifica todos los parámetros (o si alguno de ellos no es válido), solo quiero mostrar los controles de entrada del usuario. (Solo hay dos)
  • Si un usuario ingresa la aplicación con una URL que tiene todos los parámetros requeridos, quiero mostrar los resultados y los controles de entrada (para que puedan cambiar los parámetros)

Estos son mis requisitos autoimpuestos (mezcla de diseño e implementación):

  • Quiero que el envío use GET en lugar de POST, principalmente para que los usuarios puedan marcar la página fácilmente.
  • No quiero que la URL termine pareciendo tonta después de la sumisión, con retazos extraños en ella. Solo la URL principal y los parámetros reales, por favor.
  • Lo ideal sería evitar evitar el uso de JavaScript. No hay una buena razón para ello en esta aplicación.
  • Quiero poder acceder a los controles durante el tiempo de renderizado y establecer valores, etc. En particular, deseo poder establecer los valores predeterminados de los controles en los valores de los parámetros pasados, si ASP.NET no puede hacer esto automáticamente para mí (dentro de las otras restricciones).
  • Me complace hacer toda la validación de parámetros, y no necesito mucho en el camino de los eventos del lado del servidor. Es muy sencillo configurar todo en la carga de la página en lugar de adjuntar eventos a botones, etc.

La mayor parte de esto está bien, pero no he encontrado ninguna forma de eliminar por completo ViewState y mantener el resto de la funcionalidad útil. Al utilizar la publicación de esta publicación de blog , he logrado evitar obtener un valor real para viewstate, pero aún así termina como un parámetro en la URL, que se ve realmente feo.

Si lo hago un formulario HTML sin formato en lugar de un formulario ASP.NET (es decir, saco runat="server" ), entonces no obtengo ningún estado visual mágico, pero entonces no puedo acceder a los controles programáticamente.

Podría hacer todo esto ignorando la mayor parte de ASP.NET y IHttpHandler un documento XML con LINQ a XML, e implementando IHttpHandler . Sin embargo, eso parece un poco bajo nivel.

Me doy cuenta de que mis problemas podrían resolverse relajando mis restricciones (por ejemplo, utilizando POST y sin preocuparme por el parámetro de excedente) o utilizando ASP.NET MVC, pero ¿son mis requisitos realmente irrazonables?

Tal vez ASP.NET simplemente no se reduce a este tipo de aplicaciones. Sin embargo, hay una alternativa muy probable: solo estoy siendo estúpido, y hay una manera perfectamente simple de hacerlo que simplemente no he encontrado.

Cualquier pensamiento, alguien? (Cue comenta cómo cayeron los poderosos, etc. Está bien; espero que nunca haya afirmado ser un experto en ASP.NET, ya que la verdad es todo lo contrario ...)


¿Ha pensado en no eliminar el POST sino en redirigir a una url GET adecuada cuando el formulario está PUBLICADO? Es decir, acepte tanto GET como POST, pero en POST construya una solicitud GET y redirija a ella. Esto podría manejarse en la página o a través de un HttpModule si desea hacerlo independiente de la página. Creo que esto haría las cosas mucho más fáciles.

EDITAR: Supongo que tiene EnableViewState = "false" establecido en la página.


Bien, Jon, el problema de ViewState primero:

No he comprobado si hay algún tipo de cambio de código interno desde 2.0, pero así es como me las arreglé para deshacerme del viewstate hace unos años. En realidad, ese campo oculto está codificado dentro de HtmlForm, por lo que debe derivar uno nuevo y pasar a su representación realizando las llamadas usted mismo. Tenga en cuenta que también puede dejar __eventtarget y __eventtarget out si se atiene a los controles de entrada normales (lo que supongo que querría ya que también ayuda a no requerir JS en el cliente):

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

Así que obtienes esas 3 metodologías estáticas de MethodInfo y las llamas omitiendo esa parte viewstate;)

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

y aquí está el constructor de tipos de tu formulario:

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

Si recibo correctamente su pregunta, también desea no utilizar POST como la acción de sus formularios, así es como lo haría:

protected override void RenderAttributes(System.Web.UI.HtmlTextWriter writer) { writer.WriteAttribute("method", "get"); base.Attributes.Remove("method"); // the rest of it... }

Supongo que esto es más o menos. Déjame saber como va.

EDITAR: Olvidé los métodos viewtest de la página:

Entonces, su Formulario personalizado: HtmlForm obtiene su nueva página abstracta (o no): System.Web.UI.Página: 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; }

En este caso, sello los métodos porque no se puede sellar la Página (incluso si no es abstracto, Scott Guthrie lo incluirá en otra: P) pero puede sellar su Formulario.


Crearía un módulo HTTP que maneje el enrutamiento (similar a MVC pero no sofisticado, solo un par de enunciados if ) y ashx páginas aspx o ashx . aspx es preferido ya que es más fácil modificar la plantilla de página. Sin embargo, no usaría WebControls en el aspx . Just Response.Write .

Por cierto, para simplificar las cosas, puede hacer la validación de parámetros en el módulo (ya que comparte el código con el enrutamiento probablemente) y guardarlo en HttpContext.Items y luego representarlos en la página. Esto funcionará más o menos como el MVC sin todos los detalles. Esto es lo que hice mucho antes de los días de ASP.NET MVC.


Definitivamente (IMHO) está en el camino correcto al no usar runat = "servidor" en su etiqueta FORM. Sin embargo, esto significa que tendrá que extraer valores de Request.QueryString directamente, como en este ejemplo:

En la página .aspx en sí:

<%@ 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>

y en el código subyacente:

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

El truco aquí es que estamos usando ASP.NET Literals dentro de los atributos value = "" de las entradas de texto, por lo que los cuadros de texto no tienen que runat = "server". Los resultados se envuelven dentro de un ASP: Panel y la propiedad Visible establecida en la carga de la página depende de si desea mostrar los resultados o no.


Esta solución le dará acceso programático a los controles en su totalidad, incluidos todos los atributos en los controles. Además, solo aparecerán los valores del cuadro de texto en la URL al momento de la presentación, por lo que su URL de solicitud GET será más "significativa".

<%@ 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>

Luego, en su código subyacente puede hacer todo lo que necesita en 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]; } }

Si no desea un formulario que tenga runat="server" , entonces debe usar controles HTML. Es más fácil trabajar para tus propósitos. Simplemente use etiquetas HTML normales y ponga runat="server" y asígneles una identificación. Luego puede acceder a ellos programáticamente y codificar sin un ViewState .

El único inconveniente es que no tendrá acceso a muchos de los controles de servidor ASP.NET "útiles" como GridView . Repeater un Repeater en mi ejemplo porque supongo que desea tener los campos en la misma página que los resultados y (que yo sepa) un Repeater es el único control DataBound que se ejecutará sin un atributo runat="server" en la etiqueta de forma.


Pensé que el asp: el control del repetidor era obsoleto.

El motor de plantillas ASP.NET es agradable, pero puede repetir fácilmente con un ciclo 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 está más o menos bien, hay soporte decente de Visual Studio, pero esto runat = "server", eso es simplemente incorrecto. ViewState to.

Sugiero que echen un vistazo a lo que hace tan atractivo a ASP.NET MVC, que se aleja del enfoque de ASP.NET Forms sin deshacerse de todo.

Incluso puede escribir su propio material de compilación para compilar vistas personalizadas como NHaml. Creo que debería buscar aquí más control y simplemente confiar en el tiempo de ejecución de ASP.NET para envolver HTTP y como entorno de alojamiento CLR. Si ejecuta el modo integrado, también podrá manipular la solicitud / respuesta HTTP.


Realmente me alegré de abandonar por completo la clase de página y manejar todas las solicitudes con una gran caja de conmutadores basada en url. Evey "página" se convierte en una plantilla html y un objeto ac #. La clase de plantilla usa una expresión regular con un delegado de coincidencia que se compara con una colección de claves.

beneficios:

  1. Es realmente rápido, incluso después de una recompilación, casi no hay retraso (la clase de la página debe ser grande)
  2. el control es realmente granular (ideal para SEO, y la elaboración del DOM para jugar bien con JS)
  3. la presentación está separada de la lógica
  4. jQuery tiene control total del html

bummers:

  1. Las cosas simples tardan un poco más en que un cuadro de texto único requiere código en varios lugares, pero se escala muy bien
  2. Siempre es tentador hacerlo con la vista de página hasta que vea un estado de vista (urgh) y vuelva a la realidad.

Jon, ¿qué hacemos en SO un sábado por la mañana :)?