ABAP HTTP Client: Llamar REST-APIs

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

Los clientes HTTP permiten la comunicacion con APIs REST externas desde ABAP. Hay dos clases principales: CL_HTTP_CLIENT (clasica) y CL_WEB_HTTP_CLIENT (moderna/Cloud).

Vision general

ClaseDisponibilidadRecomendacion
CL_HTTP_CLIENTABAP clasicoOn-Premise
CL_WEB_HTTP_CLIENTABAP Cloud, desde 7.54Cloud, nuevos desarrollos

CL_HTTP_CLIENT (Clasico)

1. GET-Request simple

DATA: lo_client TYPE REF TO if_http_client,
lv_response TYPE string,
lv_code TYPE i.
" Crear cliente
cl_http_client=>create_by_url(
EXPORTING
url = 'https://api.example.com/users'
IMPORTING
client = lo_client
).
" Enviar request
lo_client->send( ).
lo_client->receive( ).
" Leer response
lv_code = lo_client->response->get_status( )-code.
lv_response = lo_client->response->get_cdata( ).
" Cerrar conexion
lo_client->close( ).
IF lv_code = 200.
WRITE: / 'Exito:', lv_response.
ELSE.
WRITE: / 'Error:', lv_code.
ENDIF.

2. GET con parametros de Query

DATA: lo_client TYPE REF TO if_http_client,
lv_url TYPE string.
" URL con parametros
lv_url = 'https://api.example.com/users?status=active&limit=10'.
cl_http_client=>create_by_url(
EXPORTING url = lv_url
IMPORTING client = lo_client
).
" O establecer parametros individualmente
lo_client->request->set_form_field(
name = 'status'
value = 'active'
).
lo_client->send( ).
lo_client->receive( ).
DATA(lv_response) = lo_client->response->get_cdata( ).
lo_client->close( ).

3. POST-Request con JSON

DATA: lo_client TYPE REF TO if_http_client,
lv_json TYPE string.
" Crear cuerpo JSON
lv_json = '{"name":"Max","email":"[email protected]"}'.
cl_http_client=>create_by_url(
EXPORTING url = 'https://api.example.com/users'
IMPORTING client = lo_client
).
" Establecer metodo HTTP
lo_client->request->set_method( if_http_request=>co_request_method_post ).
" Establecer Content-Type
lo_client->request->set_header_field(
name = 'Content-Type'
value = 'application/json'
).
" Establecer cuerpo
lo_client->request->set_cdata( lv_json ).
" Enviar
lo_client->send( ).
lo_client->receive( ).
DATA(lv_status) = lo_client->response->get_status( )-code.
DATA(lv_response) = lo_client->response->get_cdata( ).
lo_client->close( ).
CASE lv_status.
WHEN 200 OR 201.
WRITE: / 'Creado exitosamente'.
WHEN 400.
WRITE: / 'Solicitud invalida'.
WHEN 401.
WRITE: / 'No autorizado'.
WHEN OTHERS.
WRITE: / 'Error:', lv_status.
ENDCASE.

4. PUT-Request (Update)

lv_json = '{"name":"Max Mustermann","email":"[email protected]"}'.
cl_http_client=>create_by_url(
EXPORTING url = 'https://api.example.com/users/123'
IMPORTING client = lo_client
).
lo_client->request->set_method( if_http_request=>co_request_method_put ).
lo_client->request->set_header_field(
name = 'Content-Type'
value = 'application/json'
).
lo_client->request->set_cdata( lv_json ).
lo_client->send( ).
lo_client->receive( ).
lo_client->close( ).

5. DELETE-Request

cl_http_client=>create_by_url(
EXPORTING url = 'https://api.example.com/users/123'
IMPORTING client = lo_client
).
lo_client->request->set_method( if_http_request=>co_request_method_delete ).
lo_client->send( ).
lo_client->receive( ).
DATA(lv_status) = lo_client->response->get_status( )-code.
lo_client->close( ).
IF lv_status = 204 OR lv_status = 200.
WRITE: / 'Eliminado exitosamente'.
ENDIF.

6. Basic Authentication

cl_http_client=>create_by_url(
EXPORTING url = 'https://api.example.com/secure'
IMPORTING client = lo_client
).
" Establecer Basic Auth
lo_client->authenticate(
username = 'myuser'
password = 'mypassword'
).
lo_client->send( ).
lo_client->receive( ).
lo_client->close( ).

7. Bearer Token (OAuth)

DATA: lv_token TYPE string VALUE 'eyJhbGciOiJIUzI1NiIs...'.
cl_http_client=>create_by_url(
EXPORTING url = 'https://api.example.com/protected'
IMPORTING client = lo_client
).
" Establecer header Authorization
lo_client->request->set_header_field(
name = 'Authorization'
value = |Bearer { lv_token }|
).
lo_client->send( ).
lo_client->receive( ).
lo_client->close( ).

8. Headers personalizados

