Feb 18, 2010ASP.NET MVC – komprimera html med actionfilter

Genom att komprimera sin html kan man spara åtskillig datamängd. Jag tänkte här dela med mig av ett Actionfilter till ASP.NET MVC för att komprimera all output (genom att strippa bort onödig whitespace). Med nedanstående filter sparade jag 46kb per sidvisning, vilket blir relativt mycket om man har en välbesökt sida.

Först kommer definitionen av attributet som används för att kunna anropa filtret.

public class OptimizeHtmlOutputAttribute : ActionFilterAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            HttpResponseBase response = filterContext.HttpContext.Response;
            response.Filter = new OptimizeHtmlFilter(response.Filter);
        }
    }
 

Filtret bestÃ¥r utav lite mer kod, dock är funktionen som utför allt arbete den överlagrade metoden “Write”.

public class OptimizeHtmlFilter : Stream
    {
        private readonly StringBuilder _responseHtml;
        private readonly Stream _responseStream;

        public OptimizeHtmlFilter(Stream inputStream)
        {
            _responseStream = inputStream;
            _responseHtml = new StringBuilder();
        }

        #region Filter overrides

        public StringBuilder ResponseHtml
        {
            get { return _responseHtml; }
        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return true; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override long Length
        {
            get { return 0; }
        }

        public override long Position { get; set; }

        public override void Close()
        {
            _responseStream.Close();
        }

        public override void Flush()
        {
            _responseStream.Flush();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return _responseStream.Seek(offset, origin);
        }

        public override void SetLength(long length)
        {
            _responseStream.SetLength(length);
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return _responseStream.Read(buffer, offset, count);
        }

        #endregion

        public override void Write(byte[] buffer, int offset, int count)
        {
            string stringBuffer = Encoding.Default.GetString(buffer);

            var sb = new StringBuilder();

            var lines = stringBuffer.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            foreach (var line in lines)
                sb.Append(line.Trim());

            buffer = Encoding.Default.GetBytes(sb.ToString());
            _responseStream.Write(buffer, 0, buffer.Length);
        }
    }
 

Filtret används sedan genom att dekorera Actions (eller för den delen controllers) med [OptimizeHtmlOutput], exempelvis:

[OptimizeHtmlOutput]
public ActionResult Html(int id)
{
       var report = _repository.GetReport(id);
       return View("Csv", new CsvReport(report));
}
 

Dock bör man komma ihåg att inte optimera där det inte behövs. Man bör alltid profilera koden för att se vart flaskhalsarna sitter.

Feb 11, 2010Visual Studio 2010 RC släppt

Äntligen är RC-versionen av Visual Studio 2010 släppt för oss “vanliga” utvecklare. Jag har nyss avinstallerat Beta 2:an och kan inte göra annat än att verkligen hÃ¥lla tummarna för att RC-versionen är bÃ¥de snabbare och stabilare än betan.

Betaversionen läckte verkligen minne. Så fort man öppnade ett projekt med ett stort antal projekt (50+) eller försökte köra något större WPF-projekt med diverse kontroller och funktioner tog det ofta inte länge innan applikationen kraschade.

En annan (för mig) väldigt viktig punkt var att MVC 2 RC 2 fungerar att köra under Visual Studio 2010 RC. Detta har tidigare inte gått men skall nu vara möjligt.

Mer information:

Jason Zander om släppet av Visual Studio 2010 RC.

Phil Haack bloggpost om ASP.NET MVC 2 RC 2 på Visual Studio 2010 RC.

Feb 5, 2010Hämta ut “slöa” queries ur mssql

Min morgon började roligt med en väldigt slö applikation. Jag misstänkte att flaskhalsen låg i databaslagret, och mycket riktigt hade jag rätt. Hur kom jag då fram till detta? Datbasservern jag använder är en MsSql 2008.

Efter lite sökande hittade jag en mycket användbar query för att plocka ur de dyraste frågorna som ställts mot databasen sedan den startades:

SELECT TOP 20 SUBSTRING(qt.text, (qs.statement_start_offset/2)+1,
        ((CASE qs.statement_end_offset
          WHEN -1 THEN DATALENGTH(qt.text)
         ELSE qs.statement_end_offset
         END - qs.statement_start_offset)/2)+1),
qs.execution_count,
qs.total_logical_reads, qs.last_logical_reads,
qs.min_logical_reads, qs.max_logical_reads,
qs.total_elapsed_time, qs.last_elapsed_time,
qs.min_elapsed_time, qs.max_elapsed_time,
qs.last_execution_time,
qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
WHERE qt.encrypted=0
ORDER BY qs.total_logical_reads DESC

Detta är ett väldigt smidigt sätt för att hitta flaskhalser i sin databas.

För er som använder MySql kan jag rekommendera motsvarigheten log_slow_queries.

Feb 4, 2010Dependency Injection med PHP

Vid utveckling vill man alltid skapa sÃ¥ löst kopplade system som möjligt. Detta speciellt om man skriver enhetstester eller tillämpar testdriven utveckling. För att underlätta för sig själv skall man försöka följa de “SOLID”a principerna sÃ¥ gott det gÃ¥r.

D:et i SOLID står för Dependency Inversion, och betyder, enkelt sagt, att en klass inte skall vara hårt kopplad till en annan klass. För att lösa detta brukar man tillämpa en teknik som ofta kallas dependency injection, vilket innebär att klasser som skall instansieras får sina beroenden från den som instansierar klassen.

Ett exempel på en klass som inte följer Dependency Inversion är följande:

<?php
class HtmlParser
{
    public function GetTitle($url)
    {
        $downloader = new HtmlDownloader();
        $content = $downloader->download($url);
       
        $title = preg_match(‘~<title>(.*?)</title>~’, $content, $match);
       
        return $match;
    }
}
?>
 

Denna klassen har ett beroende av HtmlDownloader, vilket innebär att man inte kan återanvända klassen HtmlParser från ett annat ställe. Vad händer om vi istället för att ladda ner filen via HTTP vill hämta innehållet från en lokal fil? Då måste vi antingen skapa en ny klass för detta, eller en ny metod för att göra detta.

En annan, och bättre, lösning på detta är att låta klassen HtmlParser ta en klass som implementerar ett gemensamt interface, som inparameter i konstruktorn, till exempel:

<?php
interface IHtmlProvider
{
    function getHtml($path);
}

class LocalFileHtmlProvider implements IHtmlProvider
{
    function getHtml($path)
    {
        // Läs och returnera frÃ¥n filsystemet
    }
}

class RemoteHtmlProvider implements IHtmlProvider
{
    function getHtml($path)
    {
        // Ladda ner via HTTP
    }
}
?>
 

Detta gör att vi kan modifiera vår parser till att se ut enligt följande:

<?php
class HtmlParser
{
    private $provider;
   
    public function __construct(IHtmlProvider $provider)
    {
        $this->provider = $provider;
    }
   
    public function GetTitle($path)
    {
        $content = $this->provider->getHtml($path);
       
        $title = preg_match(‘~<title>(.*?)</title>~’, $content, $match);
       
        return $match;
    }
}
?>
 

Nu kan vi enkelt byta källa för vår html, och återanvända koden precis var vi vill, så länge får källa implementerar IHtmlProvider. Vill vi bygga en FtpHtmlProvider är detta inga som helst problem.

I ett kommande inlägg kommer jag visa en teknik som kallas för Inversion of Control, och är nära besläktat med Dependency Injection.

Externa länkar med mer informtion:

Wikipedia om Dependency Injection

Wikipedia om Dependency Inversion Principle

Wikipedia om SOLID Principles

Apr 9, 2009Användares statusuppdateringar på twitter

Tänkte dumpa en kodsnutt som hämtar en användares statusuppdateringar på twitter. No more no less.

$data = file_get_contents(‘http://twitter.com/statuses/user_timeline/26553013.json’);
$json = json_decode($data);

foreach($json as $message)
{
    if($message->in_reply_to_screen_name === null)
        echo $message->text;
}
 

Byt ut idt(26553013) mot den användaren du vill hämta datan för.

Mar 18, 2009Listen up!

Feb 12, 2009FireUnit – Smidigare enhetstestning i JavaScript

För oss som gillar enhetstestning har det nu kommit en riktigt smidigt plugin till Firebug – FireUnit. Pluginet möjliggör smidigare enhetstestning för JavaScript, vilket tidigare har varit ganska trivialt.

FireUnit är som sagt helt integrerat i FireBug, och placerar vid installation en ny flik vid namn ‘Test’ i fliksystemet.

NÃ¥gonting som jag inte gillar är vissa namnkonventioner, till exempel har man bytt ut ordet ‘assert’ mot ‘ok’, vilket kan ses som väldigt trivialt, men enligt mig bör följa de andra unit-testningramverken(?).

För mer information om FireUnit hänvisar jag till skaparen John Resig’s blogg.

Feb 9, 2009Serialisera array till XML

För tillfället sitter jag och skriver en REST-service för en viss tjänst, och var i behov av en klass eller funktion för att serialisera en php-array till XML. Efter lite googlande hade jag dock inte hittat någon simpel klass för att göra detta, förrutom en PEAR-klass. Dock valde jag att inte använda denna då det inte finns stöd för PEAR där tjänsten kommer att hostas. Jag bestämde mig då för att knåpa ihop en egen klass, och detta är resultatet:

Class XmlSerializer Extends XmlWriter
{
    protected $rootnode;
   
    public function __construct($rootnode)
    {
        // Use memory output
        parent::openMemory();
        parent::setIndent(true);
       
        /* Use four spaces as indent */
        parent::setIndentString(chr(0×20) .
                                chr(0×20) .
                                chr(0×20) .
                                chr(0×20)
        );
       
        $this->rootnode = $rootnode;
        parent::startElement($this->rootnode);
    }
   
    public function startElement()
    {
        parent::startElement($this->rootnode);
    }
   
    public function serialize($data)
    {
        foreach($data as $key => $value)
        {
            if(is_array($value))
            {
                parent::startElement($key);
                $this->serialize($value);
                parent::endElement();
               
                continue;
            }
           
            parent::writeElement($key, $value);
        }
    }
   
    // Override function so that we dont need to call endElement by ourselves
    public function output($flush)
    {
        parent::endElement();
        return parent::outputMemory($flush);
    }
}
 

Som ni ser är den välidgt, väldigt simpel, enbart med funktioner för att serialisera en array till php – inte vice versa. Kanske nÃ¥got att bygga in stöd för dock.

Här är ett simpelt exempel:

$data = array(
    ’status’ => ‘Success’,
    ‘data’ => array(
        ‘username’ => ‘Alexander Nyquist’,
        ‘password’ => ‘password’,
        ‘apikey’ => md5(‘password’)
    )
);

$serializer = new XmlSerializer(‘response’);
$serializer->serialize($data);
echo $serializer->output(true);
 

Ovan resulterar i följande XML:

<response>
    <status>Error</status>
    <data>
        <username>Alexander Nyquist</username>
        <password>password</password>
        <apikey>5f4dcc3b5aa765d61d8327deb882cf99</apikey>
    </data>
</response>
 

Tack o hej!

Jan 30, 2009Simpelt shell-script för att dumpa ut http headers

I jobbet är jag ständigt beroende av att kontrollera http headers för diverse sidor. Som vanligt använder jag live-http-headers, dock började dyka upp konstig respons som inte stämde överens alls med andra verktyg man använder för att kontrollera headersen.

Efter lite snabbt telnettande konstaterade jag att live-http-headers ibland skickar felaktig data, och skrev ihop ett simpelt shell script som använder lynx för att dumpa ut response-headersen för en angiven sida.

#/!bin/bash
lynx -head -dump $1
 

Visst, man skulle alltid kunna lägga till ett bash-alias.

Dec 19, 2008Simpelt wordpress plugin för att dölja dashboarden

Var idag på jobbet i behov av att dölja dashboarden för användare, detta utan att in och pilla i kärnan. Resultatet efter 3 minuters intensivt kodande kan ni själva beskåda nedan:

<?php
/*
Plugin Name: Dashboard Hider
Description: This plugins hides the motherfucking dashboard, yeah.
Author: Alexander Nyquist
Version: 1.0
*/

function hide_dashboard()
{
    global $parent_file;
   
    if($parent_file === ‘index.php’)
    {
        ?>
        <script type="text/javascript">
            document.location.href = ‘edit-pages.php’;
        </script>
           
        <meta http-equiv="refresh" content="0;url=edit-pages.php">
        <?php
    }
}

add_action(‘admin_head’, ‘hide_dashboard’);
?>
 

Tips:
Modell för en dag