AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Netzwerke REST Umsetzung eines CURL Befehls nach Delphi
Thema durchsuchen
Ansicht
Themen-Optionen

REST Umsetzung eines CURL Befehls nach Delphi

Ein Thema von mischerr · begonnen am 27. Nov 2024 · letzter Beitrag vom 28. Nov 2024
Antwort Antwort
Seite 2 von 2     12   
Benutzerbild von gubbe
gubbe

Registriert seit: 8. Okt 2005
Ort: Schleswig-Holstein
137 Beiträge
 
Delphi 11 Alexandria
 
#11

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 27. Nov 2024, 15:27
Ich fürchte trotzdem, dass zwei Parameter mit gleichem Namen (files) so nicht funktionieren werden, weil dann die zweite Datei die erste überschreibt. Wenn man sich TRESTRequestParameterList.AddItem anschaut, wird erst nach einem Parameter mit gleichen Namen gesucht und dann ggf. ersetzt.
Aber das scheint ja noch gar nicht das Problem zu sein, wenn man auch nur eine Datei übertragen könnte.

Dass Delphi im Content-Type das Boundary nicht vorher definiert, sehe ich auch. Gibt denn der Server ausser dem HTTP-Code 400 noch etwas anderes zurück, das bestätigen könnte, dass es an der Boundary-Definiton liegt?
  Mit Zitat antworten Zitat
Benutzerbild von gubbe
gubbe

Registriert seit: 8. Okt 2005
Ort: Schleswig-Holstein
137 Beiträge
 
Delphi 11 Alexandria
 
#12

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 27. Nov 2024, 15:36
Ah, jetzt sehe ich das Problem:

Setze den Content-Type Header nicht selbst, dann ist auch das Boundary enthalten.
Ausserdem würde ich auch den "Accept-Header" so setzen:

  RestRequest.Accept := 'application/json'; Und nicht wie vorher mit RESTRequest.Params.AddHeader('Accept', 'application/json');
Sonst wird der Slash evtl. falsch kodiert und kommt falsch an.

Edit: Uwe hat das ja letztlich schon alles so (und besser) geschrieben in seinem korrigierten Code. Wirkt auch gleich aufgeräumter, wenn man die Konstanten von TRESTContentType verwendet.

