AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

post Json mit REST

Ein Thema von mikel.pahl · begonnen am 22. Okt 2018 · letzter Beitrag vom 5. Nov 2018
Antwort Antwort
Seite 1 von 2  1 2   
mikel.pahl

Registriert seit: 21. Sep 2015
Ort: Karlsruhe Neureut
5 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#1

post Json mit REST

  Alt 22. Okt 2018, 18:34
Ich solle per Rest eine Json-Objekt per Post an einen Server schicken. Beispiele zum empfangen von Json-Objekten gibts viele... zum Senden nicht.

Von derem Server kommt immer zurück "invalid json primitive".

Verwundert bin ich obwohl in der Komponenten ContentType := 'application/json' ist der ContentType nach dem execute wieder "application/x-www-form-urlencoded" Das passiert auch wenn ich im Designer den Requset per Hand ausführe

Vom Support dort kommt:
Zitat:
The request body should be of content type application/json and the only body being sent should be the part between the outermost {} brackets. Example: { "ApiXRequest": ... }
Das heisst wohl, dass ich das Json-Objekt nicht als Paramter schicken darf sondern als Payload. Nur wie ??

Delphi-Quellcode:
procedure TFormRESTTest.BtnAbwertenClick(Sender: TObject);

var
  jValue, jApixResponse: TJSONValue;
  jUpdates: TJSONArray;
  hash : system.hash.THashSHA2;
  AuthToken: string;
  Stringwriter : TStringWriter;
  Writer : Tjsontextwriter;
  Builder : tjsonobjectbuilder;
  Str, Zeit,Datum, DatumZeit : string;
  Ammount : string;

begin
  memo.Clear;
  Ammount:='-'+EDBetrag.Text;
  if (AccountID<>'') and (AccountID<>'keine Karte') then
  begin
    System.SysUtils.DateTimeToString(Datum,'yyyy-mm-dd',now); // Zeit im Format yyyy-mm-ddThh-nn-ssZ
    System.SysUtils.DateTimeToString(Zeit,'hh-nn-ss',now);
    DatumZeit:=Datum+'T'+Zeit+'Z';
    hash.Reset;
    AuthToken:=hash.GetHashString(AppID+'-'+AccountInfo.AccountID+'-1-'+Ammount+'-'+DatumZeit);
    Stringwriter:=TStringWriter.Create();
    Writer:=TJsonTextWriter.Create(Stringwriter);
    Builder:=TJSONObjectBuilder.Create(Writer);
    Builder
    .BeginObject
      .BeginObject('ApiXRequest')
        .Add('Hash', AuthToken)
         .....
      .EndObject
    .EndObject;
    jValue:= TJSONObject.Create;
    jValue:= TJSONObject.ParseJSONValue(Stringwriter.ToString,true);
    Builder.Free;
    Writer.Free;
    Stringwriter.Free;
    Memo.lines.Add('-> '+jValue.ToString);
    Req_Update.Params.Clear;
    RESTWay2PAy.ContentType := 'application/json';
    Req_Update.Params.AddItem('ApiXRequest',jValue.ToString,pkGETorPOST,[poDoNotEncode]);
    Req_Update.Execute;
    if Res_Update.StatusCode = 200 then
    begin
      jValue:=Res_Update.JSONValue;
      Memo.lines.Add('<- '+ jValue.ToString);
      jApixResponse:=jValue.GetValue<TJSONObject>('ApixResponse');
      AccountInfo.AccountStatus:=jApixResponse.GetValue<Integer>('Status');
      jUpdates:=jApixResponse.GetValue<TJSONArray>('Updates');
       ......
      LBLGuthaben.Caption:=BalanceStr;
      LBLGruppe.Caption:=IntToStr(AccountInfo.DepartmentID1);
      AccountInfo.PurseBalance:=strtoint(BalanceStr);
    end
    else
    begin
      ShowMessage('Fehler :'+inttostr(Res_Update.StatusCode));
      Memo.lines.Add('<- '+ Res_Update.Content);
    end;
  end;
end;
Michael Pahl
  Mit Zitat antworten Zitat
Schokohase
(Gast)

n/a Beiträge
 
#2

AW: post Json mit REST

  Alt 22. Okt 2018, 20:39
Wenn ich mit einer REST API sprechen muss, dann erstelle ich mir dafür einen passenden Client. Dieses Zusammengebau-Gefrickel ist nicht so mein Ding.

Hier mal ein kleines Beispiel, wie so etwas aussehen kann (mit 2 Endpoints und jeweils einer POST Methode):
Delphi-Quellcode:
unit Magento.v2_3;

interface

uses
  System.Classes, System.SysUtils, System.Net.HttpClient, System.JSON,
  REST.Json, REST.Json.Types;

type
  EMagento2ClientException = class( Exception )
  end;

  TMagento2Client = class;

  TMagento2EndpointBase = class abstract
  public type
    TErrorResponse = class
    public type
      TParameterItem = class
      private
        [JsonName( 'resources' )]
        FResources: string;
        [JsonName( 'fieldName' )]
        FFieldName: string;
        [JsonName( 'fieldValue' )]
        FFieldValue: string;
      public
        property Resources: string read FResources write FResources;
        property FieldName: string read FFieldName write FFieldName;
        property FieldValue: string read FFieldValue write FFieldValue;
      end;

      TParameters = TArray<TParameterItem>;

      TErrorItem = class
      private
        [JsonName( 'message' )]
        FMessage: string;
        [JsonName( 'parameters' )]
        FParameters: TParameters;
      public
        destructor Destroy; override;
      public
        property &Message: string read FMessage write FMessage;
        property Parameters: TParameters read FParameters write FParameters;
      end;

      TErrors = TArray<TErrorItem>;
    private
      [JsonName( 'message' )]
      FMessage: string;
      [JsonName( 'errors' )]
      FErrors: TErrors;
      [JsonName( 'code' )]
      FCode: Integer;
      [JsonName( 'trace' )]
      FTrace: string;
      [JsonName( 'parameters' )]
      FParameters: TParameters;
    public
      destructor Destroy; override;
    public
      property &Message: string read FMessage write FMessage;
      property Errors: TErrors read FErrors write FErrors;
      property Code: Integer read FCode write FCode;
      property Parameters: TParameters read FParameters write FParameters;
      property Trace: string read FTrace write FTrace;
    end;

  private
    [weak]
    FClient: TMagento2Client;
  protected
    property Client: TMagento2Client read FClient;
  protected
    procedure CheckResponse( const AResponse: IHttpResponse );
    function GetUrlFromBase( const ARelativePath: string ): string;
  public
    constructor Create( AClient: TMagento2Client );
  end;

  TIntegrationAdminTokenServiceV1 = class( TMagento2EndpointBase )
  public type
    TCreateAdminAccessTokenPostBody = class
    private
      [JsonName( 'username' )]
      FUsername: string;
      [JsonName( 'password' )]
      FPassword: string;
    public
      property Username: string read FUsername write FUsername;
      property Password: string read FPassword write FPassword;
    end;

  public
    function CreateAdminAccessToken( const ABody: TCreateAdminAccessTokenPostBody ): string;
  end;

  TIntegrationCustomerTokenServiceV1 = class( TMagento2EndpointBase )
  public type
    TCreateCustomerAccessTokenPostBody = class
    private
      [JsonName( 'username' )]
      FUsername: string;
      [JsonName( 'password' )]
      FPassword: string;
    public
      property Username: string read FUsername write FUsername;
      property Password: string read FPassword write FPassword;
    end;
  public
    function CreateCustomerAccessToken( const ABody: TCreateCustomerAccessTokenPostBody ): string;
  end;

  TMagento2Client = class
  private
    FBaseUrl: string;
    FAccessToken: string;
    FHttpCLient: THttpClient;
    FIntegrationAdminTokenServiceV1: TIntegrationAdminTokenServiceV1;
    FIntegrationCustomerTokenServiceV1: TIntegrationCustomerTokenServiceV1;
    function GetHttpClient: THttpClient;
    procedure SetAccessToken( const Value: string );
    procedure SetBaseUrl( const Value: string );
    function GetIntegrationAdminTokenServiceV1: TIntegrationAdminTokenServiceV1;
    function GetIntegrationCustomerTokenServiceV1: TIntegrationCustomerTokenServiceV1;
  protected
    property HttpClient: THttpClient read GetHttpClient;
  public
    destructor Destroy; override;
  public
    property BaseUrl: string read FBaseUrl write SetBaseUrl;
    property AccessToken: string read FAccessToken write SetAccessToken;

    property IntegrationAdminTokenServiceV1: TIntegrationAdminTokenServiceV1 read GetIntegrationAdminTokenServiceV1;
    property IntegrationCustomerTokenServiceV1: TIntegrationCustomerTokenServiceV1 read GetIntegrationCustomerTokenServiceV1;
  end;

