Samstag, 20. Oktober 2018

VFP, das Web und der ganze ReST - 3 - Leerzeichen und andere Entitäten / VFP, the web and all the ReST - 3 - BLANKS and other entities

3 - Leerzeichen und andere Entitäten / BLANKS and other entities


Der Begriff der Entität ist den meisten vermutlich durch das Entity Relationship Model (ERM) geläufig. Es stellt die Grundlage für das Entwerfen von Datenbanken und Tabellen dar und für VFP Entwickler sollte das nun wirklich kein Böhmisches Dorf sein. Als datengetriebene Entwicklungsumgebung ist dies schließlich unser tägliches Brot.

In HTML versteht man unter Entitäten jedoch etwas ganz anderes. Sie ermöglichen es bestimmte Zeichen auf eine andere Art darzustellen.

Eine einfache Suche nach "Representational State Transfer" sieht bspw. so aus:

https://www.google.com/search
?client=firefox-b
&source=hp
&ei=POOxW6TEONLQrgSN2LTQDA
&q=Representational+State+Transfer
&oq=Representational+State+Transfer
&gs_l=psy-ab.3..0l3j0...
Quelle:Google®

Dies entspricht dem grundsätzlichen Aufbau einer URI.
https://max:muster@www.example.com:8080/index.html?p1=A&p2=B#ressource
\___/   \_/ \____/ \_____________/ \__/\_________/ \_______/ \_______/
  |      |    |           |         |       |          |         |
Schema   | Kennwort      Host      Port    Pfad      Query    Fragment
      Benutzer
Quelle: https://de.wikipedia.org/wiki/URL-Encoding

Eine URI ohne Leerzeichen und mit einer festen Struktur zur Parameterübergabe.

Der erste Parameter bzw. der Beginn des Parameterblocks wird mit einem einfachen Fragezeichen eingeleitet dem ein Name=Wert Paar folgt. Alle anschließenden Parameterpaare werden mit einem kaufmännischen UND/et (&) angehängt. Die Suchmaschine würde allerdings ein ernstes Problem bekommen, wenn nun im Suchbegriff ebenfalls eines dieser beiden Zeichen enthälten wäre. Eine fehlerhafte URI wäre das Resultat.

Da die zu übermittelnden Daten über HTTP(S) transportiert werden, müssen wir berücksichtigen, dass es in HTML reservierte Zeichen und Symbole gibt, die wir klartextlich nicht übergeben dürfen. Entweder weil sie nicht ohne weiteres dargestellt werden können oder weil sie für besondere Aufgaben und Kennzeichnungen reserviert sind.

Die betreffenden Zeichen müssen zuvor in HTML verständliche Entitäten gewandelt werden. Berechtigterweise kann nun angeführt werden, dass beim http Request über die Methode setRequestHeader() ein passender Zeichensatz definiert werden kann, so dass uns eine Konvertierung erspart bleibt. Bei den von mir angesprochenen Webservices kam es jedoch trotzdem zu Zeichensatzproblemen so dass ich die folgende Zeile anschließend wieder auskommentiert habe:

oXmlHttp.setRequestHeader( [Content-Type], [text/xml;charset=Windows-1252] )

Bei URIs müssen wir eine Unterscheidung zwischen der %-basierenden und der &-basierenden Codierung treffen. Bei einfachen Name=Wert Paaren ist eine %-Codierung notwendig. Diese Codierungen sind hexadezimal. Das Leerzeichen (ASC 32) wird beispielsweise durch HEX 20 (%20) ersetzt.

Der folgende als Übergabewert zusammengestellte Parameterstring

?name=abc def&strasse=Testgasse 12

erzeugt eine ungültige URI. Mit einer %-Codierung sähe die Parameterübergabe so aus:

?name=abc%20def&strasse=Testgasse%2012

Übergeben wir jedoch als Wert einen JSON String, dann ist eine Umsetzung von speziellen Zeichen innerhalb des Strings auf Basis von "&;" meist erfolgreicher.

Eine umfangreiche Liste der XML und HTML Entitäten findet sich auf:
https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references

Hier nun ein wenig Mustercode für ein Konvertierungsprogramm in dem nicht erlaubte Zeichen in eine URI konforme Kodierung (HTML entities) transformiert werden:

