klee-contrib / topmodel

Outil de modélisation et générateurs pour divers langages
https://klee-contrib.github.io/topmodel/
MIT License
11 stars 12 forks source link

[API] Possibilité de gérer des requêtes multipart #320

Closed BesnardFlorian closed 1 year ago

BesnardFlorian commented 1 year ago

J'ai un besoin de requête Multipart qui contient à la fois du json et un binaire dans le body de la requête.

Je déclare donc comme params de mon endpoint :

Ça me génère une méthode java où le json est annoté @RequestBody et le champ binaire annoté @RequestParam. Or pour fonctionner, ce type de requête a plutôt besoin des 2 annotés avec @Requestpart.

J'ai donc pensé 2 évolutions possibles pour répondre à ce besoin :

"1 - Détection automatique du besoin de multipart"

Pour le JS, si on a mis un @Multipart côté back, il faudrait que le body du fetch soit un objet dont les clefs sont les noms des différentes part et les valeurs celles de la part.

"2 - Indication manuelle du besoin de multipart"

JabX commented 1 year ago

Aujourd'hui ça marche déjà plus ou moins côté C# et JS s'il y a au moins un paramètre dans le endpoint avec un domaine qui a explicitement IFormFile (C#, équivalent de de MultipartFile) ou File (JS) (ce domaine n'est pas une composition et donc ne trigger par l'erreur "plusieurs body params").

Par exemple, j'ai ça dans une de mes applis :

  params:
    - composition: DocumentAdd
      name: document
      comment: Document.
    - name: file
      domain: DO_FILE
      comment: Contenu du fichier.

C#

// [FromForm] c'est équivalent à `@RequestPart` et j'ai pas besoin de mettre l'annotation sur le IFormFile parce que c'est par défaut pour le type)
public int AddDocument([FromForm] DocumentAdd document, IFormFile file)

JS

export function addDocument(document: DocumentAdd, file: File, options: RequestInit = {}): Promise<number> {
    const body = new FormData();
    fillFormData( // fonction générée par topmodel en bas de fichier qui remplit correctement le FormData
        {
            ...document,
            file
        },
        body
    );
    return fetch("POST", `./api/document`, {body}, options);
}

Pour moi le problème c'est juste que le générateur Spring n'implémente pas cette logique correctement...

Je suis OK par contre pour dire qu'on pourrait ajouter un attribut multipartFormData sur le domaine qui force l'utilisation d'un multipart/form-data pour tous les endpoints qui utilisent au moins un paramètre de ce domaine, ce qui :

Yachef commented 1 year ago

Voici un snippet présentant un code pouvant être généré par TopModel dans l'univers JS / Java. Dans cet exemple il est question de créer une Commune qui contiendrait, en plus d'autres propriétés, plusieurs fichiers "chartes", et un fichier "demography".

commune.service.ts

  createCommune(communeEtrangereCreate: CommuneEtrangereCreate): Observable<void> {
    const formData = new FormData();
    formData.set("data", new Blob([JSON.stringify({...communeEtrangereCreate, demography:null, chartes: null})], {type: "application/json"}));
    formData.set("demography", communeEtrangereCreate.demography!);
    if(communeEtrangereCreate.chartes) {
      communeEtrangereCreate.chartes.forEach((charte) => { formData.append('chartes', charte); });
    }
    return this.http.post<void>(`/communes`, formData, {headers: { "Content-Type": "multipart/form-data" }});
  }

CommuneController.java

    @PostMapping(path = "communes", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
    void createCommune(@RequestPart(value = "data", required = false) CommuneEtrangereCreate communeEtrangereCreate, @RequestPart(value = "demography", required = false) MultipartFile demography, @RequestPart(value = "chartes", required = false) MultipartFile[] chartes) {
        // DO SOMETHING
    }