type
  TUtils = class
  protected
    class procedure FreeArrayItems<T: class>( var AObjectArray: array of T );
  end;

implementation

{ TMagento2EndpointBase.TErrorResponse }

destructor TMagento2EndpointBase.TErrorResponse.Destroy;
begin
  TUtils.FreeArrayItems<TMagento2EndpointBase.TErrorResponse.TErrorItem>( FErrors );
  TUtils.FreeArrayItems<TMagento2EndpointBase.TErrorResponse.TParameterItem>( FParameters );
  inherited;
end;

{ TUtils }

class procedure TUtils.FreeArrayItems<T>( var AObjectArray: array of T );
var
  I: Integer;
  obj: T;
begin
  for I := Low( AObjectArray ) to High( AObjectArray ) do
    begin
      obj := AObjectArray[I];
      AObjectArray[I] := nil;
      obj.Free( );
      obj := nil;
    end;
end;

{ TMagento2EndpointBase.TErrorResponse.TErrorItem }

destructor TMagento2EndpointBase.TErrorResponse.TErrorItem.Destroy;
begin
  TUtils.FreeArrayItems<TMagento2EndpointBase.TErrorResponse.TParameterItem>( FParameters );
  inherited;
end;

{ TMagento2EndpointBase }

procedure TMagento2EndpointBase.CheckResponse( const AResponse: IHttpResponse );
var
  e: TErrorResponse;
begin
  if ( AResponse.StatusCode >= 400 )
  then
    begin
      e := TJson.JsonToObject<TErrorResponse>( AResponse.ContentAsString( ) );
      raise EMagento2ClientException.Create( e.Message );
    end;
end;

constructor TMagento2EndpointBase.Create( AClient: TMagento2Client );
begin
  inherited Create;
  if not Assigned( AClient )
  then
    raise EArgumentNilException.Create( 'AClient' );

  FClient := AClient;
end;

function TMagento2EndpointBase.GetUrlFromBase( const ARelativePath: string ): string;
begin
  Result := Client.BaseUrl + ARelativePath;
end;

{ TIntegrationAdminTokenServiceV1 }

function TIntegrationAdminTokenServiceV1.CreateAdminAccessToken( const ABody: TCreateAdminAccessTokenPostBody ): string;
var
  url: string;
  body: string;
  source: TStream;
  reqs: IHttpRequest;
  resp: IHttpResponse;
  v: TJsonValue;
begin
  if not Assigned( ABody )
  then
    raise EArgumentNilException.Create( 'ABody' );

  url := GetUrlFromBase( 'rest/all/V1/integration/admin/token' );
  body := TJson.ObjectToJsonString( ABody );
  source := TStringStream.Create( body );
  try
    Client.HttpClient.Accept := 'application/json';
    Client.HttpClient.ContentType := 'application/json';

    resp := Client.HttpClient.Post( url, source );
  finally
    source.Free;
  end;

  CheckResponse( resp );

  v := TJSONObject.ParseJSONValue( resp.ContentAsString( ) );
  try
    Result := TJsonString( v ).Value;
  finally
    v.Free;
  end;
