postmanlabs / postman-app-support

Postman is an API platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs—faster.
https://www.postman.com
5.81k stars 839 forks source link

Support for multipart/mixed headers that can generate boundary strings and Content-Disposition #11471

Open stephenlprice opened 1 year ago

stephenlprice commented 1 year ago

Is there an existing request for this feature?

Is your feature request related to a problem?

I have a Postman collection for a Public REST API that is nearly ready to share with our customers. A key workflow on our platform allows users to upload files to their environment via the UI/REST. These "publishing" methods require support for the following header:

multipart/mixed: Content-Type: multipart/mixed; boundary=<calculated when request is sent>

Adding the header manually in this way does not work: Content-Type: multipart/mixed; boundary=--------------------------808837830458042221641451

I believe this is caused because Postman will also add Content-Disposition: form-data; to the request body as shown in the raw logs for the request. Whereas the manual boundary string is being sent correctly, this mismatched Content-Disposition attribute is unexpected since it should be Content-Disposition: mixed;:

POST /api/3.16/sites/14d41e12-74ea-4b7d-83bd-ed254673e929/datasources?datasourceType=tdsx HTTP/1.1
Content-Type: multipart/mixed; boundary=--------------------------808837830458042221641451
X-Tableau-Auth: 1lSU84EiSBKAXGt_hPU5Gg|VJS7K3mbSJcRro0BSGXoPBBsYu5IBC6k|14d41e12-74ea-4b7d-83bd-ed254673e929
User-Agent: PostmanRuntime/7.29.2
Accept: */*
Cache-Control: no-cache
Postman-Token: a05306e4-79d0-43de-96f1-8d50b5426a21
Host: 10ay.online.tableau.com
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: AWSELB=A1C7138F06714863BC67FB7B4BBB58E53F877896C9BF741843C826233D1F67A88AB8B733971A833BFCC5EF7A67795ABA4A5EC3D87B74C8D6293A5A1263BB5EFE685E5ECE1C4DFAAE9CBCF370BFCEBB3C0290447EB5; hid=10aypd-hap02
Content-Length: 1601179

----------------------------808837830458042221641451
Content-Disposition: form-data; name="request_payload"
Content-Type: text/xml
<tsRequest>
<datasource name="SuperStore"
useRemoteQueryAgent="False"
description="SuperStore Sales Data Source">
<project id="4972f17f-a8f8-4a92-a3ec-8f89371cda49" />
</datasource>
</tsRequest>
----------------------------808837830458042221641451
Content-Disposition: form-data; name="tableau_datasource"; filename="Sample - Superstore.tdsx"
Content-Type: application/octet-stream
<Sample - Superstore.tdsx>
----------------------------808837830458042221641451--

Our servers respond to the above request with:

<error code="400000">
    <summary>Bad Request</summary>
    <detail>Payload is either malformed or incomplete</detail>
</error>

Our endpoint requires that the Request Body look like this:

--boundary-string
Content-Disposition: name="request_payload"
Content-Type: text/xml
<tsRequest>
    <datasource name="Sales"
    useRemoteQueryAgent="False"
    description="Sales Data Source">
        <connectionCredentials name="janedoe"
        password="xxxxxx"
        embed="True" />
        <project id="ff2a8360-3c2b-4b05-a793-7607c602e1fb" />
    </datasource>
</tsRequest>
--boundary-string
Content-Disposition: name="tableau_datasource"; filename="Sales.TDSX"
Content-Type: application/octet-stream
    content-of-datasource-file
--boundary-string--

Describe the solution you'd like

We would like the same level of support for multipart/mixed as is currently offered for multipart/form-data headers. We also have methods that use multipart/form-data and users are able to successfully send requests with Postman.

Currently, multipart/form-data headers are created by the Postman UI automatically and result in this: Content-Type: multipart/form-data; boundary=<calculated when request is sent>

Which will result in a header that looks like this when sent: Content-Type: multipart/form-data; boundary=--------------------------808837830458042221641451

It is not necessary to significantly alter the current Postman UI, perhaps provide a dropdown so users can choose between mixed and form-data content types under the form-data selector. This will effectively create valid boundary strings automatically for the end user.

Additionally when selecting multipart/mixed, the Content-Disposition must be set to the correct value (Content-Disposition: mixed;?) rather than Content-Disposition: form-data;.

Describe alternatives you've considered

Manually writing the header value. This creates valid Content-Type and boundary strings but will still add form-data as the Content-Disposition.

I have also tried sending requests using a multipart/form-data header generated automatically by the UI and I get the following error response from our servers:

<error code="406000">
    <summary>Bad Request</summary>
    <detail>Content type 'multipart/form-data;boundary=--------------------------615924037189507167035811' not supported</detail>
</error>

Users can send successful requests via cURL however, it is possible that lack of first-class support for multipart/mixed will make it difficult or impossible to adequately describe the method in a collection and generate valid code without requiring users to understand how to configure a cURL request to make it work.

There are other similar GH issues that are either open or have been closed as duplicates: https://github.com/postmanlabs/postman-app-support/issues/8203 https://github.com/postmanlabs/postman-app-support/issues/6933 https://github.com/postmanlabs/postman-app-support/issues/1104 https://github.com/postmanlabs/postman-app-support/issues/814 https://github.com/postmanlabs/postman-app-support/issues/6140

Additional context

In other words, a key workflow/scenario on our platform is currently blocked since our aim is to enable internal and external stakeholders to send requests and generate valid code without needing to have enough expertise to do so from our API documentation alone.

This is stopping us from making a public release of the collection to Salesforce's Public Workspace.

stephenlprice commented 1 year ago

@jeremymayo @joseacortez91 @illonage @dzucker-tab

stephenlprice commented 1 year ago

The following cURL request generated by Postman works:

curl --location --request POST 'https://10ay.online.tableau.com/api/3.16/sites/{{your-site-id}}/datasources?datasourceType=tdsx' \
--header 'Content-Type: multipart/mixed; boundary=--------------------------808837830458042221641451' \
--header 'X-Tableau-Auth: {{valid-api-key}}' \
--header 'Cookie: AWSELB=A1C7138F06714863BC67FB7B4BBB58E53F877896C9BF741843C826233D1F67A88AB8B733971A833BFCC5EF7A67795ABA4A5EC3D87B74C8D6293A5A1263BB5EFE685E5ECE1C4DFAAE9CBCF370BFCEBB3C0290447EB5; hid=10aypd-hap02' \
--form 'request_payload="<tsRequest>
    <datasource name=\"SuperStore\"
    useRemoteQueryAgent=\"False\"
    description=\"SuperStore Sales Data Source\">
        <project id=\"4972f17f-a8f8-4a92-a3ec-8f89371cda49\" />
  </datasource>
</tsRequest>";type=text/xml' \
--form 'tableau_datasource=@"Sample - Superstore.tdsx"'

W3 defines multipart/mixed as:

7.2.2 The Multipart/mixed (primary) subtype The primary subtype for multipart, "mixed", is intended for use when the body parts are independent and intended to be displayed serially. Any multipart subtypes that an implementation does not recognize should be treated as being of subtype "mixed".

Microsoft describes it as:

multipart/mixed [RFC1521]: The multipart/mixed content type is used when the body parts are independent and need to be bundled in a particular order. When a UA does not recognize a multipart subtype, it will treat the message as multipart/mixed.

If you look at RFC1521 it indicates that "mixed" is the primary subtype of the multipart content type and a pretty big gap for Postman in that case. On the other hand multipart/form-data is defined under RFC1867 for "Form-based File Upload in HTML".