<

Ook gij Microsoft - Load me up dinsdag 23 juli 2013

>

De AjaxControlToolkit blijft me achtervolgen, zo ontdekte ik laatst weer een mooie implementatie in de toolkit bij het zogenaamde AsyncFileUploadControl. Maar voordat we daar zijn, laat ik eerst even de situatie beschrijven.

Zoals inmiddels wel bekend is, ben ik bezig met een webapplicatie voor Driessen. Om deze applicatie neer te kunnen zetten bij een aantal klanten, moest de applicatie voldoen aan een aantal eisen voordat ze akkoord zouden gaan. Het ging hier onder andere om de mogelijkheid om bestanden te kunnen uploaden. Deze bestanden zouden dan als bijlage bij een PDF-rapportage meegestuurd worden naar een bepaald dossier van elke medewerker.

Nu was het in de eerste instantie zo dat ik dacht: “Oh, we hebben daar een speciaal control voor in het Framework van Driessen zelf, dus zo moeilijk zou dat niet kunnen zijn.”. Dit bleek al gauw een verkeerde gedachtegang. Het probleem was namelijk dat dat control onderliggend gebruik maakte van het FileUploadControl van ASP.Net zelf. Een mooie eigenschap van dit control is dat deze niet gebruikt kan worden binnen applicaties die gebruik maken van asynchrone postbacks (dus via een UpdatePanel uit de AjaxControlToolkit). De FileUploadControl moet altijd een volledige postback doen, omdat hij het Form object mee moet sturen van de pagina. Dit was dus geen optie meer.

Het antwoord op alles: De AsyncFileUploadControl

Nu worstelden we al in diverse -eerdere- webapplicaties met ditzelfde euvel, en ik ging dus op zoek naar een alternatief. Nu kwam ik erachter dat de AjaxControlToolkit sinds september 2009 een AsyncFileUploadControl heeft, welke dus precies doet wat de naam zegt: het is de asynchrone implementatie van de FileUploadControl.

Ik dacht dat ik hiermee helemaal uit alle problemen was, en begon met de implementatie van dit control. Ik realiseerde me echter niet dat er in de afwerking van deze functionaliteit nog behoorlijk wat tegenslag zou komen.

Naweeën

Nadat het control volledig geïmplementeerd was, en ik diverse tests had uitgevoerd, kwam ik erachter dat er via een Javascript ConfirmDialog een fout werd gegooid wanneer het bestand te groot was ten aanzien van de MaxRequestLength property van ASP.Net zelf. Standaard staat deze ingesteld op 4096 (dit hoef je zelf niet expliciet te vermelden), en dit vond ik ook prima zo. Na veelvuldig onderzoek op het wereldwijde web, en diverse oplossingen die suggereerden dat ik deze moest verhogen later, vond ik uiteindelijk een aardige workaround. Wat deze deed was eigenlijk het volgende.

Wanneer de MaxRequestLength te hoog is, wordt er door ASP.Net / IIS zelf een exception gegooid. Oftewel: deze zou ik kunnen onderscheppen in de Global.asax van mijn webapplicatie. Het mooie hiervan is dat ik het Response object kon afbreken, vóórdat deze de foutmelding aan de ‘gebruiker’ (in ons geval het iframe wat de AsyncFileUploadControl gebruikt) ‘toont’. Op die manier kon ik voorkomen dat het control de foutmelding zou detecteren en dus was er ‘niets’ aan de hand. Het control weet dan echter wél dat het uploaden is mislukt, en zal daarom in zijn Errorstate gaan.

/// <summary>
/// Method that runs when the application runs into an unhandled exception
/// </summary>
/// <param name="sender">The sender</param>
/// <param name="e">The eventarguments</param>
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
if (exception != null)
{
// Declare local exception to get the webeventcode
HttpException innerHttpException = (exception.InnerException as HttpException);
// This statement will suppress the MaxRequestLength Exception.
// This generally happens when a file is uploaded that is larger // than the MaxRequestLength
if (innerHttpException != null &&
innerHttpException.WebEventCode == WebEventCodes.RuntimeErrorPostTooLarge &&
Request.Url.Query.Contains("AsyncFileUploadID="))
{
Server.ClearError();
Response.Close();
}
}
}

Een best aardige oplossing vond ik dus. Vervolgens heb ik deze dus ook gebruikt in de uitlevering naar de testomgeving. Daar kwamen we echter al gauw tot de ontdekking dat dit om magische redenen niet werkte. Heel erg vreemd was dit. Het leek er echter op dat de foutafhandeling van IIS zelf onze implementatie negeerde, en de fout alsnog naar het control stuurde, met als resultaat: het gruwelijk irritante ConfirmDialog.

Na letterlijk een paar uur zoekwerk kwam ik achter een oplossing die werkelijk waar nergens op sloeg. Ik heb hem op dat moment wel geprobeerd, omdat ik gewoonweg een poging wilde wagen. Het gekke van dit alles was: dit werkte. We (een aantal collega’s en ik) waren dus stomverbaasd dat dit blijkbaar de oplossing zou zijn. De implementatie die we moesten gebruiken was namelijk het zetten van de ClientIdMode van de AsyncFileUploadControl. Deze moest ik expliciet op AsyncFileUploadControl.ClientIdMode = ClientIdMode.AutoID; zetten.

En nu waren we er dan eindelijk?

Nee. Nog steeds niet. Ik dacht van wel, maar niets was minder waar. Na het verwerken van de overige testresultaten hield ik me bezig met het garanderen van cross-browsercompatibility. Ofwel: kijken of de applicatie er in de verschillende browsers (en versies) nagenoeg hetzelfde uitziet.

Dit was uiteraard op een fundamenteel onderdeel niet het geval. Ik had voor mijn interpretatie van de AsyncFileUploadControl gebruik gemaakt van wat handige CSS en HTML trucs om een onstijlbaar element toch te voorzien van een passende vormgeving. En toen kwam ik er al snel genoeg achter: het control werkt helemaal niet meer.

Dit bleek te liggen aan de implementatie van de ‘ClientIdMode fix’ die ik de dag ervoor had gedaan. Deze zorgde ervoor dat mijn eigen Javascript functies niet meer draaiden, en in sommige browsers (lees: IE8) de rest van de pagina niet eens. Eigenlijk was ik dus weer terug bij af.

Sjaak Swart

Welnu, ik heb zojuist alle euvels kunnen repareren door de Toolkit wéér open te breken, en op zoek te gaan naar de functie in de AsyncFileUploadControl welke die irritante ConfirmDialog veroorzaakte. Ik heb deze kunnen vinden in het AsyncFileUpload.pre.js bestand, en wel op regelnummer 381. Ik heb simpelweg de implementatie vanaf dat regelnummer t/m regel 397 weggehaald, en c’est ça: het werkte. Mede dankzij de implementatie binnen mijn Global.asax werd de fout nu correct afgehandeld, kon ik eindelijk de styling van het control weer herstellen in alle browsers, en een volledig werkende versie uitleveren naar de testomgeving.

Conclusie

Redelijk moe word ik ervan. Ik ben wat dat betreft blij dat ik eindelijk klaar ben voor de komende tijd met de ontwikkeling van deze applicatie. Na de acceptatie zal deze simpelweg alleen maar worden uitgerold bij klanten, en écht in gebruik zijn. In de tussentijd ga ik me bezighouden met een nieuwe webapplicatie die voor een groot deel gebaseerd is op deze applicatie, alleen weet ik nu waar ik op moet letten én welke toolkit ik moet gebruiken.