AGB  ·  Datenschutz  ·  Impressum  







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

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 1 von 2  1 2   
Benutzerbild von mischerr
mischerr

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

REST Umsetzung eines CURL Befehls nach Delphi

  Alt 27. Nov 2024, 09:25
Hallo.

Ich habe eine Problem dass mich schon seit 2 Tagen verfolgt.
Und zwar muss ich folgenden CURL-Befehl zur Nutzung einer API in Delphi umsetzen welcher mehrere Dateien über ein "files" array überträgt:
Code:
curl -X 'POST' \
  'https://<provider>/Documents/Outbox'
  -H 'accept: application/json'
  -H 'X-Api-Key: My-API-Key'
  -H 'Content-Type: multipart/form-data'
  -F 'files=@testFile.pdf;type=application.pdf'
  -F 'files=@testFile.xml;type=text/xml'
Der CURL-Befehl funktioniert. Die Dateien liegen vor.
Ich habe es per RESTClient wie auch Indy versucht.
Andere Befehle welche per GET JSON Daten austauschen funktionieren einwandfrei.
Jedoch beim POST erhalte ich immer nur ein http/400.

Indy:
Code:
procedure SendFilesIndy(const PdfFile, XmlFile: string);
var HTTP: TIdHTTP;
    FormData: TIdMultiPartFormDataStream;
    Response: string;
begin
  HTTP:= TIdHTTP.Create(nil);
  FormData:= TIdMultiPartFormDataStream.Create;
  try
    HTTP.Request.CustomHeaders.AddValue('Accept', 'application/json');
    HTTP.Request.CustomHeaders.AddValue('X-Api-Key', 'My-API-Key');
    HTTP.Request.CustomHeaders.AddValue('Content-Type', 'multipart/form-data');
    AttachSSLHandler(HTTP, [sslvTLSv1_2]); // Setzt IOHandler
    if PdfFile<>'' then
      FormData.AddFile('files', PdfFile, GetMIMETypeFromFile(PdfFile));
//    FormData.AddFormField('files', ExtractFileName(PdfFile), '', GetMIMETypeFromFile(PdfFile), PdfFile);
    if XmlFile<>'' then
      FormData.AddFile('files', XmlFile, GetMIMETypeFromFile(XmlFile));
//    FormData.AddFormField('files', ExtractFileName(XmlFile), '', GetMIMETypeFromFile(XmlFile), XmlFile);
    Response:= HTTP.Post('https://<provider>/Documents/Outbox', FormData);
    // ...
  except
    on E: EIdHTTPProtocolException do begin
      OutputDebugStr(pchar('HTTP-Fehler: '+E.ErrorMessage+#13#10+'Antwort des Servers: '+E.Message));
    end;
    on E: Exception do
      OutputDebugStr(pchar('Allgemeiner Fehler: '+E.Message));
  end;
  FormData.Free;
  HTTP.Free;
end;
RESTClient:
Code:
procedure SendFilesREST(const PdfFile, XmlFile: string);
var RESTClient:= TRESTClient;
    RESTRequest:= TRESTRequest;
    RESTResponse:= TRESTResponse;
    MultipartData: TMultipartFormData;
begin
  RESTClient:= TRESTClient.Create(nil);
  RESTRequest:= TRESTRequest.Create(nil);
  RESTResponse:= TRESTResponse.Create(nil);
  RESTRequest.Client:= RESTClient;
  RESTRequest.Response:= RESTResponse;
  RESTRequest.Method:= rmPost;
  RESTRequest.ConnectTimeout:= -1;
  RESTRequest.ReadTimeout:= -1
  RESTRequest.Params.AddHeader('Accept', 'application/json');
  RESTRequest.Params.AddHeader('X-Api-Key', 'My-API-Key');
  RESTRequest.Params.AddHeader('Content-Type', 'multipart/form-data');
  MultipartData:= TMultipartFormData.Create;
  try
    FRESTClient.BaseURL:= 'https://<provider>/Documents/Outbox';
    FRESTRequest.Method:= rmPOST;
    if PdfFile<>'' then
      MultipartData.AddFile('files', PdfFile, GetMIMETypeFromFile(PdfFile));
    if XmlFile<>'' then
      MultipartData.AddFile('files', XmlFile, GetMIMETypeFromFile(XmlFile));
    FRESTRequest.AddBody(MultipartData);
    FRESTRequest.Execute;
    if FRESTResponse.Status.Success then begin
      // ...
    end;
  finally
    RESTClient.Free;
    RESTRequest.Free;
    RESTResponse.Free;
    MultipartData.Free;
  end;
end;
Hat evtl. jemand sowas schon mal erfolgreich versucht und einen Tip für mich?
  Mit Zitat antworten Zitat
Rollo62

Registriert seit: 15. Mär 2007
4.101 Beiträge
 
Delphi 12 Athens
 
#2

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 27. Nov 2024, 10:19
Schon mit RestDebugger versucht, der kann nach korrekter Verbindung direkt die voreingestellten Komponenten erzeugen.
https://www.youtube.com/watch?v=RoBd68tMi7s
  Mit Zitat antworten Zitat
Benutzerbild von gubbe
gubbe

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

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 27. Nov 2024, 10:50
Schau Dir den Code mit dem Restclient nochmal genauer an.

Du erstellt zwar in der Funktion einen lokalen RESTClient und einen RESTRequest, referenziert dann aber globale FRESTClient und FRESTRequest.

Das kann nicht klappen.
  Mit Zitat antworten Zitat
Bbommel
Online

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

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 27. Nov 2024, 11:56
Falls das, was gubbe geschrieben hat, nur ein Tippfehler hier im Forum war, hab ich mal in meinem Code gewühlt, weil ich so grob etwas ähnliches schon gebastelt hatte. Bei mir musste ein JSON gefolgt von einem PDF gesendet werden. Ich habe das damals so gelöst (erzeugen der ganzen REST-Objekte passierte außerhalb der hier gezeigten Funktion, hier wird nur der RESTRequest zusammengesetzt, aber sollte für den Kern reichen):

Delphi-Quellcode:
function SendInvoiceAndPDFToCRM (aRestReq: TRESTRequest; aInvoice: TecsDataCustomerInvoice; aExcelWriter: TinvoiceExcelWriter; var oErrorMsg: string): boolean;
begin
  Result:=true;
  aRestReq.Body.Add(TJson.ObjectToJsonObject(aInvoice),ooREST);
  if (aExcelWriter.PDFName<>'') and FileExists(aExcelWriter.PDFName) then
    // wir müssen die Datei hier mit Params.AddItem hinzufügen, sonst können wir sie nicht als pkFILE
    // kennzeichnen und wenn wir das nicht machen, dann rafft Delphi die Sache mit den Multiparts nicht
    // (Bug in REST.Client -> TCustomRESTRequest.ContentType seit Delphi 11.2)
    aRestReq.Params.AddItem('invoiceFile',TFileStream.Create(aExcelWriter.PDFName,fmOpenRead),pkFILE,[poDoNotEncode],ctAPPLICATION_PDF,ooREST);
  aRestReq.Method:=rmPOST;
  aRestReq.Resource:='/updateInvoice';
  try
    aRestReq.Execute;
  except
    on e: Exception do begin
      oErrorMsg:='Fehler beim Senden einer neuen Rechnung. '+e.Message;
      Result:=false;
    end;
  end;
  [...]
end;
So hat es problemlos funktioniert. Wichtig ist dabei sicherlich mein Kommentar. Ich muss allerdings auch sagen, dass ich damals bei meinen Versuchen nicht über "TMultipartFormData" gestolpert bin. Lese ich hier gerade zum ersten Mal, man lernt ja nie aus. Vielleicht wäre das auch in meinem Fall damit irgendwie besser/eleganter gegangen, aber das oben ist jedenfalls eine (für mich) funktionierende Lösung.
  Mit Zitat antworten Zitat
Benutzerbild von mischerr
mischerr

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

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 27. Nov 2024, 12:48
@Rollo62
Ja, ich kenne den REST-Debugger. Aber leider finde ich keine Infos dazu, wie ich die Dateien dort anhängen kann.

@gubbe
Ja, hast Recht - hatte ich übersehen beim Erstellen des Codes hier in Forum.
Normalerweise sind das Elemente der Klasse.

@Bbommel
Danke für den Code. Leider bekomme ich aber noch immer ein http/400.
Da "AddItem" nicht mehrere gleichlautenden Parameter zulässt, habe ich das zum Test "MultipartData" entfernt und wie folgt geändert
Code:
if PdfFile<>'' then begin
  RESTParam:= RESTRequest.Params.AddItem;
  RESTParam.Name:= 'files';
  RESTParam.SetStream(TFileStream.Create(PdfFile, fmOpenRead), ooREST);
  RESTParam.Kind:= pkFILE;
  RESTParam.Options:= [poDoNotEncode];
  RESTParam.ContentType:= GetMIMETypeFromFile(PdfFile);
end;
Selbes für die 2te Datei.

Der Anbieter der API schreibt das die Dateien als "files" und als "array" zu übertragen sind - ist das damit gegeben?
Ober übersehe ich eine Methode um mehrere Dateien mit identischem "Schlüssel" zu übertragen?
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

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

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 27. Nov 2024, 14:16
Der Anbieter der API schreibt das die Dateien als "files" und als "array" zu übertragen sind
Am besten fragst du mal nach einem Beispiel.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von gubbe
gubbe

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

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 27. Nov 2024, 14:27
Ich habe meinen Code mal durchsucht nach einem RestRequest mit Dateiübertragung.
Dabei habe ich auch einfach "Request.addfile" verwendet, aber das war nur eine Datei.
Das Problem ist vielleicht wirklich der Dateiname "files". Hast Du es schon einmal mit Varianten probiert wie files[0] und files[1]?

Ansonsten, wenn es mit Curl funktioniert, würde ich mir mal die HTTP-Kommunikation in einem "Debugging Proxy" anschauen. z.B. mit mitmproxy, fiddler oder Charles.
Dann kannst Du vergleichen und gezielt die Unterschiede ausbügeln.
  Mit Zitat antworten Zitat
Benutzerbild von gubbe
gubbe

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

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 27. Nov 2024, 14:37
Wenn Du bei Curl noch "--trace -" bzw. "--trace-ascii -" dranhängst, siehst Du auch, wie es versendet wird. Vielleicht gibt das schon einen Hinweis darauf, wie es aussehen sollte.

Probiere auch mal "files[]" als Dateiname.

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

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

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 27. Nov 2024, 15:01
Also wenn ich unter Postman den Request simuliere (funktioniert) und anschließend eine "Mock collection" starte sehe ich, dass mein von Delphi an die Mock Adresse gesendeter Request für den Content-Typ keine "boundary" enthält.

postman_reqbody.jpg

postman_workspace.jpg

Hätte nicht gedacht dass sdas so kompliziert wird.
Werde wohl versuchen vom Anbieter ein Beispiel zu bekommen.

BTW: Es müssen keine 2 Dateien versendet werden - eine reicht auch. Dennoch immer nur 404...
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

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

AW: REST Umsetzung eines CURL Befehls nach Delphi

  Alt 27. Nov 2024, 15:20
Ich empfehle auch die Bordmittel (RESTRequest.AddFile) zu verwenden. Dabei werden sowohl der ContentType als auch die Boundary entsprechend gesetzt. Eine dementsprechend korrigierte Version deines Codes könnte dann so aussehen:
Delphi-Quellcode:
procedure SendFilesREST(const PdfFile, XmlFile: string);
var RESTClient: TRESTClient;
    RESTRequest: TRESTRequest;
    RESTResponse: TRESTResponse;
begin
  RESTRequest:= TRESTRequest.Create(nil);
  try
    RESTClient:= TRESTClient.Create(RESTRequest);
    RESTResponse:= TRESTResponse.Create(RESTRequest);
    RESTRequest.Client:= RESTClient;
    RESTRequest.Response:= RESTResponse;
    RESTRequest.Method:= rmPost;
    RESTRequest.ConnectTimeout:= -1;
    RESTRequest.ReadTimeout:= -1;
    RESTRequest.Accept := TRESTContentType.ctAPPLICATION_JSON;
    RESTRequest.AddAuthParameter('X-Api-Key', 'My-API-Key', TRESTRequestParameterKind.pkHTTPHEADER);
    RESTClient.BaseURL:= 'https://<provider>/Documents/Outbox';
    RESTRequest.Method:= rmPOST;
    if PdfFile<>'then
      RESTRequest.AddFile('files', PdfFile, TRESTContentType.ctAPPLICATION_PDF);
    if XmlFile<>'then
      RESTRequest.AddFile('files', XmlFile, TRESTContentType.ctTEXT_XML);
    RESTRequest.Execute;
    if RESTResponse.Status.Success then begin
      // ...
    end;
  finally
    RESTRequest.Free;
  end;
end;
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

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 15:38 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