Geändert von gubbe (27. Nov 2024 um 15:56 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von mischerr
mischerr

Registriert seit: 6. Feb 2004
Ort: Konz
243 Beiträge
 
Delphi 12 Athens
 
#13

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 28. Nov 2024, 08:56
@Uwe, @gubbe
Vielen Dank für die Hinweise. Der von Uwe geänderte Code funktioniert für eine Datei.

Das Problem ist jedoch, dass AddFile intern AddItem verwendet, welches auf den Namen prüft und eine doppelte Verwendung nicht zulässt.
Mein Ansatz über:
Code:
RESTParam:= FRESTRequest.Params.AddItem;
RESTParam.Name:= 'files';
RESTParam.Value:= ExtractFileName(PdfFile);
RESTParam.SetStream(TFileStream.Create(PdfFile, fmOpenRead), TRESTObjectOwnership.ooREST);
RESTParam.Kind:= TRESTRequestParameterKind.pkFILE;
RESTParam.Options:= [TRESTRequestParameterOption.poDoNotEncode];
RESTParam.ContentType:= GetMIMETypeFromFile(PdfFile);
schlägt jedoch auch fehl, da in innerhalb von RESTRequest.Execute nochmals die Parameterliste neu aufgebaut und doppelten Einträge aussortiert werden - keine Ahnung wieso man das macht und nicht dem Programmierer überlässt.

Mein letzter Versuch war noch über MultipartData.AddFile zu gehen, aber entweder bin ich zu doof, irgendwas stimmt mit folgender Zuweisung nicht.
Code:
FRESTRequest.AddBody(MultipartData.Stream, MultipartData.MimeTypeHeader, TRESTObjectOwnership.ooREST);
Wird auch ablehnt. Wie auch immer...
Ich verwende nun folgenden Code:
Code:
procedure SendFilesIndy2(const PdfFile, XmlFile: string);
var HTTP: TIdHTTP;
    FormData: TIdMultiPartFormDataStream;
    url, Response: string;
begin
  HTTP:= TIdHTTP.Create(nil);
  FormData:= TIdMultiPartFormDataStream.Create;
  try
    HTTP.Request.Accept:= 'application/json';
    HTTP.Request.ContentType:= 'multipart/form-data';
    HTTP.Request.CustomHeaders.AddValue('X-Api-Key', MY_API_KEY);
    AttachSSLHandler(HTTP, [sslvTLSv1_2]);
    if PdfFile<>'' then
      FormData.AddFile('files', PdfFile, GetMIMETypeFromFile(PdfFile));
    if XmlFile<>'' then
      FormData.AddFile('files', XmlFile, GetMIMETypeFromFile(XmlFile));
    url:= 'MyURL';
    Response:= HTTP.Post(url, FormData);
    // ...
  except
//    on E: EIdHTTPProtocolException do
  end;
  FormData.Free;
  HTTP.Free;
end;
Zumindest zeigt Postman eine plausible Content-Length an und wie API akzeptiert auch den Request.
Muss nur noch mit dem Anbieter klären, ob die Dateien auch korrekt empfangen wurden - im Moment gehe ich aber davon aus.

Jetzt stehe ich aber von einem Gewissensproblem:
Bleibe ich bei den übrigen API-Aufrufen beider Kombi aus RESTClient/Request/Response oder stelle ich komplett auf Indy um?
  Mit Zitat antworten Zitat
Bbommel

Registriert seit: 27. Jun 2007
Ort: Köln
659 Beiträge
 
Delphi 12 Athens
 
#14

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 28. Nov 2024, 09:25
Ich würde mir echt überlegen, ob es eine gute Idee ist, auf Indy zu gehen. Du müsstest die OpenSSL-Bibliotheken zusammen mit deinem Produkt ausliefern und selbst auf die Aktualisierung achten und zumindest in dem standardmäßig mitgelieferten Indy kann man aktuelle OpenSSL-Versionen nicht mit einbauen. Mir wäre das zu kritisch.

Du könntest alternativ die REST-Unit aus der Delphi-RTL in dein Programmverzeichnis legen und dort eine angepasste Version erstellen, welche es zulässt, dass es mehrere Parts mit der gleichen Bezeichnung gibt. Ist auch nicht richtig schön, aber in einen saueren Apfel muss man wohl beißen.

Ob es "richtig", also standardkonform ist, was Delphi macht oder was der Anbieter deiner API macht, weiß ich gar nicht. Da müsste man sich durch irgendwelche RFC-Dokumente wühlen. Wenn aber mehrere Parts mit gleichem Namen zulässig sind, sollte man mal einen Bugreport dafür aufmachen.

Das nur mal als abschließende Gedanken dazu...
  Mit Zitat antworten Zitat
Benutzerbild von gubbe
gubbe

Registriert seit: 8. Okt 2005
Ort: Schleswig-Holstein
137 Beiträge
 
Delphi 11 Alexandria
 
#15

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 28. Nov 2024, 09:25
Schau Dir mal die procedure TCustomRESTRequest.DoPrepareRequestBody an.
Die ist virtuell und damit könntest Du sie in einer abgeleiteten Klasse überschreiben.

Ein Ansatz könnte sein, die Dateien vorher unter verschiedenen Namen hinzuzufügen und hier einmal das Array der TRESTRequestParameter durchzugehen und die Namen nochmal zu ändern, um dann mit inherited die Funktion der Basis-Klasse aufzurufen.
  Mit Zitat antworten Zitat
Benutzerbild von mischerr
mischerr

Registriert seit: 6. Feb 2004
Ort: Konz
243 Beiträge
 
Delphi 12 Athens
 
#16

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 28. Nov 2024, 10:15
@Bbommel
Da hast du Recht. Wäre aber kein Problem da alles eh nur auf 1 Installation und nur inhouse läuft.

@gubbe
Das war eine super Idee!

Ich setze die Parameter nun wie folgt:
Code:
if PdfFile<>'' then begin
  RESTParam:= FRESTRequest.Params.AddItem;
  RESTParam.Name:= 'files[0]';
  RESTParam.Value:= ExtractFileName(PdfFile);
  RESTParam.SetStream(TFileStream.Create(PdfFile, fmOpenRead), TRESTObjectOwnership.ooREST);
  RESTParam.Kind:= TRESTRequestParameterKind.pkFILE;
  RESTParam.Options:= [TRESTRequestParameterOption.poDoNotEncode];
  RESTParam.ContentType:= GetMIMETypeFromFile(PdfFile);
end;
if XmlFile<>'' then begin
  RESTParam:= FRESTRequest.Params.AddItem;
  RESTParam.Name:= 'files[1]';
  RESTParam.Value:= ExtractFileName(XmlFile);
  RESTParam.SetStream(TFileStream.Create(XmlFile, fmOpenRead), TRESTObjectOwnership.ooREST);
  RESTParam.Kind:= TRESTRequestParameterKind.pkFILE;
  RESTParam.Options:= [TRESTRequestParameterOption.poDoNotEncode];
  RESTParam.ContentType:= GetMIMETypeFromFile(XmlFile);
end;
...und überschreibe die Funktion wie folgt...
Code:
procedure TMyAPi.TMyRESTRequest.DoPrepareRequestBody(
  AParamList: TRESTRequestParameterArray; AContentType: TRESTContentType;
  var ABodyStream: TStream; var ABodyStreamOwner: Boolean);
var P: TRESTRequestParameter;
begin
  for P in AParamList do
    if StartsText('files[', P.Name) then
      P.Name:= 'files';
  inherited;
end;
Die API akzeptiert und Postman zeigt als Content-Length ca. die Summe beider Dateien!
Bleibt zu hoffen dass der Anbieter das auch so sieht.
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe
Online

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#17

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 28. Nov 2024, 11:25
Schau Dir mal die procedure TCustomRESTRequest.DoPrepareRequestBody an.
Die ist virtuell und damit könntest Du sie in einer abgeleiteten Klasse überschreiben.

Ein Ansatz könnte sein, die Dateien vorher unter verschiedenen Namen hinzuzufügen und hier einmal das Array der TRESTRequestParameter durchzugehen und die Namen nochmal zu ändern, um dann mit inherited die Funktion der Basis-Klasse aufzurufen.
Das ist in der Tat eine gute Idee!

Es gibt übrigens einen Report für dieses Problem (RSP-23724 - RestClient does not allow for multiple params with the same name), aber der ist geschlossen worden weil der Ersteller nicht auf eine Rückfrage reagiert hat. Das verstehe ich aber nicht ganz, da die Steps ausreichend sind, das Problem zu reproduzieren. Ich habe allerdings selbst schon oft die Erfahrung gemacht, dass ein angehängtes Projekt, mit dem man den Fehler durch simples Compilieren und Starten schnell reproduzieren und auch einen Fix verifizieren kann, bessere Aussichten auf eine Lösung bringt. Vielleicht schaffe ich es ja zeitnah einen entsprechenden Report mit einem Minimalprojekt einzustellen.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 2     12   

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:37 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz