[ABAP] Einfacher REST-Service (ohne OData)

Piyush Philip auf sap.com hat eine simple Möglichkeit beschrieben, wie man in ABAP einen REST-Service mit den entsprechenden CRUD-Methoden implementieren kann. Dazu ist im wesentlichen nur eine eigene Handler-Klasse zu implementieren, die von IF_HTTP_EXTENSION erbt. Diese Handler-Klasse wird von einem Service des ICF gerufen.

  1. SE80 -> Neue Klasse anlegen
  2. In der Klasse im Reiter Interfaces: vom Interface IF_HTTP_EXTENSION erben
  3. die Interface-Methode IF_HTTP_EXTENSION~HANDLE_REQUEST implementieren (implementiert nur REST, kein OData-Sevice!)
    METHOD if_http_extension~handle_request.
    * Typen
      TYPES: BEGIN OF ty_json_request,
               param1 TYPE char64,
               param2 TYPE char64,
             END OF ty_json_request.
    
      TYPES: BEGIN OF ty_json_response,
               success   TYPE string,
               msg       TYPE string,
               operation TYPE string,
             END OF ty_json_response.
    
    * Variablen
      DATA: rv_response TYPE ty_json_response.
      DATA: it_path_info TYPE stringtab.
      DATA: it_query_strings TYPE stringtab.
    
    * Header Fields auslesen
      DATA: it_header_fields TYPE tihttpnvp.
      server->request->get_header_fields( CHANGING fields = it_header_fields ).
    
    * Request Methode (POST, GET, PUT, DELETE ...)
      DATA(lv_request_method) = server->request->get_header_field( name = '~request_method' ).
    
    * URL Pathinfo
      DATA(lv_path_info) = server->request->get_header_field( name = '~path_info' ).
    * erstes '/' entfernen
      SHIFT lv_path_info LEFT BY 1 PLACES.
      SPLIT lv_path_info AT '/' INTO TABLE it_path_info.
    
    * URL Parameter (Query Strings) für Auswertung
      DATA(lv_query_string) = server->request->get_header_field( name = '~query_string' ).
      SPLIT lv_query_string AT '&' INTO TABLE it_query_strings.
    
    * Request Methoden behandeln
      CASE lv_request_method.
    * C (Create)
        WHEN 'POST'.
    * Beispiel: per Request übermittelte JSON-Daten auslesen und behandeln
    * Content-Typ auslesen
          DATA(lv_request_content_type) = server->request->get_content_type( ).
    
    * Wenn Content-Typ JSON
          IF lv_request_content_type CP 'application/json'.
    * JSON-Daten auslesen und nach ABAP-Typ konvertieren -> Parameternamen müssen übereinstimmen, sonst HTTP 500!
            DATA(lv_request_cdata) = server->request->get_cdata( ).
    
            DATA: lv_request_abap TYPE ty_json_request.
    
            TRY.
                /ui2/cl_json=>deserialize( EXPORTING json = lv_request_cdata
                                                     pretty_name = /ui2/cl_json=>pretty_mode-camel_case
                                           CHANGING  data = lv_request_abap ).
    
              CATCH cx_root INTO DATA(e_txt).
                MESSAGE e_txt->get_text( ) TYPE 'S' DISPLAY LIKE 'E'.
            ENDTRY.
    
            rv_response = VALUE #( success   = 'true'
                                   msg       = lv_request_abap-param1 && ' | ' && lv_request_abap-param2
                                   operation = lv_request_method ).
          ELSE.
            rv_response = VALUE #( success   = 'false'
                                   msg       = 'Request content type must be application/json'
                                   operation = lv_request_method ).
          ENDIF.
    * R (Read)
        WHEN 'GET'.
    * Wert des URL-Parameters 'param' lesen
          DATA(lv_param) = server->request->get_form_field( 'param' ).
    
          rv_response = VALUE #( success   = 'true'
                                 msg       = lv_param
                                 operation = lv_request_method ).
    * U (Update)
        WHEN 'PUT'.
          rv_response = VALUE #( success   = 'true'
                                 msg       = ''
                                 operation = lv_request_method ).
    * D (Delete)
        WHEN 'DELETE'.
          rv_response = VALUE #( success   = 'true'
                                 msg       = ''
                                 operation = lv_request_method ).
    
    * unerlaubte Methoden abfangen
        WHEN OTHERS.
    * Fehlercode 405 zurückgeben
          server->response->set_status( code   = '405'
                                        reason = 'method not allowed' ).
    
          server->response->set_header_field( name  = 'allow'
                                              value = 'POST, GET, PUT, DELETE' ).
    
          rv_response = VALUE #( success   = 'false'
                                 msg       = 'error'
                                 operation = lv_request_method ).
      ENDCASE.
    
    * Service-Response als JSON zurückgeben
      DATA(o_conv) = NEW /ui2/cl_abap2json( ).
      DATA(lv_json_str) = o_conv->struc2json( iv_struc = rv_response ).
    
      server->response->set_header_field( name  = 'Content-Type'
                                          value = 'application/json; charset=iso-8859-1' ).
    
      server->response->set_cdata( data = lv_json_str ).
    
    ENDMETHOD.
    
  4. SICF -> neue ICF-Node (Sub-Element) unterhalb default_host -> sap -> bc als neuen unabhängigen Service anlegen
  5. in der Handlerliste des Service die erstellte Klasse als „Handler“ angeben
  6. auf die neue ICF-Node rechtsklicken -> Service aktivieren
  7. auf die neue ICF-Node rechtsklicken -> Service testen
  8. URL aus Browserfenster kopieren (http://domain.de/sap/bc/servicename?sap-client=900)
  9. im Browser mir einem Rest-Plugin (FireFox-Prugin: Rested) testen

Links

[ABAP] Mit Hilfe der Klasse cl_fxs_image_processor Infos zu einem Bild auslesen, Größe verändern (Resize) und Bildformat konvertieren (Convert)

* Im Beispiel wird das Eingabebild (PNG) nach JPG konvertiert und auf 800x600px in der Größe verändert

* Dateiname der Bilddatei zum hochladen
PARAMETERS: p_fname TYPE file_table-filename OBLIGATORY.
PARAMETERS: p_width TYPE i DEFAULT 800.
PARAMETERS: p_height TYPE i DEFAULT 600.

* wenn die F4-Hilfe für den Dateinamen aufgerufen wird
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_fname.

  DATA: lv_rc TYPE i.
  DATA: it_files TYPE filetable.
  DATA: lv_action TYPE i.

* File-Tabelle leeren, da hier noch alte Einträge von vorherigen Aufrufen drin stehen können
  CLEAR it_files.

* FileOpen-Dialog aufrufen
  TRY.
      cl_gui_frontend_services=>file_open_dialog( EXPORTING
                                                    file_filter = |png (*.png)\|*.png\|{ cl_gui_frontend_services=>filetype_all }|
                                                  CHANGING
                                                    file_table  = it_files
                                                    rc          = lv_rc
                                                    user_action = lv_action ).

      IF lv_action = cl_gui_frontend_services=>action_ok.
* wenn Datei ausgewählt wurde
        IF lines( it_files ) > 0.
* ersten Tabelleneintrag lesen
          p_fname = it_files[ 1 ]-filename.
        ENDIF.
      ENDIF.

    CATCH cx_root INTO DATA(e_text).
      MESSAGE e_text->get_text( ) TYPE 'S' DISPLAY LIKE 'E'.
  ENDTRY.

START-OF-SELECTION.

  TRY.

  DATA: lv_filesize TYPE w3param-cont_len.
  DATA: lv_filetype TYPE w3param-cont_type.
  DATA: it_bin_data TYPE w3mimetabtype.

  WRITE: / p_fname.

* Bild auf Appl. Server hochladen (binary)
  cl_gui_frontend_services=>gui_upload( EXPORTING
                                          filename   = |{ p_fname }|
                                          filetype   = 'BIN'
                                        IMPORTING
                                          filelength = lv_filesize
                                        CHANGING
                                          data_tab   = it_bin_data ).

* solix -> xstring
  DATA(lv_img_data) = cl_bcs_convert=>solix_to_xstring( it_solix = it_bin_data ).

* FXS-Objekt holen
      DATA(o_ip) = NEW cl_fxs_image_processor( ).

* Bild zum Konverter hinzufügen
      DATA(lv_hndl) = o_ip->add_image( iv_data = lv_img_data ).

      DATA: lv_mimetype	TYPE string.
      DATA: lv_xres	TYPE i.
      DATA: lv_yres	TYPE i.
      DATA: lv_xdpi	TYPE i.
      DATA: lv_ydpi	TYPE i.
      DATA: lv_bitdepth	TYPE i.

* Infos holen
      o_ip->get_info( EXPORTING
                        iv_handle   = lv_hndl
                      IMPORTING
                        ev_mimetype = lv_mimetype
                        ev_xres     = lv_xres
                        ev_yres     = lv_yres
                        ev_xdpi     = lv_xdpi
                        ev_ydpi     = lv_ydpi
                        ev_bitdepth = lv_bitdepth ).

* Infos ausgeben
      WRITE: / 'MIME-Type:', lv_mimetype.
      WRITE: / '        X:', lv_xres.
      WRITE: / '        Y:', lv_yres.
      WRITE: / '    X-DPI:', lv_xdpi.
      WRITE: / '    Y-DPI:', lv_ydpi.
      WRITE: / '      Bit:', lv_bitdepth.

* Größe ändern (Resize)
      o_ip->resize( EXPORTING
                      iv_handle = lv_hndl
                      iv_xres   = p_width
                      iv_yres   = p_height ).

* Konvertierung (Convert) nach JPG
* Zielformate können nur sein (andernfalls gibt es eine Exception vom Typ cx_sy_range_out_of_bounds):
*   cl_fxs_mime_types=>co_image_bitmap
*   cl_fxs_mime_types=>co_image_png
*   cl_fxs_mime_types=>co_image_jpeg
      o_ip->convert( EXPORTING
                      iv_handle = lv_hndl
                      iv_format = cl_fxs_mime_types=>co_image_jpeg ).

* Infos holen
      o_ip->get_info( EXPORTING
                        iv_handle   = lv_hndl
                      IMPORTING
                        ev_mimetype = lv_mimetype
                        ev_xres     = lv_xres
                        ev_yres     = lv_yres
                        ev_xdpi     = lv_xdpi
                        ev_ydpi     = lv_ydpi
                        ev_bitdepth = lv_bitdepth ).

* Infos ausgeben
      WRITE: / 'MIME-Type:', lv_mimetype.
      WRITE: / '        X:', lv_xres.
      WRITE: / '        Y:', lv_yres.
      WRITE: / '    X-DPI:', lv_xdpi.
      WRITE: / '    Y-DPI:', lv_ydpi.
      WRITE: / '      Bit:', lv_bitdepth.

* Binärdaten (xstring) des Bildes holen
      DATA(lv_img_conv_data) = o_ip->get_image( EXPORTING iv_handle = lv_hndl ).

* xstring -> solix
      DATA(it_img_conv_data) = cl_bcs_convert=>xstring_to_solix( iv_xstring = lv_img_conv_data ).

      WRITE: / |{ p_fname }.jpg|.

* Image lokal speichern
      cl_gui_frontend_services=>gui_download( EXPORTING
                                                filename = |{ p_fname }.jpg|
                                                filetype = 'BIN'
                                              CHANGING
                                                data_tab = it_img_conv_data ).

    CATCH cx_root INTO DATA(e_txt).
      WRITE: / e_txt->get_text( ).
  ENDTRY.

[ABAP] MIME-Repository: Objekt-ID, Pfad, Attribute, Berechtigungen

DATA: lv_io    TYPE skwf_io.
DATA: lv_error TYPE skwf_error.

* Repository-Objekt (MIME) über Pfad suchen
CALL FUNCTION 'SKWF_NMSPC_IO_FIND_BY_ADDRESS'
  EXPORTING
    url   = '/SAP/PUBLIC/xyz.jpg'
    appl  = wbmr_c_skwf_appl_name " 'MIME'
  IMPORTING
    io    = lv_io
    error = lv_error.

IF lv_error-type = 'E'.
ELSE.
  WRITE: / lv_io-objid, lv_io-objtype, lv_io-object, lv_io-class.

* Objekttyp
  CASE lv_io-objtype.
    WHEN skwfc_obtype_folder.
      WRITE: / 'Folder'.
    WHEN skwfc_obtype_any.
      WRITE: / 'Any'.
    WHEN skwfc_obtype_loio.
      WRITE: / 'Logical object'.
    WHEN skwfc_obtype_phio.
      WRITE: / 'Physical object'.
    WHEN skwfc_obtype_rela.
      WRITE: / 'Relation'.
  ENDCASE.

  TRY.
* Zugriff prüfen
      cl_mime_services=>mime_check_authority( i_operation = 'SHOW' " Aktion ('INSERT','MODIFY','SHOW','DELETE')
                                              i_object_io = lv_io ).

      DATA: lv_url TYPE skwf_url.
      DATA: lv_relative_url TYPE skwf_descr.

      DATA: lv_error2 TYPE skwf_error.

* Pfad zum Repository-Objekt (MIME) suchen
      CALL FUNCTION 'SKWF_NMSPC_IO_ADDRESS_GET'
        EXPORTING
          io           = lv_io
        IMPORTING
          url          = lv_url
          relative_url = lv_relative_url
          error        = lv_error2.

      IF lv_error2-type = 'E'.
      ELSE.
        WRITE: / lv_url.
        WRITE: / lv_relative_url.
      ENDIF.

    CATCH cx_root INTO DATA(e).
  ENDTRY.
ENDIF.

[ABAP] MIME-Repository: logische Objekt-ID, physiche Object-ID, Berechtigungen

DATA: lv_loio	TYPE skwf_io.
DATA: lv_is_folder TYPE boole_d.

DATA(o_mime_rep) = cl_mime_repository_api=>get_api( ).

TRY.
* logische IO zu MIME-Objekt holen
    o_mime_rep->get_io_for_url( EXPORTING
                                  i_url       = '/SAP/PUBLIC/xyz.jpg'
                                IMPORTING
                                  e_is_folder = lv_is_folder
                                  e_loio      = lv_loio ).

    WRITE: / lv_is_folder.
    WRITE: / lv_loio.

    DATA: lv_error TYPE skwf_error.
    DATA: it_phios TYPE STANDARD TABLE OF skwf_io WITH DEFAULT KEY.

* physical IOs zu log. IO holen
    CALL FUNCTION 'SKWF_LOIO_ALL_PHIOS_GET'
      EXPORTING
        loio  = lv_loio
      IMPORTING
        error = lv_error
      TABLES
        phios = it_phios.

    LOOP AT it_phios ASSIGNING FIELD-SYMBOL(<p>).

      DATA: lv_err TYPE skwf_error.

* Berechtigungen zu physical IO prüfen
      CALL FUNCTION 'SKWF_AUTH_OBJECT_CHECK'
        EXPORTING
          activity = skwfa_c_act_read
          object   = <p>
        IMPORTING
          error    = lv_err.

      IF lv_err-type = 'E'.
      ELSE.
        WRITE: / <p>.
      ENDIF.

    ENDLOOP.

  CATCH cx_root INTO DATA(e).
    WRITE: / e->get_text( ).
ENDTRY.

[ABAP] Bilddatei konvertieren mit Hilfe der Klasse cl_igs_image_converter (Internet Graphics Service: Image Converter)

* Demo: GRAPHICS_IGS_IMGCONV_DEMO

* Dateiname der Bilddatei zum hochladen
PARAMETERS: p_fname TYPE file_table-filename OBLIGATORY.

* wenn die F4-Hilfe für den Dateinamen aufgerufen wird
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_fname.

  DATA: lv_rc TYPE i.
  DATA: it_files TYPE filetable.
  DATA: lv_action TYPE i.

* File-Tabelle leeren, da hier noch alte Einträge von vorherigen Aufrufen drin stehen können
  CLEAR: it_files.

* FileOpen-Dialog aufrufen
  TRY.
      cl_gui_frontend_services=>file_open_dialog( EXPORTING
                                                    file_filter = |gif (*.gif)\|*.gif\|{ cl_gui_frontend_services=>filetype_all }|
                                                  CHANGING
                                                    file_table  = it_files
                                                    rc          = lv_rc
                                                    user_action = lv_action ).

      IF lv_action = cl_gui_frontend_services=>action_ok.
* wenn Datei ausgewählt wurde
        IF lines( it_files ) > 0.
* ersten Tabelleneintrag lesen
          p_fname = it_files[ 1 ]-filename.
        ENDIF.
      ENDIF.

    CATCH cx_root INTO DATA(e_text).
      MESSAGE e_text->get_text( ) TYPE 'S' DISPLAY LIKE 'E'.
  ENDTRY.

START-OF-SELECTION.

  TRY.
      DATA: lv_filesize TYPE w3param-cont_len.
      DATA: lv_filetype TYPE w3param-cont_type.
      DATA: it_bin_data TYPE w3mimetabtype.

* Bild auf Appl. Server hochladen (binary)
      cl_gui_frontend_services=>gui_upload( EXPORTING
                                              filename   = |{ p_fname }|
                                              filetype   = 'BIN'
                                            IMPORTING
                                              filelength = lv_filesize
                                            CHANGING
                                              data_tab   = it_bin_data ).

* Konverterobjekt erzeugen
      DATA(o_conv) = NEW cl_igs_image_converter( ).

* MIME-Typen (Format) für input und output festlegen
      o_conv->input  = 'image/gif'.      " Quellformat ist ein GIF
      o_conv->output = 'image/x-ms-bmp'. " Ausgabeformat ist ein BMP

* neue Bildgröße festlegen
*  o_conv->height = '100'. " neue Höhe 100
*  o_conv->width = '100'.  " neue Breite 100

* Bildaten übergeben
      o_conv->set_image( blob      = it_bin_data
                         blob_size = lv_filesize ).

* Image konvertieren
      o_conv->execute( ).

* Anzahl Ergebnisbilder > 0
      IF o_conv->get_image_count( ) > 0.
* Bilddaten im Ausgabeformat holen
        o_conv->get_image( IMPORTING
                             blob      = it_bin_data
                             blob_size = lv_filesize
                             blob_type = lv_filetype ).

* Image lokal speichern
        cl_gui_frontend_services=>gui_download( EXPORTING
                                                  filename = 'c:\temp\temp.bmp'
                                                  filetype = 'BIN'
                                                CHANGING
                                                  data_tab = it_bin_data ).
      ENDIF.
    CATCH cx_root INTO DATA(e_txt).
      WRITE: / e_txt->get_text( ).
  ENDTRY.

[ABAP] Verwendung des Engineering Client Viewer für 2D-/3D-Objekte (ECL viewer)

Beispiel 1: JPEG anzeigen

DATA: o_dock TYPE REF TO cl_gui_docking_container.

PARAMETERS: p_name TYPE string. " Dummy-Parameter

AT SELECTION-SCREEN OUTPUT.

  IF NOT o_dock IS BOUND.
* Dockingcontainer erzeugen
    o_dock = NEW #( repid = sy-repid
                    dynnr = sy-dynnr
                    side  = cl_gui_docking_container=>dock_at_right
                    ratio = 50 ).

* ECL viewer erzeugen
    DATA(o_viewer) = NEW cl_gui_ecl_2dviewer( parent = o_dock ).

* Toolbar einstellen
    o_viewer->create_toolbar( close_button = abap_false
                              navigation = abap_true
                              options = abap_true
                              remove_document = abap_true
                              tools = abap_false
                              tool_stamp = abap_false
                              tool_compare = abap_false
                              tool_measurement = abap_false
                              viewer = abap_true
                              viewer_openfile = abap_true
                              viewer_savefile = abap_true
                              viewer_closefile = abap_true ).

* JPEG anzeigen
    o_viewer->open_document( file = 'c:\temp\temp.jpg'
                             file_type = 'image/jpeg' ). " https://wiki.selfhtml.org/wiki/Referenz:MIME-Typen

  ENDIF.

Beispiel 2: PDF anzeigen

  
* ECL viewer in extra Fenster
DATA(o_viewer) = NEW cl_gui_ecl_viewerbox( ).

* Fullscreen
o_viewer->set_alignment( alignment = cl_gui_control=>align_at_left + cl_gui_control=>align_at_right + cl_gui_control=>align_at_top + cl_gui_control=>align_at_bottom ).

* PDF öffnen
o_viewer->open_document( file = 'c:\temp\temp.pdf'
                         file_type = 'application/pdf' ). " https://wiki.selfhtml.org/wiki/Referenz:MIME-Typen