lo_client->request->set_header_field(
name = 'X-API-Key'
value = 'my-api-key-12345'
).
lo_client->request->set_header_field(
name = 'Accept'
value = 'application/json'
).
lo_client->request->set_header_field(
name = 'Accept-Language'
value = 'es-ES'
).

9. Establecer Timeout

cl_http_client=>create_by_url(
EXPORTING url = 'https://api.example.com/slow'
IMPORTING client = lo_client
).
" Timeout en segundos
lo_client->send( timeout = 30 ).
lo_client->receive( timeout = 30 ).

10. Certificados SSL/HTTPS

" Para certificados auto-firmados
lo_client->propertytype_accept_cookie = if_http_client=>co_enabled.
" Certificado cliente SSL (STRUST)
lo_client->set_ssl_id( 'ANONYM' ). " O nombre de PSE
" Ignorar errores SSL (SOLO PARA PRUEBAS!)
lo_client->propertytype_logon_popup = if_http_client=>co_disabled.

CL_WEB_HTTP_CLIENT (Moderno/Cloud)

11. GET-Request (Cloud)

DATA: lo_client TYPE REF TO if_web_http_client,
lo_response TYPE REF TO if_web_http_response.
TRY.
" Destination desde SM59 o HTTP-URL
DATA(lo_destination) = cl_http_destination_provider=>create_by_url(
i_url = 'https://api.example.com/users'
).
lo_client = cl_web_http_client_manager=>create_by_http_destination(
i_destination = lo_destination
).
lo_response = lo_client->execute( if_web_http_client=>get ).
DATA(lv_status) = lo_response->get_status( )-code.
DATA(lv_body) = lo_response->get_text( ).
lo_client->close( ).
CATCH cx_web_http_client_error INTO DATA(lx_error).
WRITE: / 'HTTP Error:', lx_error->get_text( ).
CATCH cx_http_dest_provider_error INTO DATA(lx_dest).
WRITE: / 'Destination Error:', lx_dest->get_text( ).
ENDTRY.

12. POST-Request (Cloud)

TRY.
DATA(lo_destination) = cl_http_destination_provider=>create_by_url(
i_url = 'https://api.example.com/users'
).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination(
i_destination = lo_destination
).
DATA(lo_request) = lo_client->get_http_request( ).
" Establecer headers
lo_request->set_header_field(
i_name = 'Content-Type'
i_value = 'application/json'
).
" Establecer cuerpo
lo_request->set_text( '{"name":"Max","email":"[email protected]"}' ).
" Ejecutar POST
DATA(lo_response) = lo_client->execute( if_web_http_client=>post ).
DATA(lv_status) = lo_response->get_status( )-code.
DATA(lv_body) = lo_response->get_text( ).
lo_client->close( ).
CATCH cx_web_http_client_error cx_http_dest_provider_error INTO DATA(lx_error).
WRITE: / lx_error->get_text( ).
ENDTRY.

13. Con Destination desde SM59

TRY.
" Usar destination de SM59
DATA(lo_destination) = cl_http_destination_provider=>create_by_comm_arrangement(
comm_scenario = 'Z_MY_SCENARIO'
service_id = 'Z_MY_SERVICE'
).
" O directamente por nombre
DATA(lo_dest_by_name) = cl_http_destination_provider=>create_by_cloud_destination(
i_name = 'MY_DESTINATION'
).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination(
i_destination = lo_destination
).
" ...
CATCH cx_http_dest_provider_error INTO DATA(lx_error).
WRITE: / lx_error->get_text( ).
ENDTRY.

Ejemplos practicos

14. Cliente REST completo

CLASS zcl_api_client DEFINITION PUBLIC.
PUBLIC SECTION.
TYPES: BEGIN OF ty_user,
id TYPE i,
name TYPE string,
email TYPE string,
END OF ty_user,
ty_users TYPE TABLE OF ty_user WITH EMPTY KEY.
METHODS: constructor
IMPORTING iv_base_url TYPE string
iv_api_key TYPE string OPTIONAL.
METHODS: get_users
RETURNING VALUE(rt_users) TYPE ty_users
RAISING cx_web_http_client_error.
METHODS: create_user
IMPORTING is_user TYPE ty_user
RETURNING VALUE(rs_user) TYPE ty_user
RAISING cx_web_http_client_error.
PRIVATE SECTION.
DATA: mv_base_url TYPE string,
mv_api_key TYPE string.
METHODS: create_client
RETURNING VALUE(ro_client) TYPE REF TO if_http_client.
ENDCLASS.
CLASS zcl_api_client IMPLEMENTATION.
METHOD constructor.
mv_base_url = iv_base_url.
mv_api_key = iv_api_key.
ENDMETHOD.
METHOD create_client.
cl_http_client=>create_by_url(
EXPORTING url = mv_base_url
IMPORTING client = ro_client
).
IF mv_api_key IS NOT INITIAL.
ro_client->request->set_header_field(
name = 'X-API-Key'
value = mv_api_key
).
ENDIF.
ro_client->request->set_header_field(
name = 'Accept'
value = 'application/json'
).
ENDMETHOD.
METHOD get_users.
DATA(lo_client) = create_client( ).
lo_client->request->set_uri_path( '/users' ).
lo_client->send( ).
lo_client->receive( ).
DATA(lv_response) = lo_client->response->get_cdata( ).
lo_client->close( ).
" Parsear JSON
/ui2/cl_json=>deserialize(
EXPORTING json = lv_response
CHANGING data = rt_users
).
ENDMETHOD.
METHOD create_user.
DATA(lo_client) = create_client( ).
lo_client->request->set_uri_path( '/users' ).
lo_client->request->set_method( if_http_request=>co_request_method_post ).
lo_client->request->set_header_field(
name = 'Content-Type'
value = 'application/json'
).
DATA(lv_json) = /ui2/cl_json=>serialize( data = is_user ).
lo_client->request->set_cdata( lv_json ).
lo_client->send( ).
lo_client->receive( ).
DATA(lv_response) = lo_client->response->get_cdata( ).
lo_client->close( ).
/ui2/cl_json=>deserialize(
EXPORTING json = lv_response
CHANGING data = rs_user
).
ENDMETHOD.
ENDCLASS.
" Uso
DATA(lo_api) = NEW zcl_api_client(
iv_base_url = 'https://api.example.com'
iv_api_key = 'my-key'
).
TRY.
DATA(lt_users) = lo_api->get_users( ).
DATA(ls_new_user) = lo_api->create_user(
is_user = VALUE #( name = 'Test' email = '[email protected]' )
).
CATCH cx_web_http_client_error INTO DATA(lx_error).
WRITE: / lx_error->get_text( ).
ENDTRY.

15. Manejo de errores

TRY.
cl_http_client=>create_by_url(
EXPORTING url = 'https://api.example.com'
IMPORTING client = lo_client
).
lo_client->send( ).
lo_client->receive( ).
DATA(lv_status) = lo_client->response->get_status( ).
CASE lv_status-code.
WHEN 200.
" Exito
DATA(lv_data) = lo_client->response->get_cdata( ).
WHEN 400.
" Bad Request
DATA(lv_error) = lo_client->response->get_cdata( ).
WRITE: / 'Solicitud invalida:', lv_error.
WHEN 401.
WRITE: / 'Autenticacion fallida'.
WHEN 403.
WRITE: / 'Acceso denegado'.
WHEN 404.
WRITE: / 'Recurso no encontrado'.
WHEN 500.
WRITE: / 'Error del servidor'.
WHEN OTHERS.
WRITE: / 'HTTP-Status:', lv_status-code, lv_status-reason.
ENDCASE.
CATCH cx_root INTO DATA(lx_error).
WRITE: / 'Error:', lx_error->get_text( ).
CLEANUP.
IF lo_client IS BOUND.
lo_client->close( ).
ENDIF.
ENDTRY.

16. Enviar datos binarios (archivos)

DATA: lv_file_content TYPE xstring.
" Archivo como datos binarios
lv_file_content = '...'. " ej. desde archivo o tabla
lo_client->request->set_method( if_http_request=>co_request_method_post ).
lo_client->request->set_header_field(
name = 'Content-Type'
value = 'application/octet-stream'
).
lo_client->request->set_data( lv_file_content ).
lo_client->send( ).
lo_client->receive( ).

17. Multipart/Form-Data

DATA: lv_boundary TYPE string VALUE '----WebKitFormBoundary7MA4YWxk'.
lo_client->request->set_header_field(
name = 'Content-Type'
value = |multipart/form-data; boundary={ lv_boundary }|
).
DATA(lv_body) = |--{ lv_boundary }\r\n| &&
|Content-Disposition: form-data; name="field1"\r\n\r\n| &&
|value1\r\n| &&
|--{ lv_boundary }\r\n| &&
|Content-Disposition: form-data; name="file"; filename="test.txt"\r\n| &&
|Content-Type: text/plain\r\n\r\n| &&
|File content here\r\n| &&
|--{ lv_boundary }--\r\n|.
lo_client->request->set_cdata( lv_body ).

Notas importantes / Mejores practicas

  • CL_WEB_HTTP_CLIENT para ABAP Cloud y nuevos desarrollos.
  • Siempre llamar close() - tambien en caso de error (TRY-FINALLY).
  • Destination (SM59) usar para endpoints configurables.
  • Timeouts establecer para APIs externas lentas.
  • Certificados SSL importar en STRUST para HTTPS.
  • JSON serializar/deserializar con /ui2/cl_json.
  • Evaluar codigos de estado HTTP para manejo de errores.
  • API-Keys/Tokens no hardcodear en el codigo - usar Destinations.
  • Logging implementar para debugging.
  • Considerar configuracion de proxy en sistemas productivos.