FUNCTION URIConverter as String
    * // see link above for a complete list 
    LPARAMETERS vStream as String
    * // has to run first!!!!
    m.vStream = STRTRAN( m.vStream , CHR(  38 ) , [&] )   && &
    * // now the rest...
    m.vStream = STRTRAN( m.vStream , CHR(   9 ) , [    ] )        && TAB
    m.vStream = STRTRAN( m.vStream , [<] , [&lt;] )        && <
    m.vStream = STRTRAN( m.vStream , [>] , [&gt;] )        && >
    m.vStream = STRTRAN( m.vStream , CHR(  10 ) , [<br/>] ) && LF
    m.vStream = STRTRAN( m.vStream , CHR(  13 ) , [] )    && CR
    m.vStream = STRTRAN( m.vStream , ["] , [&quot;] )         && "
    m.vStream = STRTRAN( m.vStream , [ä] , [&auml;] )         && ä
    m.vStream = STRTRAN( m.vStream , [Ä] , [&Auml;] )         && Ä
    m.vStream = STRTRAN( m.vStream , [ö] , [&ouml;] )         && ö
    m.vStream = STRTRAN( m.vStream , [Ö] , [&Ouml;] )         && Ö
    m.vStream = STRTRAN( m.vStream , [ü] , [&uuml;] )         && ü
    m.vStream = STRTRAN( m.vStream , [Ü] , [&Uuml;] )         && Ü
    m.vStream = STRTRAN( m.vStream , [ß] , [&szlig;] )        && ß
    m.vStream = STRTRAN( m.vStream , [à] , [&agrave;] )       && à
    m.vStream = STRTRAN( m.vStream , [á] , [&aacute;] )       && á
    m.vStream = STRTRAN( m.vStream , [â] , [&acirc;] )        && â
    m.vStream = STRTRAN( m.vStream , [À] , [&Agrave;] )       && À
    m.vStream = STRTRAN( m.vStream , [Á] , [&Aacute;] )       && Á
    m.vStream = STRTRAN( m.vStream , [Â] , [&Acirc;] )        && Â
    m.vStream = STRTRAN( m.vStream , [è] , [&egrave;] )       && è
    m.vStream = STRTRAN( m.vStream , [é] , [&eacute;] )       && é
    m.vStream = STRTRAN( m.vStream , [ê] , [&ecirc;] )        && ê
    m.vStream = STRTRAN( m.vStream , [È] , [&Egrave;] )       && È
    m.vStream = STRTRAN( m.vStream , [É] , [&Eacute;] )       && É
    m.vStream = STRTRAN( m.vStream , [Ê] , [&Ecirc;] )        && Ê
    m.vStream = STRTRAN( m.vStream , [ì] , [&igrave;] )       && ì
    m.vStream = STRTRAN( m.vStream , [í] , [&iacute;] )       && í
    m.vStream = STRTRAN( m.vStream , [î] , [&icirc;] )        && î
    m.vStream = STRTRAN( m.vStream , [Ì] , [&Igrave;] )       && Ì
    m.vStream = STRTRAN( m.vStream , [Í] , [&Iacute;] )       && Í
    m.vStream = STRTRAN( m.vStream , [Î] , [&Icirc;] )        && Î
    m.vStream = STRTRAN( m.vStream , [ò] , [&ograve;] )       && à
    m.vStream = STRTRAN( m.vStream , [ó] , [&oacute;] )       && á
    m.vStream = STRTRAN( m.vStream , [ô] , [&ocirc;] )        && â
    m.vStream = STRTRAN( m.vStream , [Ò] , [&Ograve;] )       && À
    m.vStream = STRTRAN( m.vStream , [Ó] , [&Oacute;] )       && Á
    m.vStream = STRTRAN( m.vStream , [Ô] , [&Ocirc;] )        && Â
    m.vStream = STRTRAN( m.vStream , [ù] , [&ugrave;] )       && ù
    m.vStream = STRTRAN( m.vStream , [ú] , [&uacute;] )       && ú
    m.vStream = STRTRAN( m.vStream , [û] , [&ucirc;] )        && û
    m.vStream = STRTRAN( m.vStream , [Ù] , [&Ugrave;] )       && Ù
    m.vStream = STRTRAN( m.vStream , [Ú] , [&Uacute;] )       && Ú
    m.vStream = STRTRAN( m.vStream , [Û] , [&Ucirc;] )        && Û
    m.vStream = STRTRAN( m.vStream , [ç] , [&ccedil;] )       && ç
    m.vStream = STRTRAN( m.vStream , [Ç] , [&Ccedil;] )       && Ç
    RETURN m.vStream
ENDFUNC

Die Liste der als Entität darstellbaren Symbole und Sonderzeichen würde den Code um einiges vergrößern. Das habe ich mir im obigen Beispiel gespart und kann nach Bedarf relativ einfach ergänzt werden. Die notwendigen Informationen stehen über den darüber stehenden Link auf Abruf zur Verfügung.

Je nach Aufbau der URI sollten sowohl einzelne Parameterwerte als auch im Vorfeld generierte JSON Streams über solch einen Konverter laufen. Am Beispiel eines kompletten Objektes sieht es wie folgt aus:

TEXT TO lcJSONData TEXTMERGE NOSHOW PRETEXT 2+4+8
    "int1" : <<myCursor.Column1>>,
    "int2" : <<myCursor.Column2>>,
    "text1" : "<<ALLTRIM( myCursor.Column3 )>>",
    "int3" : <<myCursor.Column4>>,
    "text2" : "<<ALLTRIM( myCursor.Column5 )>>"
ENDTEXT 
lcJSONData = [{] + URIConverter( lcJSONData ) + [}]

Wollen wir jedoch auf Nummer sicher gehen, dann macht es durchaus Sinn, nicht erst den komplett generierten Datenblock zu konvertieren. Wollen wir die Korrektur für jedes Textfeld individuell durchführen, dann sieht der Code folgendermaßen aus:

TEXT TO lcJSONData TEXTMERGE NOSHOW PRETEXT 2+4+8
    "int1" : <<myCursor.Column1>>,
    "int2" : <<myCursor.Column2>>,
    "text1" : "<<URIConverter( ALLTRIM( myCursor.Column3 ) )>>",
    "int3" : <<myCursor.Column4>>,
    "text2" : "<<URIConverter( ALLTRIM( myCursor.Column5 ) )>>"
ENDTEXT 
lcJSONData = [{] + lcJSONData + [}]

Wie bereits erwähnt sind einzelne URI-Parameterwerte bei einem %-Konverter besser aufgehoben. Dies bedeutet, dass wir vor dem konkatenieren eines Name=Wert Paares den Wert über einen %-Konverter jagen der sowohl eine eigene Funktion als auch im bereits gesehenen Konverter als 2. Parameter aktiviert werden kann.

Im folgenden Codemuster wird der URIConverter() um einen entsprechenden Parameter ergänzt.

FUNCTION URIConverter as String
    LPARAMETERS vStream as String, vVariante as String
    m.vVariante = EVL( m.vVariante , [&] )
    DO CASE 
    CASE m.vVariante = [&]
        * // has to run first!!!!
        m.vStream = STRTRAN( m.vStream , CHR( 38 ) , [&amp;] )    && &
        * // now the rest...
        m.vStream = STRTRAN( m.vStream , CHR(  9 ) , [    ] )         && TAB
        m.vStream = STRTRAN( m.vStream , [<] , [&lt;] )        && <
        m.vStream = STRTRAN( m.vStream , [>] , [&gt;] )        && >
        m.vStream = STRTRAN( m.vStream , CHR( 10 ) , [<br/>] )  && LF
        m.vStream = STRTRAN( m.vStream , CHR( 13 ) , [] )             && CR
        m.vStream = STRTRAN( m.vStream , [ ] , [&nbsp;] )         && SPACE
        m.vStream = STRTRAN( m.vStream , ["] , [&quot;] )         && "
        m.vStream = STRTRAN( m.vStream , [ä] , [&auml;] )         && ä
        m.vStream = STRTRAN( m.vStream , [Ä] , [&Auml;] )         && Ä
        m.vStream = STRTRAN( m.vStream , [ö] , [&ouml;] )         && ö
        m.vStream = STRTRAN( m.vStream , [Ö] , [&Ouml;] )         && Ö
        m.vStream = STRTRAN( m.vStream , [ü] , [&uuml;] )         && ü
        m.vStream = STRTRAN( m.vStream , [Ü] , [&Uuml;] )         && Ü
        m.vStream = STRTRAN( m.vStream , [ß] , [&szlig;] )        && ß
        m.vStream = STRTRAN( m.vStream , [à] , [&agrave;] )       && à
        m.vStream = STRTRAN( m.vStream , [á] , [&aacute;] )       && á
        m.vStream = STRTRAN( m.vStream , [â] , [&acirc;] )        && â
        m.vStream = STRTRAN( m.vStream , [À] , [&Agrave;] )       && À
        m.vStream = STRTRAN( m.vStream , [Á] , [&Aacute;] )       && Á
        m.vStream = STRTRAN( m.vStream , [Â] , [&Acirc;] )        && Â
        m.vStream = STRTRAN( m.vStream , [è] , [&egrave;] )       && è
        m.vStream = STRTRAN( m.vStream , [é] , [&eacute;] )       && é
        m.vStream = STRTRAN( m.vStream , [ê] , [&ecirc;] )        && ê
        m.vStream = STRTRAN( m.vStream , [È] , [&Egrave;] )       && È
        m.vStream = STRTRAN( m.vStream , [É] , [&Eacute;] )       && É
        m.vStream = STRTRAN( m.vStream , [Ê] , [&Ecirc;] )        && Ê
        m.vStream = STRTRAN( m.vStream , [ì] , [&igrave;] )       && ì
        m.vStream = STRTRAN( m.vStream , [í] , [&iacute;] )       && í
        m.vStream = STRTRAN( m.vStream , [î] , [&icirc;] )        && î
        m.vStream = STRTRAN( m.vStream , [Ì] , [&Igrave;] )       && Ì
        m.vStream = STRTRAN( m.vStream , [Í] , [&Iacute;] )       && Í
        m.vStream = STRTRAN( m.vStream , [Î] , [&Icirc;] )        && Î
        m.vStream = STRTRAN( m.vStream , [ò] , [&ograve;] )       && à
        m.vStream = STRTRAN( m.vStream , [ó] , [&oacute;] )       && á
        m.vStream = STRTRAN( m.vStream , [ô] , [&ocirc;] )        && â
        m.vStream = STRTRAN( m.vStream , [Ò] , [&Ograve;] )       && À
        m.vStream = STRTRAN( m.vStream , [Ó] , [&Oacute;] )       && Á
        m.vStream = STRTRAN( m.vStream , [Ô] , [&Ocirc;] )        && Â
        m.vStream = STRTRAN( m.vStream , [ù] , [&ugrave;] )       && ù
        m.vStream = STRTRAN( m.vStream , [ú] , [&uacute;] )       && ú
        m.vStream = STRTRAN( m.vStream , [û] , [&ucirc;] )        && û
        m.vStream = STRTRAN( m.vStream , [Ù] , [&Ugrave;] )       && Ù
        m.vStream = STRTRAN( m.vStream , [Ú] , [&Uacute;] )       && Ú
        m.vStream = STRTRAN( m.vStream , [Û] , [&Ucirc;] )        && Û
        m.vStream = STRTRAN( m.vStream , [ç] , [&ccedil;] )       && ç
        m.vStream = STRTRAN( m.vStream , [Ç] , [&Ccedil;] )       && Ç
    CASE m.vVariante = [%]
        LOCAL    lcReserved as String, liLoop as Integer, ;
            lcOrgChar as String, lcHexCode as String
        * // defining chars for converting to entities        
        lcReserved    = [%?&!"#$'()*+,-./:;<=>@\{|}§] ;
                + '[ ]' + CHR( 10 ) + CHR( 13 ) ;
                + [ßäöüÄÖÜàáâÀÁÂèéêÈÉÊìíîÌÍÎòóôÒÓÔùúûÙÚÛçÇ]
        * // loop through definition string
        FOR liLoop = 1 TO LEN( lcReserved )
            * // extract char to convert    
            lcOrgChar = SUBSTR( lcReserved , liLoop , 1 )
            * // convert char to URI Hexcode entity
            lcHexCode = [%] + RIGHT( TRANSFORM( ASC( lcOrgChar ) , [@0x] ) , 2 )
            m.vStream = STRTRAN( m.vStream , lcOrgChar , lcHexCode )
        ENDFOR 
    ENDCASE 
    RETURN m.vStream
ENDFUNC

Als nächster Schritt steht nun das Umwandeln unserer auf einem Cursor basierenden Daten in einen JSON String auf der Liste der Vorarbeiten. Dazu dann mehr im nächsten Teil.


Links zu den restlichen Kapiteln:

Einführung / Introduction 

Teil 1: Abkürzungen und was sie bedeuten
           / Abbreviations and their meaning

Teil 2: Wer ReST sagt, sagt auch JSON
          / In for a ReST, in for a JSON

Teil 3: Leerzeichen und andere Entitäten
          / BLANKS and other entities

Teil 4: JSON und der goldene Konverter
          / JSON and the golden converter

Teil 5: Die Startvorbereitungen
          / preparations for launch

Teil 6: Kleine Denkpause gefällig?
          / in need of a reflection period?

Teil 7: Fertig machen zur Landung
          / preparing for landing

Keine Kommentare:

Kommentar veröffentlichen