cancel
Showing results for 
Search instead for 
Did you mean: 

Issues for generating OData multipart batch/changeset with CL_HTTP_CLIENT

Sandra_Rossi
Active Contributor
169

I had some weird issues with CL_HTTP_CLIENT 7.40 SP23 for generating an HTTP request to consume an OData V2.0 service, e.g. a simple creation (POST), and to help other people, I'm posting a solution right now for generating this kind of HTTP request with 2 nested multipart and an application/http message at the lowest level:

  • POST .../$batch with Content-Type: multipart/mixed; boundary=batch_xxxxxx
    • Content-Type: multipart/mixed; boundary=changeset_xxxxxx
      • Content-Type: application/http (the body is the POST request)

The weird issues were for instance:

  • a multipart/form-data content-type without boundary definition,
  • a loss of the contents inside the changeset,
  • a content-type duplicated from the inner multipart,
  • etc.

In fact, all these issues come from a wrong usage of the CL_HTTP_* methods, methods called in the wrong order, etc.

NB: it's just for test purpose, to be used in an old ABAP system, I don't want to log in a recent ABAP system to generate a Service Consumption Class (How to use the OData Client Proxy in SAP S/4 HANA ... - SAP Community).

The final HTTP request should look like this:

 

POST https://dummy/sap/opu/odata/sap/MD_CUSTOMER_MASTER_SRV_01/$batch?sap-client=100 HTTP/1.0
Content-Type: multipart/mixed; boundary=batch_847E-D772-BCB1
Content-Length:          382
x-csrf-token: x2VarmW8AM0BAsY_flyWlg==
dataserviceversion: 2.0

--batch_847E-D772-BCB1
Content-Type: multipart/mixed; boundary=changeset_26E1-B3AE-6F51

--changeset_26E1-B3AE-6F51
Content-Type: application/http

POST C_BusinessPartnerCustomerCreate?sap-client=100&BusinessPartnerCategory='1' HTTP/1.0
Content-Type: application/json
Content-Length: 0
dataserviceversion: 2.0


--changeset_26E1-B3AE-6F51--

--batch_847E-D772-BCB1--

 

Sandra

View Entire Topic
Sandra_Rossi
Active Contributor

You may use this ABAP code to generate the HTTP request shown above. It's shown in the variable final_xstring_request in UTF-8 encoding:

 

    DATA(sap_client) = `100`.
    DATA(host) = `https://dummy`.
    cl_http_client=>create_by_url( EXPORTING url    = host
                                   IMPORTING client = DATA(client) ).

    client->request->set_header_field(
        name  = '~request_uri'
        value = |{ host }/sap/opu/odata/sap/MD_CUSTOMER_MASTER_SRV_01/$batch?sap-client={ sap_client }| ).
    client->request->set_method( 'POST' ).

    client->request->set_header_field( name  = 'x-csrf-token'
                                       value = 'x2VarmW8AM0BAsY_flyWlg==' ).
    client->request->set_header_field( name  = 'DataServiceVersion'
                                       value = '2.0' ).
    client->request->set_content_type( 'multipart/mixed; boundary=batch_847E-D772-BCB1' ).

    DATA(multipart_batch) = client->request->add_multipart( suppress_content_length = abap_true ).
    multipart_batch->set_content_type( 'multipart/mixed; boundary=changeset_26E1-B3AE-6F51' ).

    DATA(multipart_changeset) = multipart_batch->add_multipart( suppress_content_length = abap_true ).
    multipart_changeset->set_content_type( 'application/http' ).
    cl_http_client=>create_by_url( EXPORTING url    = host
                                   IMPORTING client = DATA(client_bp_cust_create) ).
    client_bp_cust_create->request->set_method( 'POST' ).
    client_bp_cust_create->request->set_header_field( name  = 'Content-Type'
                                                      value = 'application/json' ).
    client_bp_cust_create->request->set_header_field( name  = 'DataServiceVersion'
                                                      value = '2.0' ).
    client_bp_cust_create->request->set_header_field(
        name  = '~request_uri'
        value = |C_BusinessPartnerCustomerCreate?sap-client={ sap_client }&BusinessPartnerCategory='1'| ).
    DATA(xstring_request_bp_cust_create) = client_bp_cust_create->request->to_xstring( ).
    multipart_changeset->set_data( xstring_request_bp_cust_create ).

    client->request->preserve_multipart_boundary( abap_true ).

    DATA(final_xstring_request) = client->request->to_xstring( ).

You may check automatically the final value within ABAP Unit tests:

    cl_abap_unit_assert=>assert_equals(
        act = cl_abap_codepage=>convert_from( final_xstring_request )
        exp = concat_lines_of(
                  sep   = |\r\n|
                  table = VALUE string_table(
                      ( `POST https://dummy/sap/opu/odata/sap/MD_CUSTOMER_MASTER_SRV_01/$batch?sap-client=100 HTTP/1.0` )
                      ( `Content-Type: multipart/mixed; boundary=batch_847E-D772-BCB1` )
                      ( `Content-Length:          382` )
                      ( `x-csrf-token: x2VarmW8AM0BAsY_flyWlg==` )
                      ( `dataserviceversion: 2.0` )
                      ( `` )
                      ( `--batch_847E-D772-BCB1` )
                      ( `Content-Type: multipart/mixed; boundary=changeset_26E1-B3AE-6F51` )
                      ( `` )
                      ( `--changeset_26E1-B3AE-6F51` )
                      ( `Content-Type: application/http` )
                      ( `` )
                      ( `POST C_BusinessPartnerCustomerCreate?sap-client=100&BusinessPartnerCategory='1' HTTP/1.0` )
                      ( `Content-Type: application/json` )
                      ( `Content-Length: 0` )
                      ( `dataserviceversion: 2.0` )
                      ( `` )
                      ( `` )
                      ( `--changeset_26E1-B3AE-6F51--` )
                      ( `` )
                      ( `--batch_847E-D772-BCB1--` )
                      ( `` ) ) ) ).