Closed
Description
openedon Oct 1, 2024
What are you generating using Kiota, clients or plugins?
API Client/SDK
In what context or format are you using Kiota?
Windows executable
Client library/SDK language
Csharp
.net8
Describe the bug
I am facing an issue while generating a C# client for my FastAPI application using Kiota. The API has an endpoint for file uploads, but the generated client does not utilize multipart/form-data
, which is required for this operation.
The generated Client's method:
public async Task<UntypedNode?> PostAsync(Body_upload_file_uploadfile__post body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable restore
#else
public async Task<UntypedNode> PostAsync(Body_upload_file_uploadfile__post body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
_ = body ?? throw new ArgumentNullException(nameof(body));
var requestInfo = ToPostRequestInformation(body, requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "422", HTTPValidationError.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendAsync<UntypedNode>(requestInfo, UntypedNode.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Upload File
/// </summary>
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="body">The request body</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToPostRequestInformation(Body_upload_file_uploadfile__post body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToPostRequestInformation(Body_upload_file_uploadfile__post body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
_ = body ?? throw new ArgumentNullException(nameof(body));
var requestInfo = new RequestInformation(Method.POST, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "application/json");
requestInfo.SetContentFromParsable(RequestAdapter, "multipart/form-data", body);
return requestInfo;
}
Generated model Body_upload_file_uploadfile__post.cs:
// <auto-generated/>
#pragma warning disable CS0618
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using System.Collections.Generic;
using System;
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.18.0")]
#pragma warning disable CS1591
public partial class Body_upload_file_uploadfile__post : IAdditionalDataHolder, IParsable
#pragma warning restore CS1591
{
/// <summary>Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.</summary>
public IDictionary<string, object> AdditionalData { get; set; }
/// <summary>The file property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public string? File { get; set; }
#nullable restore
#else
public string File { get; set; }
#endif
/// <summary>The language property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public string? Language { get; set; }
#nullable restore
#else
public string Language { get; set; }
#endif
/// <summary>
/// Instantiates a new <see cref="Body_upload_file_uploadfile__post"/> and sets the default values.
/// </summary>
public Body_upload_file_uploadfile__post()
{
AdditionalData = new Dictionary<string, object>();
Language = "En";
}
/// <summary>
/// Creates a new instance of the appropriate class based on discriminator value
/// </summary>
/// <returns>A <see cref="Body_upload_file_uploadfile__post"/></returns>
/// <param name="parseNode">The parse node to use to read the discriminator value and create the object</param>
public static Body_upload_file_uploadfile__post CreateFromDiscriminatorValue(IParseNode parseNode)
{
_ = parseNode ?? throw new ArgumentNullException(nameof(parseNode));
return new Body_upload_file_uploadfile__post();
}
/// <summary>
/// The deserialization information for the current model
/// </summary>
/// <returns>A IDictionary<string, Action<IParseNode>></returns>
public virtual IDictionary<string, Action<IParseNode>> GetFieldDeserializers()
{
return new Dictionary<string, Action<IParseNode>>
{
{ "file", n => { File = n.GetStringValue(); } },
{ "language", n => { Language = n.GetStringValue(); } },
};
}
/// <summary>
/// Serializes information the current object
/// </summary>
/// <param name="writer">Serialization writer to use to serialize this model</param>
public virtual void Serialize(ISerializationWriter writer)
{
_ = writer ?? throw new ArgumentNullException(nameof(writer));
writer.WriteStringValue("file", File);
writer.WriteStringValue("language", Language);
writer.WriteAdditionalData(AdditionalData);
}
}
#pragma warning restore CS0618
Expected behavior
The generated client should properly handle multipart/form-data for file uploads in accordance with the OpenAPI specification.
Kiota should generate a method with MultipartBody like this:
public async Task<?> PostAsync(MultipartBody body, Action<RequestBuilderPostRequestConfiguration>? requestConfiguration = default, CancellationToken cancellationToken = default)
How to reproduce
Here’s the relevant code snippet for my FastAPI application:
main.py :
from fastapi import FastAPI, File, UploadFile, Form
from fastapi.responses import HTMLResponse
app = FastAPI()
app.openapi_version = "3.0.2"
@app.get("/", response_class=HTMLResponse)
async def main():
content = """
<form action="/uploadfile/" enctype="multipart/form-data" method="post">
<input name="file" type="file">
<button type="submit">Upload</button>
</form>
"""
return HTMLResponse(content=content)
@app.post("/uploadfile/")
async def upload_file(
file: UploadFile = File(...),
language: str = Form(default="En")
):
return {"filename": file.filename}
requirements.txt
fastapi==0.112.4
python-multipart==0.0.12
uvicorn==0.30.6
Open API description file
{
"openapi": "3.0.2",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/": {
"get": {
"summary": "Main",
"operationId": "main__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"text/html": {
"schema": {
"type": "string"
}
}
}
}
}
}
},
"/uploadfile/": {
"post": {
"summary": "Upload File",
"operationId": "upload_file_uploadfile__post",
"requestBody": {
"content": {
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/Body_upload_file_uploadfile__post"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Body_upload_file_uploadfile__post": {
"properties": {
"file": {
"type": "string",
"format": "binary",
"title": "File"
},
"language": {
"type": "string",
"title": "Language",
"default": "En"
}
},
"type": "object",
"required": [
"file"
],
"title": "Body_upload_file_uploadfile__post"
},
"HTTPValidationError": {
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
},
"ValidationError": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
}
}
}
}
Kiota Version
1.18.0+5c6b5d0ef23865ba2f9d9f0b9fe4b944cf26b1ec
Latest Kiota version known to work for scenario above?(Not required)
No response
Known Workarounds
No response
Configuration
No response
Debug output
Click to expand log
```</details>
### Other information
_No response_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Metadata
Assignees
Type
Projects
Status
Done ✔️