2009-01-10 16 views
122

Estoy tratando de 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 de vainilla (es decir, no MVC).¿Cómo puedo tener más control en ASP.NET?

el flujo es muy simple:

  • Si un usuario introduce la aplicación con una URL que no especifica todos los parámetros (o si alguno de ellos no son válidos) Quiero simplemente mostrar la entrada del usuario controles. (Hay sólo dos son.)
  • Si un usuario introduce la aplicación con una URL que hace tienen todos los parámetros necesarios, quiero mostrar los resultados y los controles de entrada (para que puedan cambiar los parámetros)

Éstos son mis requisitos autoimpuestas (mezcla de diseño e implementación):

  • Quiero que la sumisión a usar GET en lugar de POST, sobre todo por lo que los usuarios pueden marcar la página fácilmente.
  • I no desea querer que la URL termine pareciendo tonta después del envío, con partes y fragmentos extraños en ella. Solo la URL principal y los parámetros reales, por favor.
  • Idealmente me gustaría evitar la necesidad de JavaScript en absoluto. 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).
  • Estoy contento de hacer toda la validación de parámetros yo mismo, y no necesito mucho en el camino de los eventos del lado del servidor. Es muy fácil de configurar todo al cargar la página en lugar de asociar eventos a los botones etc.

La mayor parte de esto está bien, pero no he encontrado ninguna manera de completamente retirar el estado de vista y mantener el resto de la funcionalidad útil. Usando la publicación de this blog post he logrado evitar obtener valor real para viewstate, pero todavía 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, eliminar runat="server"), entonces no obtengo ningún estado de visualización mágico, pero no puedo acceder a los controles mediante programación.

I podía hacer todo esto haciendo caso omiso de la mayoría de ASP.NET y la creación de un documento XML con LINQ to XML, y la implementación de IHttpHandler. Sin embargo, eso parece un poco bajo nivel.

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

Quizás ASP.NET simplemente no escala abajo para este tipo de aplicación? 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.

¿Alguna idea, alguien? (Cue comenta cómo cayeron los poderosos, etc. Está bien - Espero nunca haber afirmado ser un experto en ASP.NET, ya que la verdad es todo lo contrario ...)

+15

"Cue comentarios de cómo los poderosos están caídos" - todos ignoramos, solo de cosas diferentes. Recientemente comencé a participar aquí, pero admiro la pregunta más que todos los puntos. Obviamente, sigues pensando y aprendiendo. Felicitaciones a usted. – duffymo

+15

No creo que alguna vez le preste atención a alguien que había dejado de aprender :) –

+1

Verdad en el caso general. Muy cierto en informática. –

Respuesta

74

Esta solución le dará acceso programático a los controles en su totalidad, incluidos todos los atributos en los controles. Además, sólo los valores del cuadro de texto aparecerá en la URL a la presentación para que su solicitud de URL 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 el código subyacente que 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 tiene runat="server", 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. A continuación, puede acceder a ellos programáticamente y código sin ViewState.

El único inconveniente es que no tendrá acceso a muchos de los "útiles" controles del servidor ASP.NET como GridView s. Incluí un Repeater en mi ejemplo porque asumo 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 Formulario .

+1

Tengo tan pocos campos que hacerlo manualmente es realmente fácil :) La clave era que no sabía que podía use runat = server con controles HTML normales. No he implementado los resultados todavía, pero eso es lo fácil. ¡Cerca de allí! –

+0

De hecho, un

agregará el campo oculto __VIEWSTATE (y algunos otros) incluso si configura EnableViewState = "False" a nivel de página. Este es el camino a seguir si quiere perder el ViewState en la página. En cuanto a la facilidad de Url, urlrewriting podría ser una opción. –

+1

No es necesario volver a escribir. Esta respuesta funciona bien (aunque significa tener un control con una ID de "usuario" - por alguna razón no puedo cambiar el nombre de un control de cuadro de texto por separado de su ID). –

1

¿Has pensado en no eliminando el POST pero redirigiendo a una url GET adecuada cuando el formulario es POSTed. 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.

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

+0

Buena idea. Bueno, horrible idea en términos de ser forzado a hacerlo, pero bueno en términos de que probablemente funcione :) Lo intentaré ... –

+0

Y sí, he intentado con EnableViewState = false por todas partes. No lo deshabilita por completo, simplemente lo reduce. –

+0

Jon: Si no usa los malditos controles de servidor (no runat = "servidor") y no tiene un en absoluto, ViewState no será un problema. Es por eso que dije que no use controles de servidor. Siempre puedes usar la colección Request.Form. –

1

me gustaría crear un módulo HTTP que se encarga de enrutamiento (similar a MVC, pero no sofisticado, sólo un par if declaraciones) y entregarlo a aspxashx o páginas. aspx es preferible ya que es más fácil modificar la plantilla de página. Sin embargo, no usaría WebControls en el aspx. Solo 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 renderizarlos 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.

12

Definitivamente (en mi humilde opinión) va por buen camino al no usar runat = "servidor" en su etiqueta FORM. Esto sólo significa que necesita para extraer los valores de la Request.QueryString directamente, aunque, 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 literales dentro del value = "" atributos de las entradas de texto, por lo que los cuadros de texto a sí mismos no tenemos 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.

+0

Funciona bastante bien, pero las URL no serán tan amigables como, por ejemplo, StackOverflow. –

+1

Las URL serán bastante amigables, creo ... Esto parece una muy buena solución. –

+0

Argh, leí tus tweets antes, lo había investigado, y ahora me perdí tu pregunta preparando mis pequeños bebés para la bañera ... :-) – splattne

1

Realmente he sido feliz de abandonar por completo la clase de página y solo manejar cada solicitud con una gran caja de conmutador basada en url. Evey "página" se convierte en una plantilla html y un objeto C#. 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 muy rápido, incluso después de una recompilación, no hay casi ningún retraso (la clase de página debe ser grande)
  2. control es muy granular (ideal para SEO, y la elaboración del DOM jugar bien con JS)
  3. la presentación es independiente de la lógica
  4. jQuery tiene el control total de la html

bummers:

  1. cosas simples tarda un poco más en que un único cuadro de texto requiere código en varios lugares, pero sí Escala de la realmente bien
  2. Siempre es tentador sólo lo hacen con vistas página hasta Veo un viewstate (urgh) y luego vuelvo a realidad.

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

+1

Es sábado por la noche aquí. ¿Eso lo hace bien? (Me encantaría ver un gráfico de dispersión de mi publicación veces/días, por cierto ...) –

1

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

El motor de plantillas ASP.NET es agradable, pero se puede lograr con la misma facilidad con la repetición de un bucle 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> 

formularios ASP.NET es una especie de bien, no es conveniente sustento de Visual Studio, pero este runat = "servidor", eso está mal. ViewState to.

Te sugiero que eches un vistazo a lo que hace tan grande a ASP.NET MVC, que se aleja del enfoque de ASP.NET Forms sin tirarlo todo por la borda.

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

2

bien Jon, la cuestión estado de vista en primer lugar:

No he comprobado si hay algún tipo de cambio en el código interno desde 2.0, pero aquí es cómo manejó deshacerse del estado de vista 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 si usted se pega a los controles de entrada llanura de edad (que supongo que te gustaría, ya que tampoco ayuda a que requiere 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); 
} 

para que pueda obtener los 3 estática de MethodInfo y llamarlos a cabo saltarse esa parte estado de vista a cabo;)

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

y aquí es de tipo constructor de su forma:

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 yo estoy haciendo la pregunta correcta, y ou también quieren no utilizar POST como la acción de sus formas así que aquí te mostramos cómo hacerlo:

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 la misma. Déjame saber como va.

EDIT: se me olvidó los métodos de página de estado de vista:

Así el formulario personalizado: HtmlForm obtiene su nuevo resumen (o no) Página: 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; 
} 

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.

+0

Gracias por esto, aunque parece mucho trabajo. La solución de Dan funcionó bien para mí, pero siempre es bueno tener más opciones. –

Cuestiones relacionadas