Børre Stenseth
Moduler>Websites>Inputkontroll

Inputkontroll

Hva
Format og eksistenskontroll på vevsider

I så godt som alle webløsninger der brukeren skal bidra med data i en eller annen form har vi behov for å kontrollere at datanene som gis inn er korrekte i en eller annen betydning av ordet, f.eks.:

  • Hvis vi forventer at brukeren oppgir fødselsår må vi sjekke at det vi får inn er siffre, og vi vil kanskje sjekke at tallet er rimelig eller mulig som fødselsår.
  • Hvis vi forventer en dato må vi sjekke at formatet er riktig f.eks. åååå:mm:dd, og vi må sjekke at datoen eksisterer ( ikke: 2007:02:31) , og vi må kanskje sjekke at datoene er fram i tid dersom det er en bestilling, eller at den er rimelig langt bak i tid dersom det er en fødselsdag.
  • Hvis vi forventer en E-post adresse må vi sjekke at det vi får inn er en tekst med et slikt format som vi forventer at E-postadresser har

Ofte er det slik at denne sjekkingen faller i to deler. For det første vil vi sjekke at dataene er syntaktisk riktige. Det vil si at formatet på dataene er riktig. For det andre vil vi sjekke at dataene er rimelige eller lovlige. Det siste kan vi kalle en semantisk kontroll. Formatkontroll, syntakskontroll, gjør vi ofte enklest med regulæruttrykk, se nedenfor.

Vi kan gjøre slik kontroll på to måter. Vi kan kontrollere data før de sendes fra klienten og vi kan kontrollere data på tjeneren. I det første tilfellet bruker vi Javascript og i det andre tilfellet bruker det språket vi kjører på tjeneren, i vårt tilfelle C#. Her som ellers er det vel en god leveregel å ta fatt i problemene så nær kilden som mulig. Det vil si at vi ønsker å sjekke data før de sendes til tjeneren, dersom det er mulig. Det er to grunner til at dette kan være problematisk. Den ene grunnen er at Javascript er tyngere å programmere ikke-trivielle tester i og den andre grunnen er at sjekkingen kanskje er avhengig av data som vi bare har tilgang på på tjeneren. Det kan kanskje være en god rettesnor å gjøre syntakskontroll på klienten og semantisk kontroll på tjeneren.

I denne modulen skal vi:

  1. Lage en vevside med inputkontroll i handskrevet javascript. Denne løsningen er helt uavhengig av .Net
  2. Lage en vevside som benytter automatisk inputkontroll slik det tilbys i Visial studio for .Net-løsninger. Denne løsningen vil ha automatisk generert javascrpt.
  3. Kombinere strategiene ved at vi lager en vanlig aspx-side i Visual studio, men bruker vår egen håndlagde javascript til kontroll.

Håndlaget eksempel

La oss anta at vi skal registrere to felter: Fødselsdato og E-post adresse. Vi lager oss en form. Denne formen sender data til et skript som bare forteller hva vi har fylt inn. Du kan teste:

Født:
Epost:

Den aktuelle HTML-koden ser slik ut

<fieldset style="width:300px">
<form action="http://www.it.hiof.no/~borres/cgi-bin/demo/std.py" 
  method="post">
<table class="normal">
  <tr>
      <td>Født: </td> <td><input type="text" id="fdate0" name="fdate0"/></td>
  </tr>
  <tr>
      <td>Epost:</td><td><input type="text" id="email0" name="email0"/></td>
  </tr>
  <tr>
      <td> </td><td><input type="submit" value="Send"/></td>
  </tr>
  </table>
</form>
</fieldset>
    

Vi ser at verdien av action-attributten er adressen til et skript som gir oss en respons. I .Net ville action-attributten i den presenterte HTML-fila være navnet på websiden, f.eks.: Default.aspx.

Vi ønsker å legge til inputkontroll i Javascript. Vi begynner med å skrive en funksjon for hvert av de to feltene:

function controlDate(form)
{
    var bs=form.fdate.value;
    document.getElementById('error_date').innerHTML='';
      if (bs.length!=10)
      {
        document.getElementById('error_date').innerHTML='feil format';
        return false;
      }
      var regex=/(19|20)\d\d[:](0[1-9]|1[012])[:](0[1-9]|[12][0-9]|3[01])/;
      if (regex.test(bs))
      {
        return true;
      }
      document.getElementById('error_date').innerHTML='feil format';
      return false;
}
function controlMail(form)
{
    var ms=form.email.value;
    document.getElementById('error_mail').innerHTML='';
      if (ms.length==0)
      {
        document.getElementById('error_mail').innerHTML='feil format';
        return false;
      }
      var regex=/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
      if (regex.test(ms))
      {
        return true;
      }
      document.getElementById('error_mail').innerHTML='feil format';
      return false;
}
function controlForm(form){
    return controlDate(form) && controlMail(form);
}

Vi ser at vi har brukt regulære uttrykk for å teste syntaksen. Regulære uttrykk har ikke noen spesiel plass i dette materialet, men det er greitt å finn og tilpasse regulære uttrykk for mange situasjoner. [1] , [2]

