Angående dubletter till extern databas

Diskussion i 'Frågor, support och diskussion' startad av appcreator, 12 jan 2012.

  1. appcreator

    appcreator Baby Droid Medlem

    Blev medlem:
    10 nov 2011
    Inlägg:
    19
    Mottagna gillanden:
    0

    MINA ENHETER

    Har en tabell med 2 fält

    name Varchar(10) Primary
    timestamp timestamp CURRENT_TIMESTAMP

    Jag skickar data från "appen" via ett phpscript till databasen.

    Funktionalitet fungerar som det är tänkt, dvs det går inte lägga till 2 poster med samma "name".

    När man skickar datan från appen:
    //http post
    try{
    HttpClient httpclient = new DefaultHttpClient();
    HttpPost httppost = new
    HttpPost("http://www.mindatabas.se/phpscript/insert.php");
    httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
    HttpResponse response = httpclient.execute(httppost);
    HttpEntity entity = response.getEntity();
    InputStream is = entity.getContent();
    Log.i("insertData", response.getStatusLine().toString());
    System.out.println("Data inserted");
    }

    catch(Exception e) // om man försöker lägga till en dublett så
    // hamnar man inte här. Jag kan alltså inte särskilja när post läggs till eller inte
    {
    Log.e("log_tag", "Error in http connection "+e.toString());
    System.out.println("Data not inserted, try to change pathname");

    Toast.makeText(getApplicationContext(), "Data not inserted, try to change pathname",
    Toast.LENGTH_SHORT).show();
    }
    }

    När man lägger till en post som redan finns via phpMyAdmin så får man ju ett felmeddelande MySQL said:

    #1062 - Duplicate entry 'databaspost' for key 1, hur tar man hand om detta via phpscriptet/java?


    Jag har en lösning på problemet, det är att man skapar ett phpscript som kollar om namnet finns i DB och skickar tillbaka namesträngen via JSONArray till appen
    Är inte nöjd med den lösningen eftersom det känns som det blir ännu ett "onödigt"/resurskrävande anrop till databasen.

    Så min fråga är om det finns en bättre lösning för att hantera dubletter via appen/php/databasen?
     
  2. ViLANDER

    ViLANDER Senior Droid Medlem

    Blev medlem:
    12 dec 2009
    Inlägg:
    1 594
    Mottagna gillanden:
    172

    MINA ENHETER

    Lägg gärna längre kodsnuttar inom en CODE-tagg.

    Innan jag börjar ge ett lösningsförslag; har jag förstått det rätt om du inte kan skilja på när en post har lagts till och när den ger ett felmeddelande angående dubbletter?
     
  3. woody

    woody Teen Droid Medlem

    Blev medlem:
    3 sept 2009
    Inlägg:
    319
    Mottagna gillanden:
    19

    MINA ENHETER

    Jag förstår kanske inte riktigt problemet, men ger mig på en gissning.

    Problem:
    Du har en androidapp som anropar en webbtjänst som i sin tur lägger in data som kommer från appen i en databas.

    Om appen anropar webbtjänsten med ett namn som redan finns i databasen ska felhantering ske i appen.

    Lösning:
    På serversidan måste du när du gör insert fånga upp sql-felet och returnera lämplig HTTP-statuskod(4XX eller 5XX).
    I appen måste du kolla vilken HTTP-statuskod du fick. Om det var 200 OK är allt frid och fröjd, om det var den felkod du valde vet du att namnet redan fanns i databasen. Var det någon annan statuskod hanteras det på något annat lämpligt sätt.
     
  4. appcreator

    appcreator Baby Droid Medlem

    Blev medlem:
    10 nov 2011
    Inlägg:
    19
    Mottagna gillanden:
    0

    MINA ENHETER

    Hej!


    Vilander: Jag kan inte skilja på om en post har lagts till eller ej. Däremot så fungerar det inte att lägga till en dublett vilket är bra. Jag vill dock gärna kunna visa för användaren att "uppladdningen" av en post inte fungerat.

    Felmeddelandet visas ej när jag laddar upp en dublett post via appen utan det visar sig bara när jag använder administrationsverktyget, phpMyAdmin

    woody: datat skickas från app->phpscript->databas

    yes, jag vill kunna informera användaren om att det redan finns en post med tex name= swedroid.


    Får HTTP/1.1 200 OK, oavsett om det gick lägga till en post eller inte

    Jag kör detta phpscript...
    Kod:
    //' databaskoppling
    //'
    mysql_select_db("mindatabas", $con);
    
    
    $name = $_POST['namn']; // namn skickas från appen
    
    // Addera en ny post till databasen
    
    mysql_query("INSERT INTO findmypath (name, timestamp_) VALUES('$name', CURRENT_TIMESTAMP) ") or die(mysql_error());
    
    mysql_close($con)
    
    
    Mvh/appcreator
     
  5. DroidStop

    DroidStop Youth Droid Medlem

    Blev medlem:
    20 okt 2010
    Inlägg:
    137
    Mottagna gillanden:
    4

    MINA ENHETER

    Hej!
    En eventuell lösning är att i ditt phpscript göra så här:

    Kod:
    $errorString = "";
    mysql_query("INSERT INTO findmypath (name, timestamp_) VALUES('$name', CURRENT_TIMESTAMP) ") or $errorString = mysql_error();
    
    $insertedRows = mysql_affected_rows();
    if (($errorString != "") || ($insertedRows < 1))
    { 
     header('HTTP/1.0 404 Not Found');
    }
    
    Om du ger dej på den här lösningen så är det enormt viktigt att du inte skrivit någon output innan du skickar headern, annars kommer du inte att se 404 som returstatus.

    I de fall jag gjort liknande saker har jag använt mej av svarstexten eller htmlen som anger hur det gått.. T.ex i php
    Kod:
     if ($insertedRows == 1) 
     {
       echo "1";
     }
    else 
    {
      echo "0".$errorString;
     }
    
    Och sedan i Java
    Kod:
      String responseText = getResponseBodyText(response);
      String resultCode = responseText.subString(0,1);
      if (!resultCode.equals("1"))
      {
        showToast("Nått gick fel" + responseText.subString(1));
      }
    
    Koden är direkt från huvudet och fel kan förekomma.. getResponseBodyText måste du skriva själv, men du kanske förstår poängen? Hojta till annars så försöker jag vara lite tydligare..

    Lycka till
     
  6. appcreator

    appcreator Baby Droid Medlem

    Blev medlem:
    10 nov 2011
    Inlägg:
    19
    Mottagna gillanden:
    0

    MINA ENHETER

    Hej!

    Jag får inte till detta
    header('HTTP/1.0 404 Not Found')

    ..så jag försöker med din andra lösning.

    förstår tänket, men jag vet inte riktigt hur jag ska ta vara på strängen echo


    mvh/appcreator
     
  7. ReaPadda

    ReaPadda Youth Droid Medlem

    Blev medlem:
    30 jan 2011
    Inlägg:
    155
    Mottagna gillanden:
    24

    MINA ENHETER

    Varför inte göra en select i ditt php-script först för att kolla om det redan ligger samma data i tabellen? Om samma hittas returnerar du en sak till appen, om ingen hittas så gör du en insert och returnerar nåt annat?
     
  8. appcreator

    appcreator Baby Droid Medlem

    Blev medlem:
    10 nov 2011
    Inlägg:
    19
    Mottagna gillanden:
    0

    MINA ENHETER

    ReaPadda: Jo får väl göra det, får nog inte till nån annan lösning.
     
  9. dhanjel

    dhanjel Senior Droid Medlem

    Blev medlem:
    9 dec 2009
    Inlägg:
    1 414
    Mottagna gillanden:
    278
    Operatör:
    Telia
    Telefon:
    iPhone 11

    MINA ENHETER

    Operatör:
    Telia
    Telefon:
    iPhone 11
    Skriv en procedur istället i mysql som först kollar om namnet redan finns, annars så lägger den till det. Skicka tillbaka en flagga, tex 0 om den redan fanns och 1 om det gick bra.

    Att "testa för att se om det smäller" är ingen snygg lösning.
     
  10. appcreator

    appcreator Baby Droid Medlem

    Blev medlem:
    10 nov 2011
    Inlägg:
    19
    Mottagna gillanden:
    0

    MINA ENHETER

    Hej!
    Har löst problemet. tack för visat intresse!

    Kod:
    $DBname=""; // Här sparas name som hämtas från DB
    $name= $_REQUEST['Name']; // namnet som användaren angett i appen 
     
    // Hämta posten som efterfrågas
    $q=mysql_query("SELECT name ...............
    
    while($db_field=mysql_fetch_assoc($q)){
            $output[]=$db_field;
            $DBname=$db_field['name'];      
    }
    // Finns det en existerande post som heter likadant som den användaren angav? 
    if($DBname==$name) 
    {
             print(json_encode($output)); // Ja, skicka namnet som en bekräftels på att den redan existerar en post med det namnet
    }
    else{ // Nej, det fanns ingen post med det namnet, då sparar vi posten efter konstens alla regler   
    // Addera en ny post 
    //$sql= mysql_query("INSERT INTO .......
    } 
    
    mysql_close(); 

    Sen tar jag hand om JSON i appen, meddelar användaren om det finns en dublett eller om databasen uppdaterats

    mvh/Appcreator
     
    Last edited: 19 jan 2012
  11. uncoloured

    uncoloured Teen Droid Medlem

    Blev medlem:
    21 feb 2010
    Inlägg:
    325
    Mottagna gillanden:
    16

    MINA ENHETER

    Bra att du fått till en lösning som funkar för dig.

    Personligen tycker jag det är helt onödigt att göra en select först. Använd dig istället av MySQL's felkoder och PHP's inbyggda funktioner för detta.

    Efter att du kört din INSERT-query så kan man kolla dels hur många rader som påverkats - mysql_affected_rows() - samt svars/felkoden från MySQL - mysql_errno(). En lista med felkoder finns länkad i manualen, om inget fel uppstått så får man alltid en 0.

    Att använda sig av HTTP status codes är väldigt bra och är enligt mig något man bör använda när man kommunicerar på det här sättet. I princip alla klienter och implementationer kan då avgöra om allt gått bra (200) eller om något gått fel.

    Sist men inte minst så brukar man (jag) kommunicera med någon typ av protokoll istället för vanlig text, t.ex. JSON eller XML. Lite snabb pseudokod:

    Kod:
    // Kör en insert mot databasen
    $sql = 'INSERT INTO `table` ...';
    $data = mysql_query($sql) or die('Error in query');	// die() bör bytas ut mot egen felhantering som kör ut rätt HTTP kod
    
    $intRows = mysql_affected_rows($data);
    $intError = mysql_errno();
    
    // Kontrollera att EN rad påverkats och att inget fel uppstått
    if ($intRows != 1 OR $intError != 0) {
    	// Inserten gick inte igenom, hantera felet genom att
    	// kontrollera värdet på $intError
    	header('HTTP/1.0 400 Bad Request'); // t.ex, men outputta alltid enligt standard
    	outputResult(false, 'Duplicate entry');
    	die();
    }
    
    // Allt verkar ha gått bra så vi kör på
    outputResult(true, 'Success');
    die();
    
    
    // Enkel funktion som skickar ut ett statusmeddelande i JSON
    function outputResult($boolSuccess, $strMessage) {
    	// Skicka ut en header som berättar att detta är ett JSON-dokument
    	header('Content-type: application/json; charset=utf8');
    	
    	// Sätt ihop en liten array som vi sen konverterar till JSON
    	$arrResponse = array(
    		'success' => $boolSuccess,
    		'message' => $strMessage
    	);
    	
    	$strJson = json_encode($arrResponse);
    	echo $strJson;
    }
    
    
    På det här sättet får du tillbaks ett JSON-objekt till din app. Du kan då enkelt utläsa om success = true (allt gått bra), samt får med ett litet meddelande som beskriver eventuella fel.
     
  12. appcreator

    appcreator Baby Droid Medlem

    Blev medlem:
    10 nov 2011
    Inlägg:
    19
    Mottagna gillanden:
    0

    MINA ENHETER

    Uncoloured: nu börjar vi närma oss svaret på min fråga :)

    MetalMan var väl inne på samma sak men det är alltid lättare att förstå med en större bit kod.

    Får väl försöka få till den lösningen. Eller så ger jag fan i och pillar på min fungerande lösning. Vi får se om jag har tid, nu har det ju dykt upp andra problem som behöver lösas. :)

    mvh/appcreator
     
  13. uncoloured

    uncoloured Teen Droid Medlem

    Blev medlem:
    21 feb 2010
    Inlägg:
    325
    Mottagna gillanden:
    16

    MINA ENHETER

    Klart du ska försöka få till en bra lösning, funktionerna kan du ju återanvända sen till andra projekt :)

    Jag vet inte hur pass verklighetsförankrad din ovanstående kodsnutt är, men tänk framförallt på MySQL injection och att säkra upp dina variabler innan du slänger in dem i MySQL. Finns väldigt mycket skrivet om detta så jag går inte in på det här, men ett enkelt sätt är att använda mysql_real_escape string:

     
  14. appcreator

    appcreator Baby Droid Medlem

    Blev medlem:
    10 nov 2011
    Inlägg:
    19
    Mottagna gillanden:
    0

    MINA ENHETER

    Där kom en "grejj" till ;). Har du några bra länkar på MYSQL injections? För det spelar väl ingen roll om man begränsar användaren i APPEN, injectionerna kommer väl troligtvis inte därifrån.

    Hursom helst, i appen har jag input via en ruta

    <EditText
    android:id=....
    android:hint=...
    android:layout_toLeftOf=....
    android:layout_height="60dip" android:layout_width="110dip" android:maxLength="6" android:inputType="textCapCharacters"/>
    <Button
    android:id=....
    android:text=......
    android:layout_alignParentRight="true"
    android:layout_height="60dip" android:layout_width="110dip"/>

    Nåt specifikt jag behöver tänka på säkerhetsmässigt alltså?

    mvh/appcreator
     
    Last edited: 23 jan 2012
  15. uncoloured

    uncoloured Teen Droid Medlem

    Blev medlem:
    21 feb 2010
    Inlägg:
    325
    Mottagna gillanden:
    16

    MINA ENHETER

    Nu börjar vi kanske spåra ur lite väl från din ursprungsfråga :) Som sagt, det finns hur mycket skrivet som helst om MySQL-injections och hur du skyddar dig, bara att Googla dig fram. Jag gav dig ett exempel ovan (mysql_real_escape_string).

    Man bör alltid förutsätta att de värden man samlar in för att använda i en SQL-query är "farliga" och/eller inkorrekta. Man kan aldrig veta vem eller vad det är som skickar datat till backenden. Dvs. ALL typ av verifikation av att värdena uppfyller förväntade krav (maxlängd, minlängd, integer/string etc) ska alltid ske i sista steget - dels på kodnivå, samt i de flesta fall även på databasnivå (fälttyper osv).

    Sen ska/kan man även lägga in dessa kontroller på textboxar och dyl. i GUI, men det är enbart för att hjälpa användaren att göra rätt och visa relevanta felmeddelanden.
     
  16. appcreator

    appcreator Baby Droid Medlem

    Blev medlem:
    10 nov 2011
    Inlägg:
    19
    Mottagna gillanden:
    0

    MINA ENHETER

    Tänkte göra om min fungerande lösning till uncoloured´s exempel

    men har kört fast ..
    Försöker bryta ner koden i exemplet till att bara skicka över data till appen via json_encode


    php

    Kod:
     //databaskopplingen
     mysql_connect("localhost", ...
     
     
    $boolSuccess=true;
    $strMessage='done';
    // Enkel funktion som skickar ut ett statusmeddelande i JSON
    //function outputResult($boolSuccess, $strMessage) {
    	// Skicka ut en header som berättar att detta är ett JSON-dokument
    	header('Content-type: application/json; charset=utf8');
    	
    	// Sätt ihop en liten array som vi sen konverterar till JSON
    	$arrResponse = array(
    		'success' => $boolSuccess,
    		'message' => $strMessage
    	);
    	
    
    	$strJson = json_encode($arrResponse);
    	echo $strJson;
    
    Java: resulterar i A JSONObject text must begin with '{' at character 0 of
    http://stackoverflow.com/questions/7332712/trying-to-return-data-from-php-with-json-to-android
    Kod:
     
      try{
            JSONObject obj = new JSONObject(result); // har testat enligt länken ovan, men icke...hamnar längst ner i "catch(JSONException)"
    		returnstring= obj.getString("success");  //success eller message finns i arrayen
            System.out.println(returnstring); // skriver till loggen
        }catch(JSONException e){
                Log.e("log_tag", "Error parsing data "+e.toString());
                System.out.println("Data not collected" + returnstring);   
        }
    	
    nån som har förslag på en lösning

    mvh/appcreator
     
    Last edited: 12 feb 2012
  17. uncoloured

    uncoloured Teen Droid Medlem

    Blev medlem:
    21 feb 2010
    Inlägg:
    325
    Mottagna gillanden:
    16

    MINA ENHETER

    Härligt! :)

    Börja med PHP-manualen för json_encode. Finns bl.a. ett antal flags man kan använda sig av, dock inget jag någonsin behövt.

    Ett JSONObject är, som namnet antyder, ett objekt. En JSONArray är naturligtvis en array. De definieras inom {} respektive [].

    Object = {}
    Array = []
    Object som innehåller två arrays = {"myArray": [1, 2, 3], "second": [10,30]}

    Du får titta på den output ditt PHP-skript levererar. Kör jag din kod på min server så får jag ut ett fint JSON-object:
    Kod:
    {"success":true,"message":"done"}
    Ser din output ut sådär så är väl nästa logiska steg att titta på hur din Java-applikation uppfattar det hela. Titta på variabeln result utan att försöka göra det till ett objekt eller en array och se om det verkar stämma.

    Sist men inte minst, om det fortfarande inte fungerar... titta på teckenkodningen på servern och i ditt PHP-dokument. I ditt fall skickar du ut en header som inte bara säger att det är ett JSON-dokument, utan också att det skickas som UTF8. Det här är lite av en historia för sig, men det är något man också måste fundera över. Jag rekommenderar UTF8 överallt och lite till, i databas, tabeller, celler, PHP-dokument, databasuppkoppling, serveroutput osv... Edit: Det viktiga är att man kör samma överallt, annars får man problem.
     
  18. appcreator

    appcreator Baby Droid Medlem

    Blev medlem:
    10 nov 2011
    Inlägg:
    19
    Mottagna gillanden:
    0

    MINA ENHETER

    Hej!

    Jodå detta fungerar ju utmärkt. Vet inte vad jag ska med detta till i appen men jag har väl missat poängen :)

    "header('HTTP/1.0 400 Bad Request'); // t.ex, men outputta alltid enligt standard"

    Kan förstå om man använder det på någon websida....
    mvh/appcreator
     
  19. uncoloured

    uncoloured Teen Droid Medlem

    Blev medlem:
    21 feb 2010
    Inlägg:
    325
    Mottagna gillanden:
    16

    MINA ENHETER

    Du använder det för att sätta HTTP status codes. Precis som med allting annat kan man självklart välja att inte göra det och lösa det på annat sätt.

    När man kommunicerar över HTTP så tycker åtminstone jag att det är ganska smart att använda de verktyg som finns, t.ex. status code för att se vad som händer.

    200 = ok, fortsätt processa
    403 = unauthorized, be användaren logga in exempelvis
    500 = server error, meddela användaren att ett internt fel uppstått och att man kan försöka igen om en stund.

    Och så vidare... :)