Parameterübergabe für MySQL-Datenbankabfrage

  • 44 Antworten
  • Letztes Antwortdatum
M

MaThoPa1973

Neues Mitglied
0
Hallo Zusammen,
ich habe mich nun hier angemeldet, weil ich folgende Thematik habe:

1. Ich betreibe ein Internetportal zur Ermittlung der optimalen Dosierung von gerinnungshemmenden Medikamenten (Marcumar, Phenprocoumon, Falithrom, u.ä.). Viele von Euch werden mit dem Thema dieser Seite sicherlich nichts anfangen können, ist für meine aktuelle Problemstellung auch nicht relevant. Viele meiner Nutzer haben jedoch nach einer mobilen App für das Handy angefragt. Das Grundgerüst (Oberflächen und Verlinkung untereinander sind soweit auch schon erstellt.
2. Im Hintergrund der Internetseite läuft eine MySQL-Datenbank in der neben den ermittelten Dosierungen auch die entsprechenden LogIn-Informationen gespeichert sind.
3. Damit man über die mobile App nicht jedes mal die LogIn-Daten neu eingeben muss will es so gestallten, dass diese in einer lokalen Datei gespeichert werden. Die Routine zum Speichern und Auslesen der lokalen Datei sind bereits erstellt und funktionieren soweit.

Nun zum Kern der Thematik:
Ich möchte, dass bevor die Datei lokal angelegt wird, über eine mobile Datenverbindung geprüft wird, ob die angegebenen LogIn-Daten korrekt sind.

Hierzu habe ich bereits auf dem Server eine PHP-Funktionsdatei abgelegt, welche dann die zu sendenden LogIn-Daten mit denen der MySql-Datenbank abgleicht und ein True oder False zurück gibt. Die Abfrage ist in der PHP-Funktion auf dem Server hinterlegt und sieht folgendermaßen aus:

$sql = mysql_query("SELECT $mysqlname FROM $mysqltable WHERE $mysqlname='$username' AND $mysqlemail ='$email' AND $mysqlpwd='$passwort'");
$num = mysql_num_rows($sql);
while($e=mysql_fetch_assoc($num))
$output[]=$e;

Als Ergebnis sollte dann hier entsprechend über $Output das Ergebnis (True/False) an die App zurückgegeben werden. Aber........

Wie kann ich die in der App eingegebenen LogIn-Daten an diese Abfrage übergeben umd in der PHP-Funktionsdatei die Abfrage zu vervollständigen?

Innerhalb der Internetplattform würde das wie nachfolgend dargestellt erfolgen:

mysql_connect(MYSQL_HOST,MYSQL_BENUTZER,MYSQL_KENNWORT);
mysql_select_db(MYSQL_DATENBANK);

$username = $_POST['username'];
$passwort = $_POST['passwort'];
$email = $_POST['email'];

Aber wie übergebe ich hier 'username', 'passwort' und 'email' von der App an die Funktion?

Danke vielmals für Eure Unterstützung und Denkanstöße.

Gruß
Markus
 
Am besten benutzt du HTTPS (mit Authentifizierung).

Security with HTTPS and SSL | Android Developers

Wenn du bei dem Programm Personendaten überträgst, solltest du den Datenverkehr zusätzlich verschlüsseln. Patientendaten sind für Hacker immer interessant ...

Die andere Möglichkeit wäre eine OfflineApp. Du programmierst eine App mit eine Datenbank auf dem Gerät. Die SQL-Statement und die Tabellen hast Du ja schon.
 
  • Danke
Reaktionen: MaThoPa1973
Hallo Markus,
erst einmal vielen Dank für Deine Hinweise, bzgl. der SSL-Verschlüsselung. Grundlegend stimme ich Dir zu und werde dieses auch in meine weiteren Überlegungen und Anstrengungen mit einfließen lassen.

EIn Offline-App-Anwendung kommt jedoch nicht in Frage, dies aus zwei Gründen:
1. Mit einer Offline-App wäre ich gezwungen den von mir entwickelten Algorithmuss auf jedem Endgerät mit zu installieren und wäre somit ohne Weiteres für jeden Zugänglich. Also keine gute Idee. Hier muss ich also auf jeden Fall mit Webservices arbeiten.
2. Die ermittelten Mess- und Dosierungswerte der Nutzer benötige ich für Auswertungen, wo mir die Nutzer die Verwendung dieser in anonymisierter Form auch eingewilligt haben.

Bedingt Punkt 2 werden die Daten also auch nur in anonymer Form auf dem Server gespeichert und können nicht ohne die mir offline zur Verfügung stehenden Informationen auch keiner Person zugeordnet werden. Insofern ist die Datenbank letzten Endes nur voll mit irgendwelchen Zahlen. Darüber hinaus habe ich die LogIn-Daten und die gespeicherten Zahlen nicht nur in unterschiedlichen Tabellen, sondern auch in unterschiedlichen Datenbanken abgelegt.

Gruß
Markus
 
Hallo Leute,

die Probleme scheinen irgendwie nicht wegbleiben zu wollen:

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

Bei vorgenannten Importanweisungen sind die jeweils letzten Argumente alle rot geschrieben.

Dazu nachfolgende Fehlermeldungen:

Error:(21, 23) error: cannot find symbol class HttpResponse
Error:(22, 23) error: cannot find symbol class NameValuePair
Error:(23, 30) error: cannot find symbol class ClientProtocolException
Error:(24, 30) error: cannot find symbol class HttpClient
Error:(25, 37) error: cannot find symbol class UrlEncodedFormEntity
Error:(26, 38) error: cannot find symbol class HttpPost
Error:(27, 35) error: cannot find symbol class DefaultHttpClient
Error:(28, 31) error: cannot find symbol class BasicNameValuePair
Error:(139, 17) error: cannot find symbol class HttpClient
Error:(139, 41) error: cannot find symbol class DefaultHttpClient
Error:(140, 17) error: cannot find symbol class HttpPost
Error:(140, 37) error: cannot find symbol class HttpPost
Error:(142, 22) error: cannot find symbol class NameValuePair
Error:(142, 62) error: cannot find symbol class NameValuePair
Error:(143, 34) error: cannot find symbol class BasicNameValuePair
Error:(144, 34) error: cannot find symbol class BasicNameValuePair
Error:(145, 34) error: cannot find symbol class BasicNameValuePair
Error:(148, 40) error: cannot find symbol class UrlEncodedFormEntity
Error:(155, 17) error: cannot find symbol class HttpResponse
Error:(157, 17) error: cannot find symbol class HttpEntity

Wer kann mir hier weiterhelfen?
 
Das Package org.apache.http wurde nicht gefunden.

Ab Android 6 sollte man es nicht wirklich nicht mehr benutzen!!!

Es hat Sicherheitslücken und wird seit Android 2.3 (2011) nicht mehr aktiv weiter entwickelt (für Android). Am besten du benutzt URLConnection. Schon um in den Genuss einer besseren Performance zu kommen. Und den Lebensdauer der Batterie zu schonen.

Die andere schlechte Möglichkeit, wäre den Import des Package. Aber ich würde mich nicht darauf verlassen, dass es auch noch in den nächsten Versionen möglich ist.

Android 6.0 Changes | Android Developers
 
Hallo Markus,
danke für die Informationen, muss dann also komplett umdenken... werde mich dann mal auf die Suche machen und mich entsprechend informieren. Aber wie sieht es mit der Abwärtskompatibilität aus? Können Endgeräte mit einer niedrigeren Version des Android-Betriebssystemns dennoch dann mit der App arbeiten?

Gruß
Markus
 
Du solltest bei der URLConnection bzw. deren Subklassen keine Probleme mit Abwärtskompatibilität bekommen. Die Klassen gehören Standard Funktionsumfang von Java und das auch schon seit über zehn Jahren.
 
Habe mich dann heute mal intensiv nach der Möglichkeit mit URLConnection befasst. Eine entsprechende PHP-Datei, welche ich dann mittels URLConnection ansprechen will ist schon auf meinem Server hinterlegt, als Antwort erwarte ich dann nur eine "1" oder "0". Wie kann ich das Ergebnis dann am sinnigstens empfangen?
 
mit der Methode getInputStream() kannst du dir die Antwort des Servers als Stream holen, diesen Stream kannst du dann mit der Klasse InputStreamReader (ggf. noch BufferedReader) auslesen.
 
Hast Du da vielleicht ein kleines Tut griffbereit? Wie gesagt, es geht nur darum eine "1" oder "0" auszuwerten. Ich habe heute den ganzen Tag schon gegoogled aber alles was ich gefunden habe bezieht sich dann auf das Auswerten ganzer Seiten oder Arrays.
 
Https habe ich gerade nichts da, so gehts mit Http:

1. ObserverInterface:
Ich benutze immer ein Interface um zwischen AsyncTask und Activity zu kommunizieren, dann können mehrere ActivityKlassen mit der selben AsyncTaskKlasse arbeiten:

Code:
package com.example.httpscon;

public interface LoginObserver {
    public void handleLogin(Boolean loggedIn);
}

Deine Activity kann dieses interface an implementieren und in der handleLogin Methode dann auf die Serverantwort reagieren.

2.
Ein AsyncTask der sich mit der URL verbindet, username und passwort über Post an den Server übermittelt und dessen Antwort auswertet:
Code:
package com.example.httpscon;

import android.os.AsyncTask;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

public class LoginTask extends AsyncTask<String, Void, Boolean> {

    private LoginObserver observer;

    /**
     *
     * @param observer soll die Serverantwort verarbeiten.
     */
    public LoginTask(LoginObserver observer) {
        this.observer = observer;
    }

    /**
     * verbindet sich mit einer URL über HTTPS und übergibt User und Passwort
     * @param params [0]=URL [1]=user [2]=passwort
     * @return true falls Login erfolgreich
     */
    @Override
    protected Boolean doInBackground(String... params) {

        Boolean result = false;
        BufferedReader reader = null;
        BufferedWriter writer = null;
        try {
            HttpURLConnection conn = openHTTPConnection(params[0]);
            writer = writeDataToConnectionOutput(conn, params);
            conn.connect(); // Jetzt wird der HTTPRequest an den Server geschickt.
            reader = new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF-8"));
            String answer = reader.readLine();
            if (answer.trim().equals("1")) {
                result = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            cleanUpReaderAndWriter(reader, writer);
        }
        return result;

    }

    @Override
    protected void onPostExecute(Boolean result) {
        observer.handleLogin(result);
    }

    private BufferedWriter writeDataToConnectionOutput(HttpURLConnection conn, String[] params) throws IOException {
        BufferedWriter writer;
        StringBuilder postDaten = new StringBuilder();
        postDaten.append(URLEncoder.encode("user", "UTF-8"));
        postDaten.append("=");
        postDaten.append(URLEncoder.encode(params[1], "UTF-8"));
        postDaten.append("&");
        postDaten.append(URLEncoder.encode("passwort", "UTF-8"));
        postDaten.append("=");
        postDaten.append(URLEncoder.encode(params[2], "UTF-8"));
        writer = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream(),"UTF-8"));
        writer.write(postDaten.toString());
        return writer;
    }

    private HttpURLConnection openHTTPConnection(String param) throws IOException {
        URL url = new URL(param);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(10000);
        conn.setConnectTimeout(15000);
        conn.setRequestMethod("POST");
        conn.setDoInput(true);
        conn.setDoOutput(true);
        return conn;
    }

    private void cleanUpReaderAndWriter(BufferedReader reader, BufferedWriter writer) {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (writer != null) {
            try {
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
Ich habe den Task in Methoden mit sprechenden Namen zerlegt, sodass es hoffentlich gut verständlich ist.
 
Danke Dir vielmals.
Verstehe ich das dann insoweit richtig, dass Du neben der Activity, über welche die LogIn-Daten eingegeben werden, Du noch zwei zusätzliche Java-Module (Klassen) erstellst (public interface LoginObserver und public class LoginTask)? Oder kommt das alles in ein Modul rein und ich rufe dann per Intent von meiner Activity das Interface auf und übergebe dem die Anmeldeinformationen?

Edit: Und wie melde ich public class LoginTask und public interface LoginObserver in der Manifestdatei an? Ich kann die ja nicht als activity anmelden und egal was ich dort auswähle (activity, activity-alias, provider, service, meta-daten, etc.) ich bekomme die Klasse und das Interface nie zur Auswahl angezeigt?!

Edit2: Wäre der nachfolgende Code richtig um dann den Aufruf aus der aufrufenden Activity von public class LoginTask richtig (vorausgesetzt die Anmeldung der Klasse in der Manifestdatei ist dann richtig?

//Neues Intent anlegen
Intent checkDB = new Intent(getApplicationContext(), LoginTask.class);


//Intent mit den Daten füllen
checkDB.putExtra("params[0]", "www.meineInternetseite/Unterordner/Funktionsdatei.php");
checkDB.putExtra("params[1]", TNutzer);
checkDB.putExtra("params[2]", TEmail);
checkDB.putExtra("params[3]", TKennwort);

// Intent starten und zur zweiten Activity wechseln
startActivity(checkDB);
 
Zuletzt bearbeitet:
Die Klasse LoginTask ist eine Subklasse der Klasse AsyncTask. Schau dir mal die Doku dazu an, im Prinzip wird damit eine sehr simple Möglichkeit geschaffen, etwas in einem parallel laufenden Thread auszuführen. AsyncTasks haben nichts mit Intents zutun. Sie werden wie normale Objekte erzeugt und mit execute Methode gestartet. Beim Aufruf der execute Methode müssen die Parameter für die doInBackground Methode mitgegeben werden.
Das Interface ermöglicht es eine Referenz auf ein Objekt zu halten, ohne dessen Klasse genau zu kennen. Dadurch kannst du den LoginTask mit verschieden Klassen zu kombinieren, solange diese das Interface implementieren. Wenn du weißt, dass es in deiner App nur eine Klasse geben wird, die mit dem Task arbeiten soll, dann kannst du auch auf das Interface verzichten, und stattdessen die konkrete Klasse im Konstruktor angeben.
 
Hallo Kosmus,

ich habe das nun versucht insoweit für meine Bedürfnisse umzustellen und zu implementieren und würde die LoginTask wie folgt aufrufen:

new LoginTask().execute("MeineInternetseite/Unterverzeichnis/auszurufendeFunktion.php", TNutzer,
TEmail, TKennwort);

Ich erhalte jedoch immer eine NullPointerexeption mit Verweis auf den LoginObserver???

Gruß
Markus

Edit: Aufruf geändert in:

LoginTask LoginTask = new LoginTask();
LoginTask.execute("www.meineInternetseite/Unterverzeichunz/auszuführendeFunktion.php", TNutzer,
TEmail, TKennwort);

Die Nullpointerexeption bleibt aber gleich. Zudem ist ja hier ein Verweis auf sich selbst:

public LoginTask() {
this.observer = observer;

???

Gruß
Markus
 
Zuletzt bearbeitet:
In meinem Vorschlag sah der Konstruktor ja noch so aus
Code:
    public LoginTask(LoginObserver observer) {
        this.observer = observer;
    }

du hast ihn wahrscheinlich so abgeändert:
Code:
    public LoginTask() {
        this.observer = observer;
    }

die Zeile this.observer = observer macht dann gar nichts, weil hier nur noch das Attribut sich selbst zu gewiesen wird und da dieses vorher null war bleibt es null.In der onPostExecute Methode wird dann auf den observer zugegriffen, da dieser aber noch null ist, bekommst du eine NullPointerException.

Da ich dein coding nicht kenne, kann ich dir jetzt keine Lösung anbieten. Arbeitest du mit dem Interface und implementiert deine Activity dieses? dann wäre die einfachste Lösung, den ursprünglichen Konstruktor zur verwenden.
Wenn du beabsichtigst diese App zu veröffentlichen, sollst du dich aber nochmal intensiv damit beschäftigen, was da passiert, denn ich habe den Eindruck, dass du noch eine Verständnislücken hast.

Du musst bei der URL übrigens auch das Protokoll angeben:
"http://www.example.com/methode.php" oder besser "https://www.example.com/methode.php"
 
@Kosmos Den Ansatz mit den Observer verstehe ich nicht. Der Asyntask besitzt doch schon ein Handler. So blähst du nur den Code auf.

Code:
package com.example.httpscon;

import android.os.AsyncTask;
import android.util.Log;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLEncoder;

import javax.net.ssl.HttpsURLConnection;

public class LoginTask extends AsyncTask<String, Void, Boolean> {

    private static final int READ_TIMEOUT = 10000;
    private static final int WRITE_TIMEOUT = 15000;

    MainActivity activity;

    public LoginTask(MainActivity activity) {
        this.activity = activity;
    }

    @Override
    protected Boolean doInBackground(String... params) {

        String url = params[0];
        String user = params [1];
        String password = params[2];
        String line, result = "";

        BufferedReader reader = null;
        BufferedWriter writer = null;
        HttpsURLConnection conn = null;

        try {

            conn = (HttpsURLConnection) new URL(url).openConnection();

            conn.setReadTimeout(READ_TIMEOUT);
            conn.setConnectTimeout(WRITE_TIMEOUT);
            conn.setRequestMethod("POST");
            conn.setDoInput(true);
            conn.setDoOutput(true);

            String postData = "user="+user+"&password="+password;
            writer = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream(),"UTF-8"));
            writer.write(URLEncoder.encode(postData, "utf-8"));

            conn.connect();

            reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
            while ((line = reader.readLine()) != null) {
                result += line;
            }
            return result != null && !result.trim().equalsIgnoreCase("1");

        } catch (Exception e) {
            Log.e("ERROR","ERROR",e);
            return false;
        } finally {
            if(conn != null)
                conn.disconnect();
            try {
                if (reader != null)
                    reader.close();
            } catch (Exception e) {}
            try {
                if (writer != null)
                    writer.close();
            } catch (Exception e) {}
        }
    }

    @Override
    protected void onPostExecute(Boolean result) {
        activity.printFlag(result);
    }

Und in der Activity:

Code:
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new LoginTask(this).execute("https://mywebsite","marvin","42");
    }

    public void printFlag(boolean flag) {
        ((TextView)findViewById(R.id.text)).setText("FLAG: "+flag);
    }
}
 
Naja aufblähen... das ObserverInterface besteht aus 4 Zeilen Code (inklusive Package Angabe und allen Klammern). die Activity hätte dann zwei Wörter mehr (implements LoginObserver) und sonst wäre der Code genau so umfangreich, wie der den du gepostet hast (ich habe ihn nur in mehrere Methoden gegliedert). Dafür würde man sich im AsyncTask nicht auf eine Activity festlegen, und könnte ihn an verschiedenen stellen verwenden. Aber du hast recht, wenn man sich sicher ist, dass man das Ergebnis des AsyncTask immer mit einem Objekt der gleichen Klasse auswertet, könnte man auch auf das Interface verzichten.
 
@kosmus: Verstehe ich es richtig, dass ich dann aus meinem UI das Ergebnis des Interfaces abrufen und weiter verarbeiten kann? aber wie komme ich an den Wert?

public interface LoginObserver {
public void handleLogin(Boolean loggedIn);
}

Gruß
Markus
 
Zuletzt bearbeitet:
der Wert wird in der onPostExecute Methode an den Observer (z. B. deine Activity) übergeben.
Deine Activity könnte z. B. so aussehen:
Code:
package com.example.httpscon;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;


public class MainActivity extends Activity implements  LoginObserver, View.OnClickListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((Button) findViewById(R.id.buttonLogin)).setOnClickListener(this);
    }

    @Override
    public void handleLogin(Boolean loggedIn) {
        // Diese Methode wird vom LoginObersever Interface vorgegeben und vom AysncTask aufgerufen
        if(loggedIn) {
            Toast.makeText(this,"Login Erfolgreich", Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(this,"Login fehlgeschlagen", Toast.LENGTH_LONG).show();
        }
    }

    @Override
    public void onClick(View v) {
        // Diese Methode wird vom onClickListener Interface vorgegeben und aufgerufen wenn der User den Login Button klickt.
        if (v.getId() == R.id.buttonLogin) {
            String user = ((TextView) findViewById(R.id.editTextUser)).getText().toString();
            String passwort = ((TextView) findViewById(R.id.editTextPasswort)).getText().toString();
            if (!user.equals("") && !passwort.equals("")) {
                new LoginTask(this).execute("http://www.example.com/login.php", user, passwort);
            } else {
                Toast.makeText(this,"Bitte alle Eingabefelder ausfüllen", Toast.LENGTH_LONG).show();
            }
        }
    }
}
Bitte beachte, dass ich das jetzt nur ganz schnell runtergetippt habe, die Strings sollten am besten in eine Values XML ausgelagert werden, aber so ist es anschaulicher.
 
  • Danke
Reaktionen: MaThoPa1973
Danke vielmals @kosmus, - die Strings habe ich allesamt bei mir in einer Strings.XML ausgelagert.

Jetzt muss ich nur noch gucken, warum ich grundlegend ein negatives Ergebnis bekomme. Wenn ich die Anmeldeinformationen direkt im Browser eingebe bekomme ich ein true, wenn es über die App läuft wirft er mir grundlegend ein False aus.

Habe ich die Möglichkeit mir den kompletten Link, den er dann absendet im Logcat anzeigen zu lassen?
postDaten.printStackTrace(); funktioniert ja nicht.


EDIT: Hab es geschafft mir den gesendeten Link in die Logcat schreiben zu lassen, der zerschießt mir den ":" nach http und das "@" in der Email-Adresse :(

EDIT2: Ok, jetzt gibt es mir den Link korrekt an, gibt mir aber immer noch ein False zurück :(
 
Zuletzt bearbeitet:
Zurück
Oben Unten