end;

{ TMagento2Client }

destructor TMagento2Client.Destroy;
begin
  FreeAndNil( FIntegrationAdminTokenServiceV1 );
  FreeAndNil( FIntegrationCustomerTokenServiceV1 );
  FreeAndNil( FHttpCLient );
  inherited;
end;

function TMagento2Client.GetHttpClient: THttpClient;
begin
  if not Assigned( FHttpCLient )
  then
    FHttpCLient := THttpClient.Create( );

  Result := FHttpCLient;
end;

function TMagento2Client.GetIntegrationAdminTokenServiceV1: TIntegrationAdminTokenServiceV1;
begin
  if not Assigned( FIntegrationAdminTokenServiceV1 )
  then
    FIntegrationAdminTokenServiceV1 := TIntegrationAdminTokenServiceV1.Create( Self );

  Result := FIntegrationAdminTokenServiceV1;
end;

function TMagento2Client.GetIntegrationCustomerTokenServiceV1: TIntegrationCustomerTokenServiceV1;
begin
  if not Assigned( FIntegrationCustomerTokenServiceV1 )
  then
    FIntegrationCustomerTokenServiceV1 := TIntegrationCustomerTokenServiceV1.Create( Self );

  Result := FIntegrationCustomerTokenServiceV1;
end;

procedure TMagento2Client.SetAccessToken( const Value: string );
begin
  FAccessToken := Value;

  if string.IsNullOrWhiteSpace( FAccessToken )
  then
    HttpClient.CustomHeaders['Authorization'] := string.Empty
  else
    HttpClient.CustomHeaders['Authorization'] := 'Bearer ' + Value;
end;

procedure TMagento2Client.SetBaseUrl( const Value: string );
begin
  FBaseUrl := Value;
end;

{ TIntegrationCustomerTokenServiceV1 }

function TIntegrationCustomerTokenServiceV1.CreateCustomerAccessToken( const ABody: TCreateCustomerAccessTokenPostBody ): string;
var
  url: string;
  body: string;
  source: TStream;
  reqs: IHttpRequest;
  resp: IHttpResponse;
  v: TJsonValue;
begin
  if not Assigned( ABody )
  then
    raise EArgumentNilException.Create( 'ABody' );

  url := GetUrlFromBase( 'rest/all/V1/integration/customer/token' );
  body := TJson.ObjectToJsonString( ABody );
  source := TStringStream.Create( body );
  try
    Client.HttpClient.Accept := 'application/json';
    Client.HttpClient.ContentType := 'application/json';

    resp := Client.HttpClient.Post( url, source );
  finally
    source.Free;
  end;

  CheckResponse( resp );

  v := TJSONObject.ParseJSONValue( resp.ContentAsString( ) );
  try
    Result := TJsonString( v ).Value;
  finally
    v.Free;
  end;
end;

end.
und die Verwendung
Delphi-Quellcode:
var
  clt: TMagento2Client;
  accessToken: string;
  body: TIntegrationAdminTokenServiceV1.TCreateAdminAccessTokenPostBody;
begin
  clt := TMagento2Client.Create;
  try
    clt.BaseUrl := 'http://localhost/magento2/';
    body := TIntegrationAdminTokenServiceV1.TCreateAdminAccessTokenPostBody.Create;
    try
      body.Username := 'admin';
      body.Password := 'secret';
      accessToken := clt.IntegrationAdminTokenServiceV1.CreateAdminAccessToken( body );
    finally
      body.Free;
    end;
    clt.accessToken := accessToken;
    Writeln( accessToken );
  finally
    clt.Free;
  end;
end;
  Mit Zitat antworten Zitat
Bbommel

Registriert seit: 27. Jun 2007
Ort: Köln
572 Beiträge
 
Delphi 10.4 Sydney
 
#3

AW: post Json mit REST

  Alt 23. Okt 2018, 08:21
Hui, den ganzen JSON-Request in den Parameter zu packen, ist aber auch mutig.

