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.
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.
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();
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();
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:
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.
Julafton kom tidigt för oss .NET-utvecklare i år. Microsoft höll tidschemat och levererade VS 2008 med .NET 3.5 Beta 2.
Men helt smärtfritt var det inte att få igång det för mig. Eftersom jag hade läst att denna version skulle fungerar bra vid sidan om VS 2005 satte jag igång installationen. Denna avbröts dock mitt i pga något fel, och lämnade mig med VS 2005 och hela min utvecklingsmiljö kaputt. Det beror nog på att jag redan hade en del fel på mina installationer, men en liten varning alltså. Installera den inte på din produktionsmijö om du vill slippa problem.
En ominstallation av XP senare, så gick det mycket bättre och nu rullar det fint på min maskin. Än har jag bara känt på det nya utvecklingpaketet, men bara en sån sak som Intellisense på Javascript har gjort det mödan värt.
VS 2008 med ASP.NET AJAX 1.0
Något som är lite lurigt är att få Ajax att fungera i NET2.0 läget på VS 2008. Först måste man se till att köra ett skript för att inte VS 2008 automatiskt ska använda de nya inbyggda Ajax-referenserna.
Sedan är det några fler steg som måste genomföras för att få fram Ajax-kontrollerna i toolboxen och ladda in referenserna. Microsofts utvecklingsteam har gjort en grundläggande förklaring för hur man gör.
Om dessa kontroller är cachade så blir det lite svårare. Speciellt om man ska kunna sätta några properties på dem. Via OdeToCode hittade jag info hos Microsoft om hur man kan göra, och även exempelkod.
// Load the user control Control control = LoadControl("control/MyControl.ascx"); placeHolder.Controls.Add(control);
// Set its properties (if possible) ASP.MyControl uc = control as ASP.MyControl; if (uc == null) { PartialCachingControl pcc = control as PartialCachingControl; if (pcc != null) uc = pcc.CachedControl as ASP.MyControl; } if (uc != null) uc.MyProperty = "This is the value";
Ps. I mitt exempel har jag med ASP-prefixet på klassen för kontrollen. Detta beror på att jag använder inline-code, läs mer om detta i tidigare nämnt inlägg.
Något jag inte tänkt på innan är att man inte kan sätta värde-variabler till null i C#. Gjorde ett försök och fick då felet:
int x = null; Cannot convert null to 'int' because it is a value type
När jag läst detta inlägget, fick jag lite bättre förståelse till hur det fungerar, och även hur man kan göra för att använda null även på värdetyper, så här:
int? x = null;
Användbart om man jobbar med en databas och man vill att sina variabler ska reflektera de null-värden som finns där.
TinyMCE är en skön HTML-Editor som har blivit väldigt populär. Den är oftast väldigt enkel att använda och har många konfigureringsmöjligheter.
Men att använda den tillsammans med Ajax och partial update är verkligen ingen lätt match. Jag lyckades tidigare i år, efter stor möda, men det var nog mest tur.
Skrev om det i bloggen, men den postningen blev inte så fullständig eller bra. Det var mest lite egna minnesanteckningar som jag fick till där. Jocke var inne och kollade på beskrivningen men fick inte till det. Så nu tänkte jag visa precis hur man kan göra med ett uppdaterad och fullständig kod.
Problemen
Problem 1. TinyMCE gör om textboxar till en Iframe, som My Portal Project förklarar. Detta leder till att det är svårt att ta emot värden (speciellt i Firefox). Waqas_badas visar hur man kan komma runt detta genom ett javascript på submit-knappen.
Problem 2. Vid Ajax-anrop ritas inte editorn upp igen. Här kan man anropa mceAddControl-kommandot, men det gäller att resetta id-räknaren innan man gör det, här är en som kommit på hur man gör.
Kodexempel
Nu tänkte jag låta koden förklara resten. Missa inte att ladda hem zip-filen längst ner som innehåller ett färdigt project som fungerar i IE7, FF2 och Opera.
Default.aspx
<%@ Page Language="C#" ValidateRequest="false" Trace="false" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>MS Ajax UpdatePanel - TinyMCE</title> <script type="text/javascript"> function SaveMyPreciousValues() { tinyMCE.triggerSave(false,true); TextBox1 = document.getElementById('TextBox1'); alert('Check value when posting: '+ TextBox1.value) } </script> </head> <body>
using System; using System.Data; using System.Configuration; 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;
public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if(!Page.IsPostBack) LoadTinyMCE(); }
private void LoadTinyMCE() {
//Load tinyMCE HtmlGenericControl Include = new HtmlGenericControl("script"); Include.Attributes.Add("type", "text/javascript"); Include.Attributes.Add("src", "js/tiny_mce/tiny_mce.js"); this.Page.Header.Controls.Add(Include);
Label1.Text = "Content posted from TextBox1: " + inputText;
//Register some javascript to redraw the editor. //Very important to reset the id-counter to "0", or else strange things will happen.. ScriptManager.RegisterClientScriptBlock(UpdatePanel1, this.GetType(), "init", "tinyMCE.idCounter=0;tinyMCE.execCommand('mceAddControl', false, 'TextBox1');", true); } }
Nu har jag testat att använda CSS Friendly Control Adapters som jag nämde förra året. De fungerar verkligen magiskt bra och man får en mycket mer CSS-vänlig HTML-kod från kontrollerna i Asp.Net.
Dels det lite omständiga sättet som Microsoft har utvecklat, här måste man lägga till speciella javascript och CSS bibliotek. Då får man dock större kontroll över koden om man vill modifiera den själv.
Observera att jag koden skiljer sig lite från den i forum-tråden. Så jag kan inte lova att viewstate verkligen fungerar med denna modifikation. I mitt fall behöver jag inget viewstate så jag nöjer mig där.
Nu tänkte jag visa ett snarlikt exempel men här skriver vi flödet direkt till Response-strömmen istället för att skapa en fysisk fil. Kallar denna metoden, att leverera ett RSS-flöde "virtuellt", i brist på bättre term.
Hämtade lite inspiration från Dileno.com men använde istället min egen kod från tidigare exempel.
Själva ASPX-filen sparar jag som Rss.aspx och innehåller endast en rad kod.
I koden bakom hämtas posterna ur en tabell i en Access-databas. Bygger ihop en sträng med xml taggarna för RSS-definitionen och lägger sedan in den i cache. Detta för att det inte ska bli överbelastning ifall flödet anropas ofta.
using System; using System.Data; using System.Data.OleDb; using System.Configuration; using System.Text; using System.Web; using System.IO; using System.Xml;
public partial class _Rss : System.Web.UI.Page { static string strConn = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString; static string strRootUrl = "http://codeodyssey.se";
//Kollar om det redan finns en sträng i cache, annars skapas en ny protected string GetRssString() { string cacheName = "rss-feed";
//Hämta chachad data om det finns if (Cache[cacheName] != null) { return Convert.ToString(Cache[cacheName]); } else { string newlyGeneratedRss = GenerateRss(); Cache.Insert(cacheName, newlyGeneratedRss, null, DateTime.Now.AddMinutes(20), TimeSpan.Zero); return Convert.ToString(newlyGeneratedRss); } }
//Funktion som skapar en textSträng som innehåller rss-feed protected string GenerateRss() { StringWriter sw = new StringWriter(); try {
XmlTextWriter xtw = new XmlTextWriter(sw);//,System.Text.Encoding.GetEncoding("UTF-8") xtw.Formatting = Formatting.Indented; xtw.WriteStartDocument();
//skriv ut <rss version="2.0"> xtw.WriteStartElement("rss"); xtw.WriteAttributeString("version","2.0");
//skriv ut <channel> xtw.WriteStartElement("channel");
//skriv ut element som tillhör <channel> xtw.WriteElementString("title","Codeodyssey.se"); xtw.WriteElementString("link","http://www.codeodyssey.se/"); xtw.WriteElementString("description","Code Odyssey - gräver djupt i kodträsket"); xtw.WriteElementString("language","sv-SE"); xtw.WriteElementString("copyright","Copyright (c) 2004-2007 Code Odyssey");
OleDbConnection conn = new OleDbConnection (strConn); bool boolPermission = false; OleDbDataReader objDataReader=null; try { string strSQL = "SELECT TOP 10 * FROM News WHERE Published=true ORDER BY PublishDate DESC";
conn.Open();
OleDbCommand objCommand = new OleDbCommand(strSQL, conn); objDataReader = objCommand.ExecuteReader(); while (objDataReader.Read() == true) { int Id = Convert.ToInt32(objDataReader["Id"]); string Title = Convert.ToString(objDataReader["Title"]); string Body= Convert.ToString(objDataReader["Body"]); //Se till att datum följer RFC-822 standard string PublishDate = Convert.ToString( ((DateTime)objDataReader["PublishDate "]).ToString("r"));
Tabs-kontrollen i Asp.Net Ajax är cool. Men något att tänka på när man använder den är att alla flikar laddas in när den rendereras, även de som är gömda.
Det fick jag själv bittert erfara när jag hade utvecklat en omfattande applikation med Tabs. Den var seg som sirap när jag väl körde den på produktionsservern.
Om man har mycket funktionallitet under varje flik, vore det bättre om denna bara laddades när man klickar på den. Shawn Burke verkar ha en smidig lösning, den artikeln ska jag kolla på nästa gång det är dags för att använda Tabs.