Weiterführende Infos:

[ABAP] Bild (JPEG) aus dem MIME-Repository laden und anzeigen

* Demo-Programm: SAP_PICTURE_DEMO
DATA: o_dock TYPE REF TO cl_gui_docking_container.

PARAMETERS: p_name TYPE string. " Dummy-Parameter

AT SELECTION-SCREEN OUTPUT.

  IF NOT o_dock IS BOUND.
* Dockingcontainer erzeugen
    o_dock = NEW #( repid = sy-repid
                    dynnr = sy-dynnr
                    side  = cl_gui_docking_container=>dock_at_right
                    ratio = 50 ).

    DATA: lv_xstr TYPE xstring.

* MIME-API holen
    DATA(o_mime_rep) = cl_mime_repository_api=>get_api( ).

* Bild aus dem MIME-Repository laden
    o_mime_rep->get( EXPORTING
                       i_url = '/SAP/PUBLIC/Test123.jpg'
                       i_check_authority = abap_false
                     IMPORTING
                       e_content = lv_xstr ).

    DATA: it_bin_data TYPE STANDARD TABLE OF raw255.

* xstring nach binary (RAW) wandeln
    CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
      EXPORTING
        buffer     = lv_xstr
      TABLES
        binary_tab = it_bin_data.

    DATA: lv_url TYPE char255.

* temporäre URL auf das Bild erzeugen
    CALL FUNCTION 'DP_CREATE_URL'
      EXPORTING
        type                 = 'image/jpeg' " https://wiki.selfhtml.org/wiki/Referenz:MIME-Typen
        subtype              = 'jpg'
      TABLES
        data                 = it_bin_data
      CHANGING
        url                  = lv_url
      EXCEPTIONS
        dp_invalid_parameter = 1
        dp_error_put_table   = 2
        dp_error_general     = 3
        OTHERS               = 4.

* Bild über die URL laden und anzeigen
    DATA(o_pic) = NEW cl_gui_picture( parent = o_dock ).
    o_pic->set_display_mode( display_mode = cl_gui_picture=>display_mode_fit_center ).
    o_pic->load_picture_from_url_async( lv_url ).
  ENDIF.

Weiterführende Infos: Link