Hier mal ein kleiner Ausschnitt, wie man es machen könnte und die Anfrage mittels eines TRESTRequest loswerden kann:

Delphi-Quellcode:

[...]
type
  TmyClass = class
    [...]
    RestRequest: TRESTRequest;
    procedure SendData (...);
  end;

[...]

procedure TmyClass.SendData (...);

var
  jWriter: TJsonTextWriter;

begin
  jWriter:=TJsonTextWriter.Create(TStringWriter.Create);
  try
    jWriter.WriteStartObject;

    // hier nur ein einfaches Objekt mit zwei Feld/Wert-Paaren
    jWriter.WritePropertyName('headFields');
    jWriter.WriteValue('wuppdi');
    jWriter.WritePropertyName('tableFields');
    jWriter.WriteValue('foobar');
    jWriter.WriteEndObject;

    RestRequest.Method:=rmPOST;
    RestRequest.AddBody(jWriter.Writer.ToString,ctAPPLICATION_JSON);
    RestRequest.Resource:='/location/to/change/data';
    RestRequest.Execute;
  except
    [... Fehlerbehandlung ...]
  end;
  [...]
  jWriter.Writer.Free
  jWriter.Free;
end;
Vielleicht hilft dir das weiter.

Anstelle des jWriters, wie ich das hier gemacht habe, kannst du den Json-String sicherlich genauso auch mit dem jBuilder zusammenbasteln. Hauptsache im Body vom RestRequest landet der String.

Geändert von Bbommel (23. Okt 2018 um 08:27 Uhr) Grund: Irgendwie war das Execute vom Request verloren gegangen. Ist ja nicht ganz unwichtig. ;-)
  Mit Zitat antworten Zitat
Edelfix

Registriert seit: 6. Feb 2015
Ort: Stadtoldendorf
150 Beiträge
 
Delphi 10.4 Sydney
 
#4

AW: post Json mit REST

  Alt 23. Okt 2018, 08:44
Mit einem JSON Online Validator den Request auf gültiges JSON prüfen.

https://www.google.com/search?q=json...utf-8&oe=utf-8
  Mit Zitat antworten Zitat
Redeemer

Registriert seit: 19. Jan 2009
Ort: Kirchlinteln (LK Verden)
794 Beiträge
 
Delphi 2009 Professional
 
#5

AW: post Json mit REST

  Alt 23. Okt 2018, 18:46
Ich solle per Rest eine Json-Objekt per Post an einen Server schicken. Beispiele zum empfangen von Json-Objekten gibts viele... zum Senden nicht.

Von derem Server kommt immer zurück "invalid json primitive".

Verwundert bin ich obwohl in der Komponenten ContentType := 'application/json' ist der ContentType nach dem execute wieder "application/x-www-form-urlencoded" Das passiert auch wenn ich im Designer den Requset per Hand ausführe
"application/x-www-form-urlencoded" ist normal, wenn ein POST-Request mit gefüllten Feldern (Eingabefelder) verwendet wird.
Bei REST wird ein POST-Request ohne Felder verwendet sondern einfach als Payload. Feldnamen braucht man ja auch nicht, steckt ja im JSON drin. Gesendet werden folgende Dinge via TCP: HTTP-Header, zwei Windows-Absätze, JSON-String. Kann man theoretisch auch einfach von Hand machen. Komplettes Programm (nicht geprüft, da hier mal eben im Editor geschrieben):
Delphi-Quellcode:
var
  TCP: TIdTCPClient;
  Header: AnsiString;
  Payload: RawByteString;
begin
  Payload := UTF8Encode(JSON);
  Header := 'POST / HTTP/1.0'#13#10'Host: example.com'#13#10'Content-Type: application/json'#13#10'Content-Length: ' + IntToStr(Length(Payload)) + #13#10'IRGENDWELCHE HEADER HIER'#13#10#13#10;
  TCP := TIdTCPClient.Create();
  TCP.Host := '127.0.0.1';
  TCP.Port := 80;
  TCP.Connect();
  TCP.IOHandler.Write(Header[1], Length(Header));
  TCP.IOHandler.Write(Payload[1], Length(Payload));
  Result := TCP.IOHandler.AllData();
