28. oktober 2008 - 16:02
Der er
7 kommentarer og
1 løsning
Insert into + split
Hej,
Jeg prøver at ligge inputs fra min form ind i en database, men i stedet for at have mange forskellige funktioner der i bund og grund gør det samme prøver jeg at lave en opret funktion der variere i opbygning alt afhængig af hvilke data der skal gemmes... Jeg håber det giver mening når i ser koden:
Formen:
Response.Write "<form method='post' action='action.asp?a=Opret&DatabaseNavn=MinDatabase&Felter=Overskrift,Tekst'>"
Response.Write "<center><table>"
Response.Write "<tr>"
Response.Write "<td class='c_table'>Overskrift</td>"
Response.Write "<td class='c_table'><input class='log' type='text' size='30' name='Overskrift'></td>"
Response.Write "</tr>"
Response.Write "<tr valign='top'>"
Response.Write "<td class='c_table'>Tekst</td>"
Response.Write "<td><textarea class='log' wrap='on' name='Tekst' rows='13' cols='60'></textarea></td>"
Response.Write "</tr>"
Response.Write "<tr>"
Response.Write "<td class='c_table'>Status</td>"
Response.Write "<td><select class='log' size='1' name='Status'>"
Response.Write "<option value='0'>Skjult</option>"
Response.Write "<option value='1'>Synlig</option>"
Response.Write "</select></td>"
Response.Write "</tr>"
Response.Write "<tr>"
Response.Write "<td class='c_table'> </td>"
Response.Write "<td class='c_table'><input class='but' type='submit' value='Opret' name='submit'></td>"
Response.Write "</tr>"
Response.Write "</table></center>"
Response.Write "</form>"
Og funktionen:
Felter = Request.Querystring("Felter")
FeltStr = ""
ValueStr = ""
ArrFelter = Split(Felter, ",")
For n = 0 To ubound(ArrFelter)
FeltStr = FeltStr &","& ArrFelter
ValueStr = ValueStr &"'"& Replace(Trim(Request.form("ArrFelter")), "'", "''")&"', "
Next
Status = Replace(Trim(Request.form("Status")), "'", "''")
SQLgem = "Insert Into "&DatabaseNavn&" ("&FeltStr&") Values("&ValueStr&" '"&Status&"')"
Conn.Execute(SQLgem)
Set SQLgem = Nothing
Jeg håber der er en der kan hjælpe mig med at få funktionen til at fungere...
På forhånd tak.
Martin
29. oktober 2008 - 19:34
#4
Med mindre det er noget du kun skal bruge på din egen computer, synes jeg du sætter for meget sikkerhed over styr ved at oplyse om både tabelnavn og hvilke felter du har i databasen (eller i det mindste en delmængde heraf). Men hvis du synes det er OK, så vil jeg lade dig om det.
Mht. funktionens udformning, så er jeg da enig med keysersoze i, at du bør vide lidt mere om felternes typer og hvilke andre oplysninger du ellers kan komme i nærheden af til brug for validering. Med den løsning jeg har i tankerne vil dette dog kræve en lidt mere stringent udformning af felternes navne i formularen, da jeg vil foreslå dig at lægge felttýpen ind i feltets navn (men det er måske også det keysersoze foreslår...?). Helt konkret forestiller jeg mig noget i stil med:
<form method="post">
<input type="hidden" name="handling" value="opdater">
<input type="hidden" name="tabelnavn" value="person">
<input type="hidden" name="felter" value="navn,adresse,postnr,by">
<input type="hidden" name="noeglefelter" value="id">
<input type="hidden" name="integer_4_id" value="<%=request.form("integer_4_id")%>">
Navn: <input type="text" name="varchar_10_navn" value="<%=request.form("varchar_10_navn")%>"><br>
Adresse: <input type="text" name="varchar_50_adresse" value="<%=request.form("varchar_50_adresse")%>"><br>
Postnr: <input type="text" name="integer_4_postnr" value="<%=request.form("integer_4_postnr")%>"><br>
By: <input type="text" name="varchar_50_by" value="<%=request.form("varchar_50_by")%>"><br>
<input type="submit" value="gem">
</form>
Jeg vælger altså at benytte underscore som informationsseparator og at databasekolonnens navn er det sidste der findes i feltet. Desuden vælger jeg at lægge listen med felter, tabellens navn og operationen i skjulte formular-felter da det begrænser mig mindre end, hvis disse oplysninger skulle forekomme i selve URL'en. Til opdateringer har jeg behov for at vide hvilke felter der skal bruges til at identificere den række der skal opdateres, så derfor har jeg oprettet endnu en skjult felt der indeholder nøglekolonnerne. Værdien heri skal naturligvis hentes i forbindelse med indlæsning af data fra databasen.
Nu kan jeg iterere igennem mine formularfelter og udtrække de oplysninger jeg har specificeret i formularfeltet "felter" og på behørig vis oprette et command-objekt som på nogenlunde sikker vis indsætter data i den valgte tabel. Der skal stadig laves en kontrol af om tabelnavnet forsøger SQL-injection, men hvis du ellers holder dig til en simpel navngivning af dine tabeller, dvs. kun benytter bogstaver og tal, så burde et regulært udtryk rimelig let kunne spore ondsindede forsøg på at angribe din database...
Funktionen kunne så se nogenlunde således ud:
function forberedFormData(byref sql, byref arrParams)
dim felter, feltkomponenter, vaerdiliste, whereliste
dim tabelnavn
dim feltnavn, felttype, feltlaengde
dim opretHandling, opdaterHandling
dim antalFelter
dim rx
opretHandling = strComp(request.form("handling") & "", "opret", vbTextCompare) = 0
opdaterHandling = strComp(request.form("handling") & "", "opdater", vbTextCompare) = 0
' udtræk tabelnavnet fra formularen
tabelnavn = request.form("tabelnavn") & ""
' definer regelsættet for navngivning af tabeller i denne database
' dvs. bogstaver, tal og underscore accepteres - ikke andet!
set rx = new RegExp
rx.global = true
rx.IgnoreCase = true
rx.Pattern = "^[a-z0-9\_]+$"
if not rx.test(tabelnavn) then
err.Raise 10000, "forberedFormData", "Der er muligvis forsøgt SQL-injection! Kontrollér om tabelnavnet overholder standarden for navngivning."
end if
if opretHandling then
sql = "insert into " & tabelnavn & "(" & request.form("felter") & ") VALUES("
elseif opdaterHandling then
sql = "update " & tabelnavn & " set "
else
err.raise 10001, "forberedFormData", "Der skal angives en opret- eller opdater-handling."
end if
whereliste = ""
antalFelter = 0
' foran og efterstil feltlisten med komma'er så den bliver lettere at søge i
felter = "," & request.form("felter") & ","
for each fld in request.form
feltkomponenter = split(fld,"_")
if ubound(feltkomponenter) = 2 then
felttype = feltkomponenter(0)
feltlaengde = feltkomponenter(1)
feltnavn = feltkomponenter(2)
' undersøg om feltet findes i listen over felter der skal gemmes...
if instr(1, felter, "," & feltnavn & ",", vbTextCompare) > 0 then
if len(vaerdiliste) > 0 then vaerdiliste = vaerdiliste & ","
if opretHandling then
vaerdiliste = vaerdiliste & "?"
else
vaerdiliste = vaerdiliste & "[" & feltnavn & "] = ?"
end if
set param = Server.CreateObject("ADODB.Parameter")
param.name = "@" & feltnavn
param.value = request.Form(fld)
execute "param.type = ad" & felttype
param.size = feltlaengde
redim preserve arrParams(ubound(arrParams) + 1)
set arrParams(ubound(arrParams)) = param
end if
' optæl antallet af felter der gemmes, så vi senere kan kontrollere
' om SQL-sætningen overhovedet adresserer nogle felter i tabellen.
antalFelter = antalFelter + 1
end if
next
' Hvis der er tale om en opdatering, skal der tages højde for at
' det er en eksisterende række der skal opdateres og at denne række
' identificeres via en eller flere felter i tabellen.
if opdaterHandling then
' foran og efterstil feltlisten med komma'er så den bliver lettere at søge i
felter = "," & request.form("noeglefelter") & ","
for each fld in request.form
feltkomponenter = split(fld,"_")
if ubound(feltkomponenter) = 2 then
felttype = feltkomponenter(0)
feltlaengde = feltkomponenter(1)
feltnavn = feltkomponenter(2)
' undersøg om feltet findes i listen over felter der skal gemmes...
if instr(1, felter, "," & feltnavn & ",", vbTextCompare) > 0 then
if len(whereliste) > 0 then whereliste = whereliste & ","
whereliste = whereliste & "[" & feltnavn & "] = ?"
' opret en command-parameter som skal bruges i forbindelse med
' den endelige eksekvering af kommandoen
set param = Server.CreateObject("ADODB.Parameter")
param.name = "@" & feltnavn
param.value = request.Form(fld)
execute "param.type = ad" & felttype
param.size = feltlaengde
redim preserve arrParams(ubound(arrParams) + 1)
set arrParams(ubound(arrParams)) = param
end if
' optæl antallet af felter der gemmes, så vi senere kan kontrollere
' om SQL-sætningen overhovedet adresserer nogle felter i tabellen.
antalFelter = antalFelter + 1
end if
next
sql = sql & vaerdiliste & " where " & whereliste
else
sql = sql & vaerdiliste & ")"
end if
if antalFelter = 0 then
err.raise 10002, "forberedFormSave", "Der er ikke angivet nogle felter."
end if
end function
Funktionen kan så kaldes med to output-parametre, som dels indeholder den genererede SQL
dels de parametre som skal bruges i forbindelse med kaldet til databasen. Herunder er en
lille stump kode som laver en testudskrift af resultaterne...
if strcomp(request.ServerVariables("request_method"), "post", vbTextCompare) = 0 then
dim sql, params
sql = ""
params = array()
forberedFormData sql, params
response.Write sql
response.Write "<br>"
response.Write "antal parametre: " & ubound(params) + 1
response.Write "<br>"
for each p in params
response.Write p.name & "<br>"
next
end if
Selve kaldet til databasen er i princippet elementært:
dim sql, params, cmd
sql = ""
params = array()
forberedFormData sql, params
set cmd = Server.CreateObject("ADODB.Command")
set cmd.ActiveConnection = connObject ' connObject er dit åbne connectionobjekt til databasen
for each p in params
cmd.Parameters.Append p
next
cmd.Execute
Hele koden er ikke modnet (dvs. gennemtestet) og der er helt sikkert mange ting som kan optimeres og gøres bedre, men princippet er i det mindste ridset op...
Der er bla. en mangel ifht. at teste feltlisten for SQL-injection forsøg, men samme princip som for tabelnavnet kan jo benyttes.