Undvik konverteringsfel med nullvärden från databasen

IBland får jag felet "Input string was not in a correct format." vid funktioner enligt nedan och det finns null-värde i databasen.

<%# GetUser(Convert.ToInt32(DataBinder.Eval(Container.DataItem, "UserId"))) %>

Då kan man göra så här för att parera nullvärdena och istället skicka in en nolla.

<%# GetUser(Eval("UserId") == DBNull.Value ? 0 : Convert.ToInt32(Eval("UserId"))) %>

Eller i Code Behind:

int userId = objDataReader["UserId"] == DBNull.Value ? 0 : Convert.ToInt32(objDataReader["UserId"]);

By Jesper Lind

Kopiera rader mellan GridViews - En enkel operation?

Man kan tro att det är världens enklaste sak att kopiera en rad från en GridView till en annan. Men den metoden många försöker med först resulterar ofta i "This row already belongs to another table". Här visar vi en enkel metod för att lösa problemet.

DataTable newtable = new DataTable();
DataTable temptable = new DataTable();
CheckBox cb;
BLL bll = new BLL();//Our Business Layer
foreach(GridViewRow row in GridView2.Rows)
{
if (row.RowType == DataControlRowType.DataRow)
{
cb = row.FindControl("CheckBox1") as CheckBox;
if (cb.Checked == true)
{
temptable.Clear();
temptable = bll.GetItem(Convert.ToInt32(row.Cells[0].Text));
newtable.ImportRow(temptable[0]);
}
}
}
GridView3.DataSource = newtable;
GridView3.DataBind();

Trixet är alltså att skapa en tabell med den raden man vill kopiera och sedan importera den till en ny tabell. Det gå inte att bara flytta över DataRowObjektet utan att få ett fel.

Denna koden kopierar alla rader som är "checked" från gridview2 till gridview3

cell[0] innehåller Id i gridview 2. (row.datakey skulle säkert funka lika bra)

By Glenn Lundberg

Lansering av Silverlight 1.0

http://www.codeodyssey.se/upload/resource/blog/silverlight_anims.jpg

Silverlight har nu släppts i version 1.0, läs mer hos bland Microsoft representanterna Robert Folkesson och Scott Guthrie. För er som inte är insatta i tekniken så handlar det om ett plugin till webbläsare för att presentera multimedia och erbjuda interaktivitet (ungefär som Adobe's Flash-spelaren).

Man tillkännager samtidigt att man kommer samarbeta med Novell och hjälpa dem med att utveckla en Linux-baserad implementation av tekniken. På Linux kommer denna få namnet Moonlight läs mer hos Mono-utvecklaren Miguel de Icaza.

Version 1.0 av Silverlight programmeras om jag har förstått det hela rätt genom Javascript-kod och man behöver bara en texteditor för att skapa denna. Det går att ladda ner en utökning till Visual Studio för att få Intellisense på Javascript för Silverlight.

Nästa version 1.1 kommer stödja ASP.NET och finns än så länge bara i Alpha-släpp för utvecklare att testa med. På Silverlight-sajten finns alla verktyg som man behöver för att komma igång med att utveckla applikationer.

Vi ser fram emot att prova på Silverlight-utveckling och tror väldigt starkt på tekniken. IDG har också skrivit en notis om det.

By Jesper Lind

Inkludera CSS-filer med Code Behind

Att inkludera CSS-filer dynamiskt är något jag märker att vi gör allt oftare i projekten. Så dags att spara på bloggen hur man gör så vi slipper hitta exempel varje gång.

HtmlGenericControl includeCss = new HtmlGenericControl("link");
includeCss.Attributes.Add("type", "text/css");
includeCss.Attributes.Add("rel", "stylesheet");

includeCss.Attributes.Add("href", css.ToString());
this.Page.Header.Controls.Add(includeCss);

Se även hur man gör för att inkludera Javascript-filer och kodblock från .NET-kod.

By Jesper Lind

Validera CheckBox med CustomValidator

Försökte att lägga till en RequiredFieldValidator för att kontrollera att en CheckBox var ikryssad vid postning, men det gick inte så bra. Felet blev:

Control 'CheckBox1' referenced by the ControlToValidate property of
'RequiredFieldValidator1' cannot be validated.

Någonstans läste jag att man inte lagt till denna möjlighet på CheckBox för att "bägge värdena alltid är riktiga". Det kanske kan vara ett bra argument, men jag ville endå ha denna funktionelitet. I mitt fall gällde det en ruta som användaren måste kryssa i för att fortsätta.

Så jag gjorde det med en CustomValidator istället, så här ser uppmärkningen ut på sidan.

<asp:CheckBox id="CheckBox1" runat="server"></asp:CheckBox>
<br />
<asp:CustomValidator ClientValidationFunction="ValidateChecked"
Runat="server" ID="termsReadUnchecked" ErrorMessage="You must tick this box to continue"/>

Och i koden bakom lägger jag till JavaScript-funktionen för valideringen.

HtmlGenericControl validateCheckedScript = new HtmlGenericControl("script");
validateCheckedScript.Attributes.Add("type", "text/javascript");
validateCheckedScript.InnerHtml = "function ValidateChecked(src, args){args.IsValid=document.getElementById(\"" + ChckBox1.ClientID + "\").checked;}";
this.Page.Header.Controls.Add(validateCheckedScript);

By Jesper Lind

Skicka mail med IIS på din lokala maskin

Har ägnat nån timme åt att försöka skicka mail med SMTP-servern i IIS 5.1 och nu lyckats få iväg några mail. Här går jag igenom de steg jag fick göra för att få det att fungera.

Mailkoden

Själva mailkoden jag använder i applikationen använder System.Net.Mail och ser ut ungefär så här:

SmtpClient smtpClient = new SmtpClient("localhost");
//smtpClient.DeliveryMethod = SmtpDeliveryMethod.PickupDirectoryFromIis;
MailAddress from = new MailAddress("from@dummysite.domain", "Alias from name");
MailAddress to = new MailAddress("to@dummysite.domain");
MailMessage mail = new MailMessage(from, to);
mail.IsBodyHtml = true;

mail.Subject = "From my local computer";
mail.Body = "Testing, testing";

smtpClient.Send(mail);

"SmtpDeliveryMethod.PickupDirectoryFromIis" var något jag trodde behövdes ett tag, eftersom jag läste om det på ett forum, men det visade sig att det gick bra att skicka även utan.

Ställa in SMTP-Server

Det problemet kom i form av ett felmeddelandet som såg ut så här:

Mailbox unavailable. The server response was: 5.7.1 Unable to relay for ...

Lösningen till detta hittade jag på CodeBetter och innebar att lägga till 127.0.0.1 som giltigt IP-nummer för att skicka mail från SMTP-server. (Egenskaper / Åtkomst / Anslutning / Endast datorer i listan nedan)

När detta var klart så försökte jag skicka igen, nu fungerade det i applikationen men mailen fastnade i C:\Inetpub\mailroot\Queue.

Lösningen på detta fann jag på Velocityreviews där medlemmarna förklarade att man behövde lägga till en giltigt SMTP från sin ISP. (Egenskaper / Leverans / Avancerat / Smart värd)

Hoppas detta kan hjälpa någon som vill kunna skicka mail från sin egen dator med ASP.NET.

By Jesper Lind

Posta form-data till en extern URL

Jag kommer ihåg när man jobbade med klassisk ASP, och gick över till ASP.NET, att begreppet PostBack var rätt förvirrande. Typ: "Vadå ska varje sida posta tillbaks till sig själv?" Efter ett tag förstod man finessen och nu skulle skulle jag inte vilja byta tillbaks till hur föregångaren fungerade.

Ibland vill man dock kunna posta data från ett formulär till en annan URL, speciellt när man har att göra med betalväxlar (t ex Postens betalväxel), som ofta fungerar så.

PostBackUrl

I ASP.NET 1.1 var det rätt begränsade möjligheter men i 2.0 fick vi attributet PostBackUrl som kan sättas på Button, LinkButton och ImageButton. Har aldrig använt det själv men borde fungera bra.

HttpWebRequest

Med HttpWebRequest kan man skicka webbströmmar med data. Passar bäst om man vill att postningen ska ske i bakgrunden om jag förstått det rätt, och inte lika bra om man vill att webbläsaren ska följa URLen, som postningen sker till. Jag kan ha fel här för har inte provat så mycket med denna metod. Här är ett exempel som verkar rätt lätt att förstå.

Med JavaScript

Man kan lägga till ett extra formulär på sidan och posta detta genom att registrera ett javascript, från koden bakom, ungefär så här.

string sendScript = "<script language='javascript'>document.getElementById('paymentform').submit();<"+"/script>";
Page.RegisterStartupScript("SendPayment", sendScript);

På det extra formuläret anger man den externa URLen i action och variablerna som hidden-inputs.

<form id="paymentform" name="paymentform" action="https://thepaymentservice.domain" method="post">
<input type="hidden" id="Card_num" runat="server">
<input type="hidden" id="Currency" runat="server">
<input type="hidden" id="Amount" runat="server">
</form>

Eller så bygger man formuläret själv

Här är ett exempel på hur man kan använda HttpContext för att helt enkelt bygga ihop formuläret själv med html-taggar. Det är skrivet av Jigar Desai och finns att ladda hem på C# Corner.

Här kommer lätt modifierad variant:

using System;
using System.Web;
using System.Text;
using System.Collections.Specialized;

/// <summary>
/// Summary description for RemotePost
/// </summary>

public class RemotePost
{
private NameValueCollection inputs = new NameValueCollection();

private string url = string.Empty;
public string Url
{
set{ url = value; }
}
private string method = "post";
private string formName = "form1";

public void Add(string name, string value)
{
inputs.Add(name, value);
}

public void Post()
{
HttpContext.Current.Response.Clear();

StringBuilder sb = new StringBuilder();

sb.Append("<html><head>");

sb.Append(string.Format("</head><body onload=\"document.{0}.submit()\">", formName));
sb.Append(string.Format("<form name=\"{0}\" method=\"{1}\" action=\"{2}\" >", formName, method, url));

for (int i = 0; i < inputs.Keys.Count; i++)
{
sb.Append(string.Format("<input name=\"{0}\" type=\"hidden\" value=\"{1}\">", inputs.Keys[i], inputs[inputs.Keys[i]]));
}

sb.Append("</form>");
sb.Append("</body></html>");

HttpContext.Current.Response.Write(sb.ToString());

HttpContext.Current.Response.End();
}
}

Man anropar klassen så här:

RemotePost remotePost = new RemotePost();
remotePost.Url = "http://theRemoteDomain/Page.aspx";
remotePost.Add("field1","This is my first value");
remotePost.Add("field2","And another one");
remotePost.Post();

By Jesper Lind

Error-mail med Health monitor

Ett riktigt bra tips av Mads Kristensen, som förklarar hur man kan konfigurera health monotor i .NET så den skickar mail, när ett oväntat fel har uppstått i applikationen. Läs mer på MSDN.

Inställningarna i Web.config ser ut så här:

<?xml version="1.0"?>
<configuration>
   <appSettings/>
   <connectionStrings/>

   <system.web>
      <compilation debug="false" />
      <trace enabled="true" localOnly="false" />

      <healthMonitoring enabled="true">
         <providers>
            <add name="EmailProvider"
               type="System.Web.Management.SimpleMailWebEventProvider"
               from="you@domain.com"
               to="you@domain.com"
               subjectPrefix="Error: "
               buffer="true"
               bufferMode="Notification" />
         </providers>
         <rules>
            <add provider="EmailProvider" name="All App Events" eventName="All Errors" />
         </rules>
      </healthMonitoring>

   </system.web>
   <system.net>
      <mailSettings>
         <smtp from="you@domain.com">
            <network host="smtp.domain.com" />
         </smtp>
      </mailSettings>
   </system.net>
</configuration>

By Jesper Lind

Första XSS-attacken mot vår blogg

Det här är inte speciellt dramatiskt egentligen, men lite kul med action. Någon postade för en stund sen en XSS-länk på den engelska delen av vår hemsida.

Jag visste att kommentarsfältet inte var skyddat mot HTML och har inte brytt mig om att fixa det förrän nu.

Attackvektorer

I kommentaren som postades låg följande html-tagg och det är alltså bara en test för att se ifall det gick att köra javascript genom kommentarerna på vår blogg.

<INPUT TYPE="IMAGE" src="javascript:alert('XSS');">

På ha.ckers.org's XSS (Cross Site Scripting) Cheat Sheet kan man läsa att denna vektor inte fungerar på IE7 eller FF2 men på IE6 och Opera. Där finns även fler referenser till andra vektorer.

Hur man skyddar man sig mot XSS (med .NET)

Det enklaste alternativet är att låta validateRequest vara på slaget som det är som standard. Nackdelarna är att det då inte går att posta taggar över huvudtaget och att ett error visas. Läs mer om för-/nackdelar ValidateRequest här. I vår blogg som handlar om webbutveckling vill vi ju kunna tillåta html-kod från våra besökare, så vi har detta skydd avstängt.

Istället ser vi till att använda Server.HtmlEncode på all text som kommer från besökaren (det var detta jag just la till).

En bra effekt är att det numera går att skriva htmlkod i kommentarerna och den visas som den ska. Vissa har ju märkt att det inte fungerat så bra tidigare och vi borde ha fixat det för länge sen.

Testning tillåtet

Om nån är intressarad får ni gärna försöka att posta XSS till detta inlägget. Jag antar att det är omöjligt men vem vet om det finns någon obskyr metod som inte HTMLEncode klarar av att filtera.

Har även lagt in en liten funktion som ska göra om url till länkar. Där kan jag tänka mig att det kan gå att få in någon XSS-variant. Så här ser funktionen ut ifall nån vill prova att "hacka" den.

public string ConvertUrlToHyperlink(string strInput)
{
string strPattern = @"(?<url>http://(?:[\w-]+\.)+[\w-]+(?:/[\w-./?%&~=]*[^.\s|,|\)|<|!])?)";
string strReplace = "<a href=\"${url}\" target=_blank>${url}</a>";
string strResult;
strResult = Regex.Replace(strInput, strPattern, strReplace);
strPattern = @"(?<!http://)(?<url>www\.(?:[\w-]+\.)+[\w-]+(?:/[\w-./?%&~=]*[^.\s|,|\)|<|!])?)";
strReplace = "<a href=\"http://${url}\" target=_blank>${url}</a>";
strResult = Regex.Replace(strResult, strPattern, strReplace);

return strResult;
}

By Jesper Lind

Variera OutputCache beroende på sökvägens mapp

Jag har undrat ett tag över hur man kan basera OutputCache beroende på vilken mapp en sida ligger i, och nu kom jag på en lösning.

I mitt fall var det några User Controls som jag ville ha separata Cache-versioner av i varje mapp i applikationen. Först lade jag till följade deklaration på dessa:

<%@ OutputCache Duration="1800" VaryByParam="none"  VaryByCustom="CustomFolder"  %>

Sen overridade jag GetVaryByCustomString i Global.asax som man ska göra om man vill använda VaryByCustom.

public override string GetVaryByCustomString(
HttpContext context,
string arg)
    {
        string result = String.Empty;

        if (arg == "CustomFolder")
        {
            //object o = HttpContext.Current.Request.Path;<--This also gets the file name
           
            //Get the current folder, without the file name
            object o = HttpContext.Current.Request.Path.Substring(0,HttpContext.Current.Request.Path.LastIndexOf("/"));
            if (o != null)
            {
                result = o.ToString();
            }
        }
        else
        {
            result = base.GetVaryByCustomString(context, arg);
        }

        return result;
    }

Raden där den aktuella mappen hämtas ut blev lite lång och krånglig. Ville alltså ha bara mappen och inte filnamnet. Läste här att man kan använda Page.TemplateSourceDirectory, men kunde inte komma åt en referens till Page från Global.asax, eller hitta en motsvarande funktion i HttpContext.

By Jesper Lind