Så må vi ta stilling til to ting:

  • Hvordan skal vi kalle funksjonene ? Vi velger å gjøre dette ved to begivenheter: Når et felt mister fokus (onblur) og når vi forsøker å sende formen (onclick).
  • Hvordan skal vi gi feilmelding. Vi gjør med et feilmeldingsfelt knyttet til hver av inputfeltene
Født: (åååå:mm:dd)
Epost: (nn@a.b)

Den aktuelle HTML-koden ser nå slik ut

<fieldset style="width:300px">
<form  action="http://www.it.hiof.no/~borres/cgi-bin/demo/std.py" 
  method="post">
  <table class="normal">
  <tr>
      <td>Født: (åååå:mm:dd)</td>
      <td>  <input type="text" maxlength="10" id="fdate" name="fdate" 
                 onblur="javascript:controlDate(this.form)"/></td>
      <td id="error_date" style="color:red"> </td>
  </tr>
  <tr>
      <td>Epost: (nn@a.b)</td>
      <td><input type="text" id="email" name="email"  
               onblur="javascript:controlMail(this.form)"/> </td>
      <td id="error_mail" style="color:red"> </td>
  </tr>
  <tr>
      <td> </td>
      <td><input type="submit" value="Send" 
             onclick="javascript:return controlForm(this.form);"/> </td>
      <td> </td>
  </tr>
  </table>
</form>
</fieldset>
    

Det er mange måter å gjøre dette på. I eksempelet over har jeg blandet document-modeller litt. Jeg har brukt uttrykk som form.email.value for å plukke opp verdien i E-post feltet, mens jeg har brukt document.getElementById('mail_error') for å identifisere feilmeldingsfeltet. Den første formuleringen forutsetter attributten name. Dersom jeg skulle vært konsekvent på W3C's modell, skulle jeg brukt den siste formuleringen også til å plukke opp E-post feltet, feks.: slik: document.getElementById('email').innerHTML. Selv dette er forøvrig ikke helt riktig siden jeg har brukt innerHTML som ikke er W3C standard.

Vi ser at kontrollen som er laget ovenfor plukker ikke opp ulovlige datoer. Det vil si at 2007:02:31 og 2007:04:31 vil passere. Vi kan ordne de med litt tillegg i javascriptkoden:

function checkdato(s){
  var strArray=s.split(":");
  y=parseInt(strArray[0]);
  m=parseInt(strArray[1]);
  d=parseInt(strArray[2]);
  if ((m==1)||(m==3)||(m==5)||(m==7)||(m==8)||(m==10)||(m== 12))
  {
      return (d <= 31);
  }
  else if ((m==4)||(m==6)||(m==9)||(m==11))
  {
      return (d <= 30);
  }
  else if (m == 2)
  {
      if (d <= 28)
          return true;
      if (d > 29)
          return false;
      // d==29
      if ((y % 4 == 0) && (y % 100 != 0))
          return true;
      return false;
  }
  else
    return false
}
Du kan teste en komplett fil herhttp://www.it.hiof.no/~borres/dn/webinputctrl/simpleform.html

Automatisk løsning

Vi ønsker å løse det samme problemet i Visual Studio. Vi begynner med å lage en website og møblere den med våre to inputfelter og sendeknappen. Så lager vi et objekt av typen: RegularExpressionValidator til hver av input feltene. Vi finner at Visual Stdudio er satt opp med en ferdig regexp-uttrykk for E-post, mens dato-uttrykket må vi legge inn selv.

Inputctrl.aspx ser slik ut i Visual Studio (endring i koding gjør at æøå ser litt rare ut):

<%@ Page Language="C#" AutoEventWireup="true"  
         CodeFile="Inputctrl.aspx.cs" Inherits="_Default" %>
<!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>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        &nbsp;<fieldset style="width: 510px">
    Født: (åååå:mm:dd)&nbsp;
            <asp:TextBox ID="TextBox1" runat="server" MaxLength="10"></asp:TextBox>
            <asp:RegularExpressionValidator ID="RegularExpressionValidator1" 
                 runat="server" ControlToValidate="TextBox1"
                ErrorMessage="Feil dato format" ValidationExpression="\d{4}:\d{2}:\d{2}"
                Width="131px"></asp:RegularExpressionValidator><br />
    Epost: (nn@a.b)&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;
            <asp:TextBox ID="TextBox2" runat="server" Width="194px"></asp:TextBox>
            <asp:RegularExpressionValidator ID="RegularExpressionValidator2" 
                   runat="server" ControlToValidate="TextBox2"
                ErrorMessage="Feil E-post format" 
                ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"
                Width="129px">
            </asp:RegularExpressionValidator><br />
            <br />
            <asp:Button ID="Button1" runat="server" Text="Send" 
                       OnClick="Button1_Click" /><br />
            </fieldset>
    <asp:Label ID="Label1" runat="server"></asp:Label>
    <div>Inspiser kildekoden for å se hvordan javascript er lagt til</div>
    <div><a href="javascript:history.back()">Tilbake</a></div>
    </form>
    </body>
</html>
Nettsiden som lages ser du herhttp://donau.hiof.no/borres/dn/demosite8/Inputctrl.aspx

Hvis du inspiserer kildekoden vil du se hvordan Visual Studio har lagt til javaskriptkode for å foreta inputkontrollen. Det er ikke uten videre enkelt å gå inn og modifisere denne koden. Det er kanskje grunn til å gjøre en vurdering av om det er lurt å bruke automatikken i Visual studio i alle sammenhenger.

Blandet løsning

Vi forsøker å blande de to strategiene på den måten at vi lager vår egen inputkontroll på en .aspx fil.

  • Vi lager en vanlig webside i Visual Studioog og møblerer den med våre to inputfelter og sendeknappen
  • Kopierer den javascriptfila vi brukte i det håndlagde eksempelet
  • Editerer websiden for hånd slik at den blir som vist nedenfor
<%@ Page Language="C#" AutoEventWireup="true" 
CodeFile="inputctl2.aspx.cs" Inherits="inputctl2" %>
<!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>Inputkontroll</title>
    <script language="javascript" type="text/javascript" src="control.js">
    </script>
</head>
<body>
    <fieldset>
    <form id="form1" name="form1" method="get" action="inputctl2.aspx" runat="server">
    <table cellpadding="5px" cellspacing="0px" border="0px">
        <tr>
            <td>Født: (åååå:mm:dd)</td>
            <td><input id="date" name="fdate" type="text" 
                       onblur="javascript:controlDate(this.form)" /></td>
            <td id="error_date"></td>
        </tr>
        <tr>
            <td>Epost: (nn@a.b)</td>
            <td><input id="mail" name="email" type="text" 
                       onblur="javascript:controlMail(this.form)" /></td>
            <td id="error_mail"></td>
        </tr>
        <tr>
            <td></td>
            <td></td>
            <td><input type="submit" name="Button1" value="Send" id="Button1" 
                      onclick="javascript:return controlForm(this.form)" /></td>
        </tr>    
    </table>
    </form>
    </fieldset>
    <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
</body>
</html>

Javaskriptet ser slik ut:

function controlDate(form)
{
    var bs=form.fdate.value;
    document.getElementById('error_date').innerHTML='';
      if (bs.length!=10)
      {
        document.getElementById('error_date').innerHTML=
            'feil format';
        return false;
      }
      var regex=/(19|20)\d\d[:](0[1-9]|1[012])[:](0[1-9]|[12][0-9]|3[01])/;
      if (regex.test(bs))
      {
        var ok=checkdate(bs);
        if(ok)
            return true;
        document.getElementById('error_date').innerHTML=
            'datoen ekisterer ikke';
        return false;
      }
      document.getElementById('error_date').innerHTML='feil format';
      return false;
}
function checkdate(s){
  var strArray=s.split(":");
  y=parseInt(strArray[0]);
  m=parseInt(strArray[1]);
  d=parseInt(strArray[2]);
  if ((m==1)||(m==3)||(m==5)||(m==7)||(m==8)||(m==10)||(m== 12))
  {
      return (d <= 31);
  }
  else if ((m==4)||(m==6)||(m==9)||(m==11))
  {
      return (d <= 30);
  }
  else if (m == 2)
  {
      if (d <= 28)
          return true;
      if (d > 29)
          return false;
      // d==29
      if ((y % 4 == 0) && (y % 100 != 0))
          return true;
      return false;
  }
  else
    return false
}
function controlMail(form)
{
    var ms=form.email.value;
    document.getElementById('error_mail').innerHTML='';
      if (ms.length==0)
      {
        document.getElementById('error_mail').innerHTML='feil format';
        return false;
      }
      var regex=/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
      if (regex.test(ms))
      {
        return true;
      }
      document.getElementById('error_mail').innerHTML='feil format';
      return false;
}

function controlForm(form)
{
   return controlDate(form) && controlMail(form);
}

Nettsiden som lages ser du herhttp://donau.hiof.no/borres/dn/demosite8/inputctl2.aspx

Koden som går på serveren ser slik ut:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections.Specialized;
public partial class inputctl2 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (this.IsPostBack)
        {
            NameValueCollection par=this.Request.QueryString;
            String met = Request.HttpMethod;
            Label1.Text = String.Format("Mottatt({0}): date={1}, EMail={2}",
                                        met, par["fdate"], par["email"]);
        }
        else
        {
            Label1.Text = "Here we go";
        }
    }
    
}
Referanser
  1. Regular Expression LibraryRegExLib.comregexlib.com/14-09-2010
  1. Regulæruttrykk, en innføringwww.regular-expressions.info/tutorial.html14-03-2010
.Net prosjekt, både den automatiske og den blandede løsningen:
https://svn.hiof.no/svn/psource/Csharpsites/demosite8
Vedlikehold

B.Stenseth, januar 2007

(Velkommen) Moduler>Websites>Inputkontroll (Webservices)