end;
Janni
2005 PE, 2009 PA, XE2 PA
  Mit Zitat antworten Zitat
mikel.pahl

Registriert seit: 21. Sep 2015
Ort: Karlsruhe Neureut
5 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#6

AW: post Json mit REST

  Alt 29. Okt 2018, 13:04
Hallo und vielen Dank für die Posts.

@Edelfix:
Die Json Objekte sind mit Sourcecode den mir das JsonWorkbench aus den RADStudioDemos erstellt hat und daher vaild. (ccoles Tool)

Der Witz an der Sache war, dass die Fima countersolutions.co.uk obwohl die URL zum Post was mit RestAPI ist, einen nackten HTTP-Post erwartet bei dem das Json-Obekt als Payload dran hängt.

Ich schaffe jetzt eine Kommunikation im Prinzip wie es Schokohase gemacht hat:
Delphi-Quellcode:
procedure TFormRESTTest.Button1Click(Sender: TObject);
var
  body: string;
  source: TStream;
  reqs: IHttpRequest;
  resp: IHttpResponse;
  Client: THttpClient;
begin
  body := JsonUpate('10',AccountID);
  source := TStringStream.Create( body );
  Client:=THttpClient.Create( );
// Client.ContentType := 'application/json';
  resp := Client.Post( BaseURL+'AccountPurses', source );
  if ( resp.StatusCode >= 400 ) then
  begin
    ShowMessage('Fehler: '+inttostr(resp.StatusCode));
  end;
 Memo.lines.add(resp.ContentAsString);
end;

Mit der Indy Library habe ich es nicht geschafft da dort immer der Wert accept gesetzt wird selbst wenn die Property leer ist:
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
Allein das reicht damit der Server mit Bad Request antwortet. Sonst sieht das Paket gleich aus im Wireshark.
Michael Pahl
  Mit Zitat antworten Zitat
Schokohase
(Gast)

n/a Beiträge
 
#7

AW: post Json mit REST

  Alt 29. Okt 2018, 13:47
Der Witz an der Sache war, dass die Fima countersolutions.co.uk obwohl die URL zum Post was mit RestAPI ist, einen nackten HTTP-Post erwartet bei dem das Json-Obekt als Payload dran hängt.
Das ist kein Witz, sondern das ist eben so bei einer REST-API
  Mit Zitat antworten Zitat
mikel.pahl

Registriert seit: 21. Sep 2015
Ort: Karlsruhe Neureut
5 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#8

AW: post Json mit REST

  Alt 30. Okt 2018, 10:08
Das ist kein Witz, sondern das ist eben so bei einer REST-API
Und warum kann ich das das nicht mit der RestRequest von Delphi machen ?
Michael Pahl
  Mit Zitat antworten Zitat
Schokohase
(Gast)

n/a Beiträge
 
#9

AW: post Json mit REST

  Alt 30. Okt 2018, 10:20
Das ist kein Witz, sondern das ist eben so bei einer REST-API
Und warum kann ich das das nicht mit der RestRequest von Delphi machen ?
Keine Ahnung. Ich stelle mir aber auch die Frage, warum will ich das mit diesem RestRequest von Delphi machen. Bis jetzt ist mir kein Grund eingefallen der dafür spricht.
  Mit Zitat antworten Zitat
Bbommel

Registriert seit: 27. Jun 2007
Ort: Köln
572 Beiträge
 
Delphi 10.4 Sydney
 
#10

AW: post Json mit REST

  Alt 30. Okt 2018, 11:03
Das ist kein Witz, sondern das ist eben so bei einer REST-API
Und warum kann ich das das nicht mit der RestRequest von Delphi machen ?
Kannst du doch. Ich hatte dir ja ein Beispiel gegeben, wie man es machen kann - läuft bei mir problemlos. Gab es da auch bei dir Fehler oder hattest du es übersehen?
  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 03:22 Uhr.
Powered by vBulletin® Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf