![]() |
MultipartFormData und TRESTRequest
So, bin nach langer Zeit (3 Jahre+) mal wieder an einer ernsthaften Entwicklung in Delphi dran: eine Schnittstelle (extern), die bislang über Soap angesprochen wurde, hat den Support für Soap nun endgültig aufgekündigt. Ab jetzt ist die Rest-Api zu verwenden.
Es muss also ein Rest-Api-Client her. Soweit so gut. Die Rest-Api erwartet an einer Stelle ein multipart-formdata body. Die einzelnen Parts können wiederum die verschiedensten Content Types sein. Ein application/json ist immer dabei und dann diverse Dateien, also application/pdf, image/png, text/plain, whatever. Und genau da stehe ich auf dem Schlauch, wie ich das in Delphi hinbekomme. Ich nutze die unter REST.Client bereit gestellten Klassen, dh. TRESTClient, TRESTRequest und TRESTResponse. Hier komme ich für einen Multipart-Body aber nicht wirklich weiter. Zwar lässt sich mit FRestRequest.AddParameter('myName', 'myValue', TRESTRequestParameterKind.pkREQUESTBODY) ein Multipart-Body erzeugen, der jeweilige content type lässt sich so aber nicht festlegen. Der content type ist aber ziemlich wichtig :P Nach etwas Recherche bin ich auf System.Net.Mime.TMultipartFormData gestoßen. Die Klasse bietet ziemlich sinnvoll aussehende Methoden. Zb. TMultipartFormData.AddFile, wo ich einfach nur den Datei-Pfad angeben brauche und sich damit vielleicht auch meine nächste Frage, wie ich denn in Delphi aus einer Datei den passenden Octet-Stream erzeuge, erübrigt ;) Nur sehe ich gar nicht, wie TMultipartFormData mit den REST.Client Klassen zusammenspielt. Da dachte ich mir, vielleicht haben ja die Delphi-Experten den ein oder anderen Tipp für mich :-D Derzeit ist Delphi 10.2 im Einsatz. |
AW: MultipartFormData und TRESTRequest
|
AW: MultipartFormData und TRESTRequest
Wenn du mindestens ein File per AddFile zum Request hinzugefügt hast, wird der ContentType automatisch auf ctMULTIPART_FORM_DATA gesetzt.
Um mehrere AddBody-Calls abzusetzen, ohne dabei den vorigen Body zu überschreiben, funktioniert das nur mit diesem Overload:
Delphi-Quellcode:
procedure AddBody(ABodyContent: string; AContentType: TRESTContentType = ctNone); overload;
|
AW: MultipartFormData und TRESTRequest
Danke, das hat mich auf den richtigen Weg gebracht!
Bin noch über diverse weitere Schwierigkeiten gestolpert, aber dafür mache ich vielleicht besser eigene Threads auf :) |
AW: MultipartFormData und TRESTRequest
Ok, bin doch noch nicht durch mit dem Thema :(
Ich bekomme es einfach nicht hin, die nicht-file Teile des Bodies sowohl mit einem von mir gewählten Namen UND dem von mir gewählten Content Type hinzuzufügen. Und dabei nutze ich schon TRESTRequestParameterList.AddItem, bei dem ich beides tatsächlich angeben kann. Der angegebene Name wird berücksichtigt, der Content Type aber warum auch immer trotzdem auf text/plain gesetzt (anstatt des angegebenen application/json). Langsam gehen mir die Ideen aus :( |
AW: MultipartFormData und TRESTRequest
In dem Fall solltest du mal ein Beispiel geben, wie der Request genau aussehen soll, was du aktuell raus bekommst und wie du das gemacht hast.
|
AW: MultipartFormData und TRESTRequest
Ok :-)
Soll:
Code:
POST /api/derEndpunkt?QueryParameter=1234 HTTP/1.1
Connection: Keep-Alive Content-Type: multipart/form-data; boundary=--------102722082331395 Accept: application/json, application/*+json Accept-Charset: UTF-8, *;q=0.8 Authorization: Basic cm9vdDpvcHRpbWFs Cookie: JSESSIONID=CACA72D331F84414ADF4E7C8CFFE02C2 User-Agent: Embarcadero RESTClient/1.0 Content-Length: 9121 Host: zielhost ----------102722082331395 Content-Disposition: form-data; name="nameJson" Content-Type: application/json Content-Transfer-Encoding: quoted-printable {"key":"value"} ----------102722082331395 Content-Disposition: form-data; name="nameFile"; filename="Test_Bild.png" Content-Type: image/png Content-Transfer-Encoding: binary [das serialisierte Bild] ----------102722082331395-- Was ich mache:
Delphi-Quellcode:
Varianten 1, 2 und 3 führen allen zum gleichen Ergebnis:
procedure PostObject;
begin FRestRequest.Params.Clear; FRestRequest.ClearBody; FRestRequest.Response.ResetToDefaults; FRestRequest.Method := rmPOST; FRestRequest.Resource := '/api/derEndpunkt?QueryParameter=1234'; //Variante 1 //FRestRequest.AddParameter('nameJson', '{"key":"value"}', false); //Variante 2 //FRestRequest.AddParameter('nameJson', '{"key":"value"}', TRESTRequestParameterKind.pkREQUESTBODY); //FRestRequest.Params.ParameterByName('nameJson').ContentType := TRESTContentType.ctAPPLICATION_JSON; //Variante 3 FRestRequest.Params.AddItem('nameJson', '{"key":"value"}', TRESTRequestParameterKind.pkREQUESTBODY, [], TRESTContentType.ctAPPLICATION_JSON); //Variante 4 //FRestRequest.Body.Add('{"key":"value"}',TRESTContentType.ctAPPLICATION_JSON); FRestRequest.AddFile('nameFile', 'PfadZurPng', TRESTContentType.ctIMAGE_PNG); FRestRequest.Accept := 'application/json, application/*+json'; FRestRequest.Execute; end;
Code:
Der Content-Type des Json Parts des Request ist immer text/plain.
POST /api/derEndpunkt?QueryParameter=1234 HTTP/1.1
Connection: Keep-Alive Content-Type: multipart/form-data; boundary=--------102722082331395 Accept: application/json, application/*+json Accept-Charset: UTF-8, *;q=0.8 Authorization: Basic cm9vdDpvcHRpbWFs Cookie: JSESSIONID=CACA72D331F84414ADF4E7C8CFFE02C2 User-Agent: Embarcadero RESTClient/1.0 Content-Length: 9121 Host: zielhost ----------102722082331395 Content-Disposition: form-data; name="nameJson" Content-Type: text/plain Content-Transfer-Encoding: quoted-printable {"key":"value"} ----------102722082331395 Content-Disposition: form-data; name="nameFile"; filename="Test_Bild.png" Content-Type: image/png Content-Transfer-Encoding: binary [das serialisierte Bild] ----------102722082331395-- Mit Variante 4 wird der Content Type zwar richtig auf application/json gesetzt, der Name ist aber generiert. Klar, habe ja auch keinen explizit angegeben. |
AW: MultipartFormData und TRESTRequest
Debug doch mal mit Debug-DCUs aktiviert rein?
Du müsstest bei beiden Varianten 3 und 4 in einer Überladung von TRESTRequestParameterList.AddItem enden. Hier kannst du doch feststellen, wer oder was dir den ContentType überschreibt. Wenn es da noch okay ist, sollte TCustomRESTRequest.Execute das nächste Debug-Ziel sein. Da gibt es im oberen Drittel irgendwann den Aufruf von TCustomRESTRequest.ContentType(). Hier würde ich debuggen. Ältere Delphi-Versionen haben da vielleicht noch nicht alle Spezialitäten drin bzw. unterstützen so Multipart-Sachen noch nicht so gut. |
AW: MultipartFormData und TRESTRequest
Das scheint ein Problem mit Delphi 10.2 zu sein. Der verantwortliche Code liegt in in TCustomRESTRequest.DoPrepareRequestBody Zeile 2742
Delphi-Quellcode:
In Delphi 11 sieht die Sequenz dann so aus
if LContentType = TRESTContentType.ctMULTIPART_FORM_DATA then
begin // Multipart // For multipart names of body parameters are written - in contrast to WWWForm (see below) if (LParam.Kind = TRESTRequestParameterKind.pkFile) then LMultipartPeerStream.AddFormFile(LParam.Name, LParam.Value, ContentTypeToString(LParam.ContentType)) else LMultipartPeerStream.AddFormField(LParam.Name, LParam.Value); end
Delphi-Quellcode:
Im Gegensatz zu 10.2 wird hier der ContentType des Parameters mit übergeben.
if SameText(AContentType, TRESTContentType.ctMULTIPART_FORM_DATA) then
begin // Multipart // For multipart names of body parameters are written - in contrast to WWWForm (see below) if LParam.Stream <> nil then LMultipartFormData.AddStream(LParam.Name, LParam.Stream, LParam.Value, ContentTypeToString(LParam.ContentType)) else if LParam.Kind = TRESTRequestParameterKind.pkFile then LMultipartFormData.AddFile(LParam.Name, LParam.Value, ContentTypeToString(LParam.ContentType)) else LMultipartFormData.AddField(LParam.Name, LParam.Value, ContentTypeToString(LParam.ContentType)); end |
AW: MultipartFormData und TRESTRequest
Ja, habe das jetzt auch so im Code nachvollzogen.
Danke fürs Nachschauen, Uwe! So, als Workarround hätte ich nun die Option, den Json-Teil des Bodys als Datei zu speichern und dann mit AddFile hinzufügen... Ich habe geschaut, ob ich das irgendwie austricksen kann, aber sobald LParam.Kind = TRESTRequestParameterKind.pkFile ist, wird der Wert des Parameters als Dateipfad interpretiert. Mein "Json als Datei abspeichern" Umweg scheint die einzige Möglichkeit zu sein, die mir in Tokyo bleibt :freak: Mal sehen, ob das klappt. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:42 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz