kiota icon indicating copy to clipboard operation
kiota copied to clipboard

multipart/form-data request content type is not handled properly

Open baywet opened this issue 3 years ago • 4 comments

The content serialization won't work as we don't have a serializer for it. Adding a serializer should be trivial using the natively available libraries on the platform to build the body so encoding is handled for us.

More reading

Originally reported AB#9788

baywet avatar Jun 07 '21 13:06 baywet

Some example to test https://docs.microsoft.com/en-us/graph/api/section-post-pages?view=graph-rest-1.0

baywet avatar Sep 16 '21 11:09 baywet

when implemented, we should clear the appsettings.json to be able to ship self-contained executable

baywet avatar Mar 18 '22 15:03 baywet

See spec https://www.rfc-editor.org/rfc/rfc7578.html

darrelmiller avatar Mar 18 '22 15:03 darrelmiller

OpenAPI specification example of multipart forms. https://spec.openapis.org/oas/v3.1.0#encoding-object-example

In OpenAPI 3.0, JSON Schema isn't rich enough to describe multipart forms, so you may need a combination of schema object and encoding object. However, the encoding object is rarely used, so I suggest we wait until we add support for OpenAPI 3.1 as then all the information that is needed is in the JSON Schema. This will avoid us having to add support for the encoding object.

We should still be able to support multipart/form-data media types with scalar parts without support for the encoding object.

darrelmiller avatar Mar 18 '22 15:03 darrelmiller

This one is a bit more complex to handle since multipart are effectively a composition of other serialization formats. This new serializer/deserializer would effectively need to be a composition of others (configurable, should be easily done). The breaking change comes where models need to carry the information somehow that property A is serialized as JSON and property B as YAML. We don't have a place holder to carry that information today and it'd probably represent a breaking change.

It'd also be a break of our promise that models being generated are serialization format agnostic.

@darrelmiller to think about that further and come up with guidance.

baywet avatar Dec 02 '22 21:12 baywet

Another interesting point that @ddyett brought up is the fact that there's probably not even a way to describe the sub parts mime type in open API today. At the end of the day we're most likely blocked by that dependency besides giving customers access to the stream directly and some task design to compose the request body like we do for batches today.

baywet avatar Dec 13 '22 22:12 baywet

Meeting notes: we don't need an additional task, we could code-gen them but use an aggregator of parts instead of a stream.

Example of usage:

var multipartFormBody = new MultipartFormBody(); // this would come from abstractions
multipartFormBody.AddStructuredPart("data", "application/json", oneNotPageParsable);
multipartFormBody.AddRawPart("picture", "image/jpeg", stream, optionalHeaders);
await client.Me.Notes.PostAsync(multipartFormBody);
//we'd new a new serialization/parse node library with a reference to the registry
var multipartFormBody = await client.Me.Notes.GetAsync(); // pass the parse node registry so it can be used when trying to get parts
var pageInfo = multipartFormBody.GetStructuredPart<PageInfo>("data");
var headers = multipartFormBody.GetPartHeaders("data")
var imageStream = multipartFormBody.GetRawPart("picture");

baywet avatar Jan 20 '23 15:01 baywet

With this approach however we might be missing the complex types/models as nothing in the description with tie the schemas to the endpoints, more investigations needed.

baywet avatar Jan 24 '23 18:01 baywet

@darrelmiller I believe we could use an extension to bring the types over automatically and maybe an extension rule as well.

paths:
  /pets:
    post:
      requestBody:
        required: true
        content:
          multipart/form-data:
            x-ms-used-schemas:
              - $ref: '#/components/schemas/Pet'

What do you think? We could also add a validation rule for when we find this content type but no extension. (and do exactly the same for responses)

baywet avatar Feb 06 '23 20:02 baywet

We don't need an extension, the content of a multipart form data payload can in fact be entirely described today. https://github.com/OAI/OpenAPI-Specification/pull/3167#discussion_r1116340967

baywet avatar Feb 24 '23 13:02 baywet

repos created for the occasion https://github.com/microsoft/kiota-serialization-multipart-dotnet https://github.com/microsoft/kiota-serialization-multipart-go

baywet avatar Apr 06 '23 14:04 baywet

@andrueastman @rkodev I've finally been able to make progress on that... I still need to work on the generation part and on the Go/Java implementations but you can have a look at https://github.com/microsoft/kiota-abstractions-dotnet/pull/106 and https://github.com/microsoft/kiota-serialization-multipart-dotnet to start giving me feedback.

in terms of usage here is what it looks like

var mpBody = new MultipartBody();
mpBody.AddOrReplacePart("Presentation", "text/html", htmlPayload);
mpBody.AddOrReplacePart("imageBlock1", "image/png", imgStream);

await apiClient.Users["id"].OneNote.Pages.PostAsync(mpBody);

I've considered for a while trying to put all the parts as parameters of PostAsync, but that'd mean that adding a part in the schema would be binary breaking. Not ideal. Plus there are cases where we don't know all the parts in advance (e.g. one note, the number of images in any given page is variable). That kind of requires the user to know the part names (not an issue for OneNote since all the part names except for presentation depend on what's in presentation) and their mime types.

Andrew: would you mind sharing what API surface v4 was offering until now please?

baywet avatar Jul 21 '23 19:07 baywet

@baywet In V4, the API simply used MultipartFormDataContent which is a derived instance of HttpContent. This would simply be set to the HttpRequestMessage instance by passing it as a parameter. This was however not generated by Typewriter as the metadata incorrectly sets the content as a json but added as an overload/extension to the generated sdk.

var multipartContent = new MultipartFormDataContent();
var htmlString =
  "<!DOCTYPE html>\n<html>\n  <head>\n    <title>A page with <i>rendered</i> images and an <b>attached</b> file</title>\n    <meta name=\"created\" content=\"2015-07-22T09:00:00-08:00\" />\n  </head>\n  <body>\n    <p>Here's an image from an online source:</p>\n    <img src=\"https://...\" alt=\"an image on the page\" width=\"500\" />\n    <p>Here's an image uploaded as binary data:</p>\n    <img src=\"name:imageBlock1\" alt=\"an image on the page\" width=\"300\" />\n    <p>Here's a file attachment:</p>\n    <object data-attachment=\"FileName.pdf\" data=\"name:fileBlock1\" type=\"application/pdf\" />\n  </body>\n</html>";
var presentation = new StringContent(htmlString, Encoding.UTF8, "text/html");
multipartContent.Add(presentation,"Presentation");//needs a name

await graphClient.Users["id"].OneNote.Pages.Request().AddAsync(multipartContent);

andrueastman avatar Jul 25 '23 07:07 andrueastman

Thanks for sharing the information. I've also created an issue in the conversion library so we can emit the right metadata. Also after internal discussions, we'll only implement serialization for now, not deserialization.

baywet avatar Jul 25 '23 10:07 baywet

onenote example

'/users/{user-id}/onenote/pages':
    post:
      parameters:
        - name: user-id
          in: path
          description: The unique identifier of user
          required: true
          style: simple
          schema:
            type: string
          x-ms-docs-key-type: user
      description: Create a new page in the specified section.
      tags:
        - pages.create
      operationId: create_page
      requestBody:
        content:
          'multipart/form-data':
            schema:
              properties:
                Presentation:
                  type: string
            encoding:
              Presentation:
                contentType: text/html
      responses:
        '204':
          description: No Content

baywet avatar Aug 01 '23 16:08 baywet