Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1499 connectés 

  FORUM HardWare.fr
  Programmation
  ASP

  [HOW TO] Gérer proprement les erreurs avec ASP

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

[HOW TO] Gérer proprement les erreurs avec ASP

n°803292
Arjuna
Aircraft Ident.: F-MBSD
Posté le 22-07-2004 à 15:35:48  profilanswer
 

Je suis en train de réécrire un site e-Commerce pour une grande société.
Pour un nombre incalculable de raisons liées aux données récupérées de leur système d'information, il se produit régulièrement des erreurs sur leur site actuel, y compris dans des routines critiques du site, ce qui constitue souvent des problèmes graves de cohérence des données (une fois on est allé jusqu'à avoir un client qui a payé pour la commande d'une autre personne), sans que personne ne s'en rendre compte (ce n'est qu'un mois plus tard qu'on s'est rendu compte de l'erreur, lorsque le client qui avait payé la commande s'est étonné auprès du call-center de ne rien avoir reçu.
 
Je mets donc un point d'honneur sur cette nouvelle version pour faire un système de gestion des erreurs qui nous permette de gérer proprement les erreurs, notamment être prévenu sur le champ avec un message suffisament détaillé pour retrouver où s'est produite l'erreur, et pourquoi.
 
Je tiens à vous faire part des différentes techniques que j'utilise, et attends de vous un feed-back, ou des idées pour les améliorer.
 
1) Les transactions.
Evidement, c'est la première chose qu'il faut faire pour garantir l'intégrité des données dans la base... Par expérience, c'est pourtant rarement utilisé en ASP (et sur le web en général).
 
Ci-dessous un exemple de routine permettant de garantir l'intégrité des données lors de l'inscription d'un utilisateur :


sub getRegisterStep4()
 cnx.beginTrans
 on error resume next
 
 dim sqlNewId, newId
 sqlNewId = "select seq_tie.nextval newid from dual"
 dim rsNewId
 set rsNewId = Server.CreateObject("ADODB.RecordSet" )
 set rsNewId.ActiveConnection = cnx
 rsNewId.Open sqlNewId
 newId = rsNewId("newid" )
 rsNewId.Close
 Set rsNewId = Nothing
 
 dim sqlPay
 sqlPay = "select etbcod, posfis, typcde from pay where codpay = " & Quote(Request.Form("codpay" ))
 dim rsPay
 set rsPay = Server.CreateObject("ADODB.RecordSet" )
 set rsPay.ActiveConnection = cnx
 rsPay.Open sqlPay
 
 dim sqlINS
 sqlINS = "insert into tie (CODSOC, TYPTIE, SIGTIE, NOMTIE, CODPAY, CODDEV, TYPCDE, SIGREP, SIGGRP, POSFIS, CODBAR, ETBCOD, FAMTIE, MODRGL, CODDPT, CODQUA, CODSPE) " &_
    "values (0, 'CLI', " & Quote(newId) & ", " & Quote(Request.Form("NOMTIE" )) & ", " & Quote(Request.Form("CODPAY" )) & ", " & Quote(Request.Form("CODDEV" )) & ", " & Quote(rsPay("typcde" )) & ", 'WEB" & rsPay("etbcod" ) & "', ' ', " & Quote(rsPay("posfis" )) & ", ' ', " & Quote(rsPay("etbcod" )) & ", " & Quote(Request.Form("FAMTIE" )) & ", 'CB', '@', 0, " & Quote(Request.Form("CODSPE" )) & " )"
 cnx.Execute sqlINS
 
 rsPay.Close
 Set rsPay = Nothing
 
 dim contact
 if Len(Trim(Request.Form("contact1" )) & " " & Trim(Request.Form("contact2" ))) <= 20 then
  contact = Trim(Request.Form("contact1" )) & " " & Trim(Request.Form("contact2" ))
 else
  contact = Mid(Trim(Request.Form("contact2" )), 1, 20)
 end if
 
 dim sqlADR
 sqlADR = "insert into adr (CODSOC, TYPTIE, SIGTIE, TYPADR, NUMADR, LIBADR, ADRESS, ADRSUI, LOCALI, CODPOS, CENPOS, CODPAY, CONTACT, TEL, FAX, COMMEN1, COMMEN2, COMMEN3, COMMEN4, COMMEN5, ADREDI, CODCIV) " &_
    "values (0, 'CLI', " & Quote(newId) & ", 'COM', 800, " & Quote(Request.Form("nomtie" )) & ", " & Quote(Request.Form("adress" )) & ", nvl(" & Quote(Request.Form("adrsui" )) & ", ' '), nvl(" & Quote(Request.Form("locali" )) & ", ' '), " & Quote(Request.Form("codpos" )) & ", " & Quote(Request.Form("cenpos" )) & ", " & Quote(Request.Form("codpay" )) & ", " & Quote(contact) & ", " & Quote(Request.Form("tel" )) & ", nvl(" & Quote(Request.Form("fax" )) & ", ' '), ' ', ' ', ' ', ' ', ' ', " & Quote(Request.Form("email" )) & ", " & Quote(Request.Form("codciv" )) & " )"
 cnx.Execute sqlADR
 
 randomize
 dim strCHARS, strPASS, i
 strCHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 strPASS = ""
 for i = 1 to 8
  strPASS = strPASS & Mid(strCHARS, int(rnd(i) * 36) + 1, 1)
 next
 
 dim news
 if Request.Form("news" ) <> "" then
  news = "Y"
 else
  news = "N"
 end if
 
 dim sqlUSR
 sqlUSR = "insert into usr (CODSOC, LOGIN, PASSWORD, NOM, PRENOM, CODLAN, CODDEV, TYPTIE, SIGTIE, EMAIL, NEWS, ACTIF) " &_
    "values (0, " & Quote(newId) & ", " & Quote(strPASS) & ", " & Quote(Trim(Request.Form("contact1" ))) & ", " & Quote(Trim(Request.Form("contact2" ))) & ", " & Quote(Request.Form("codlan" )) & ", " & Quote(Request.Form("coddev" )) & ", 'CLI', " & Quote(newId) & ", " & Quote(Request.Form("email" )) & ", " & Quote(news) & ", 'Y')"
 cnx.Execute sqlUSR
 
 if err = 0 then
  on error goto 0
  cnx.CommitTrans
  call registerMail(Request.Form("contact1" ), Request.Form("contact2" ), newId, strPASS, Request.Form("codlan" ), Request.Form("email" ))
  dim sql
  sql = "select desc1 from news where typnew = 'REG' and codlan = " & Quote(codlan) & " and rank = 4"
  dim rs
  set rs = Server.CreateObject("ADODB.RecordSet" )
  set rs.ActiveConnection = cnx
  rs.Open sql
  if not rs.EOF then
   Response.Write "<p>" & rs("desc1" ) & "</p>"
  end if
  rs.Close
  set rs = Nothing
 else
  Response.Write getLabel("errorOccured", codlan)
  cnx.RollbackTrans
  on error goto 0
 end if
end sub


 
Il faut regarder tout en haut le cnx.beginTrans
cnx est mon objet connection : Set cnx = Server.CreateObject("ADODB.Connection" )
Si le SGBD utilisé supporte les transactions, alors cela va en créer une sur le SGBD. Ici j'utilise Oracle, donc aucun problème de ce côté.
Le "on error resume next" juste en dessous peut vous sembler bien crade... Hé oui, mais si on veut que la transaction fonctionne correctement (et récupérer l'info comme quoi il y a eu une erreur), c'est le seul moyen.
 
On effectue les traîtements de création de l'utilisateur.
 
Puis, on teste si err = 0
err est un objet mis à jour lorsqu'il se produit une erreur, et que la gestion des erreurs est active (on error resume next)
Si sa propriété par défaut est égale à 0, alors il n'y a pas eu d'erreur.
On peut alors faire le cnx.commitTrans qui va avoir pour effet de valider la transaction.
Sinon, le cnx.rollbackTrans permet d'annuler toutes les requêtes de la transaction dans la base. Ainsi, on ne se retrouve pas avec des bouts de client créés un peu partout dans la base avec des bouts qui manquent, même si une erreur se produit en plein milieu.
 
Attention, il faut IMPERATIVEMENT faire l'une ou l'autre de ces deux actions (d'où l'obligation d'utiliser un on error resume next pour ne pas s'arrêter en cas d'erreur). En effet, sinon la transaction sera automatiquement rollbackée... mais au bout de très longues minutes, au moment du timeout associé à la transaction (ca peut durer des jours). Hors une transaction peut générer des locks et surtout un espace mémoire utilisé très important, il faut donc s'en débarrasser le plus rapidement possible, ...et proprement !
 
2) Envois d'un mail de notification d'erreur, avec des données utiles :
Juste avant le rollBack, j'ai en fait cette ligne dans mon code :


call SendErrorMail(sqlINS & "<br><br>" & sqlADR & "<br><br>" & sqlUSR)


Cela me permet d'envoyer un mail avec les trois requêtes qui ont été éxécutée au moment de l'erreur.
Mais ce n'est pas tout. Voici ma fonction SendErrorMail() :


sub SendErrorMail(errorMessage)
 dim objMail
 Set objMail = Server.CreateObject("CDONTS.NewMail" )
 objMail.Subject = "## WEBSITE ERROR ##"
 objMail.From = "errormanager@gemedicalsystems.com"
 objMail.To = getAlphaNumericParameter("IT_TEAM" )
 objMail.Cc = ""
 objMAIL.Bcc = ""
 objMail.Body = "An error occured on the page " & Request.ServerVariables("SCRIPT_NAME" ) & "<br><br>Error:<br>" & err.Source & "<br>" & err.Description & "<br><br>" &_
     errorMessage & "<br><br>" &_
     "sigtie = " & sigtie & "<br>" &_
     "typtie = " & typtie & "<br>" &_
     "login = " & login & "<br>" &_
     "password = " & password & "<br>" &_
     "coddev = " & coddev & "<br>" &_
     "codlan = " & codlan & "<br>" &_
     "<br>" &_
     "AUTH_TYPE : " & Request.ServerVariables("AUTH_TYPE" ) & "<br>" &_
     "CONTENT_TYPE : " & Request.ServerVariables("CONTENT_TYPE" ) & "<br>" &_
     "CONTENT_LENGTH : " & Request.ServerVariables("CONTENT_LENGTH" ) & "<br>" &_
     "DOCUMENT : " & Request.ServerVariables("DOCUMENT" ) & "<br>" &_
     "DOCUMENT_URI : " & Request.ServerVariables("DOCUMENT_URI" ) & "<br>" &_
     "DATE_GMT : " & Request.ServerVariables("DATE_GMT" ) & "<br>" &_
     "DATE_LOCAL : " & Request.ServerVariables("DATE_LOCAL" ) & "<br>" &_
     "GATEWAY_INTERFACE : " & Request.ServerVariables("GATEWAY_INTERFACE" ) & "<br>" &_
     "HTTP_USER_AGENT : " & Request.ServerVariables("HTTP_USER_AGENT" ) & "<br>" &_
     "HTTP_REFERER : " & Request.ServerVariables("HTTP_REFERER" ) & "<br>" &_
     "HTTP_COOKIE : " & Request.ServerVariables("HTTP_COOKIE" ) & "<br>" &_
     "LAST_MODIFIED : " & Request.ServerVariables("LAST_MODIFIED" ) & "<br>" &_
     "LOGON_USER : " & Request.ServerVariables("LOGON_USER" ) & "<br>" &_
     "PATH_INFO : " & Request.ServerVariables("PATH_INFO" ) & "<br>" &_
     "PATH_TRANSLATED : " & Request.ServerVariables("PATH_TRANSLATED" ) & "<br>" &_
     "REMOTE_ADDR : " & Request.ServerVariables("REMOTE_ADDR" ) & "<br>" &_
     "REMOTE_HOST : " & Request.ServerVariables("REMOTE_HOST" ) & "<br>" &_
     "REMOTE_IDENT : " & Request.ServerVariables("REMOTE_IDENT" ) & "<br>" &_
     "REMOTE_USER : " & Request.ServerVariables("REMOTE_USER" ) & "<br>" &_
     "REQUEST_METHOD : " & Request.ServerVariables("REQUEST_METHOD" ) & "<br>" &_
     "SCRIPT_MAP : " & Request.ServerVariables("SCRIPT_MAP" ) & "<br>" &_
     "SCRIPT_NAME : " & Request.ServerVariables("SCRIPT_NAME" ) & "<br>" &_
     "SERVER_NAME : " & Request.ServerVariables("SERVER_NAME" ) & "<br>" &_
     "SERVER_PORT : " & Request.ServerVariables("SERVER_PORT" ) & "<br>" &_
     "SERVER_PORT_SECURE : " & Request.ServerVariables("SERVER_PORT_SECURE" ) & "<br>" &_
     "SERVER_PROTOCOL : " & Request.ServerVariables("SERVER_PROTOCOL" ) & "<br>" &_
     "SERVER_SOFTWARE : " & Request.ServerVariables("SERVER_SOFTWARE" ) & "<br>" &_
     "URL : " & Request.ServerVariables("URL" )
 objMail.BodyFormat = 0
 objMail.MailFormat = 0
 objMail.Send
 set objMail = nothing
end sub


 
Ca génère ce type de mail à chaque erreur (lorsqu'on a mis l'appel à la fonction à l'endroit où ça a planté ;))
[quote]Error:
 
 
 
Devise introuvable dans la table DEV : 'FRA'
 
sigtie = 520036
typtie = CLI
login = 520036
password = MonPass
coddev = FRA
codlan = FRA
 
AUTH_TYPE :  
CONTENT_TYPE :  
CONTENT_LENGTH : 0
DOCUMENT :  
DOCUMENT_URI :  
DATE_GMT :  
DATE_LOCAL :  
GATEWAY_INTERFACE : CGI/1.1
HTTP_USER_AGENT : Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322)
HTTP_REFERER : http://localhost/catalog.asp?famweb=M&sfaweb=SCA
HTTP_COOKIE : codlan=ENG; user%5Fcodlan=FRA; password=MonPass; sigtie=520036; typtie=CLI; coddev=FRA; login=520036
LAST_MODIFIED :  
LOGON_USER :  
PATH_INFO : /catalog.asp
PATH_TRANSLATED : D:\docs\Bureau\e-Commerce-Dev\catalog.asp
REMOTE_ADDR : 127.0.0.1
REMOTE_HOST : 127.0.0.1
REMOTE_IDENT :  
REMOTE_USER :  
REQUEST_METHOD : GET
SCRIPT_MAP :  
SCRIPT_NAME : /catalog.asp
SERVER_NAME : localhost
SERVER_PORT : 80
SERVER_PORT_SECURE : 0
SERVER_PROTOCOL : HTTP/1.1
SERVER_SOFTWARE : Microsoft-IIS/5.0
URL : /catalog.asp[/citation]
On voit que toutes les variables d'environnement serveur sont récupérées automatiquement, afin notamment de savoir ce qui est passé en post, en get, l'url utilisé, la page précédement visitée, le protocole de connection, le nom de la page qui a planté, la version du navigateur, etc.
 
Ensuite, un certain nombre de variables globales sont présentes, notamment tout ce qui concerne le profil du client (login/pass/compte/langue/devise)
 
Tout ceci afin de pouvoir reproduire le bug sans avoir à demander au client (quand on sait qui c'est) ce qu'il a fait, car il est généralement bien incapable de dire ce qu'il a fait, et encore moins ce qu'il désirait faire... Il se sent tellement coupable d'avoir fait planter le serveur qu'il n'ose pas dire un mot, de peur de se faire engueuler (le pire c'est que c'est vrai)
 
Voilà.
 
J'aimerais savoir ce que vous en pensé, et j'espère que ca pourra aider ceux qui ne savent pas comment faire le minium.
 
J'ai pensé aussi à une autre chose, au lieu de faire des rs.Open pour éxécuter une fonction, utiliser une fonction "getRs(sql)" qui retourne le rs ouvert lorsque ça a marché, ou envoie un mail en cas d'erreur.
 
Mais à ce moment, j'ai peur de ne pas trop savoir quoi faire en cas d'erreur (arrêt de l'appli sur cette requête, ou redonner la main à l'appli générale pour qu'elle traîte l'erreur elle-même ?)

mood
Publicité
Posté le 22-07-2004 à 15:35:48  profilanswer
 


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  ASP

  [HOW TO] Gérer proprement les erreurs avec ASP

 

Sujets relatifs
[java] peut on gérer des fichiers excel ?wxPython --> Comment gérer les boutons qui ouvrent des fenêtres
[ADVANCED] gerer les connections browser .Appel Dll depuis Asp
transaction en php/mysql : comment gerer 1 trs ds plusieurs scripts ?[Php] Je debute, mais j'ai des erreurs partout :/
[ASP] Variable globale ?programmer proprement sous matlab
[ASP - Oracle] Je suis complètement chèvre ou quoi ?[ASP] Generer un camembert Excel
Plus de sujets relatifs à : [HOW TO] Gérer proprement les erreurs avec ASP


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR