Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   post Json mit REST (https://www.delphipraxis.net/198297-post-json-mit-rest.html)

mikel.pahl 22. Okt 2018 17:34

post Json mit REST
 
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;

Schokohase 22. Okt 2018 19:39

AW: post Json mit REST
 
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;

Bbommel 23. Okt 2018 07:21

AW: post Json mit REST
 
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. :-)

Edelfix 23. Okt 2018 07:44

AW: post Json mit REST
 
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

Redeemer 23. Okt 2018 17:46

AW: post Json mit REST
 
Zitat:

Zitat von mikel.pahl (Beitrag 1416347)
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;

mikel.pahl 29. Okt 2018 12:04

AW: post Json mit REST
 
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.

Schokohase 29. Okt 2018 12:47

AW: post Json mit REST
 
Zitat:

Zitat von mikel.pahl (Beitrag 1417024)
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

mikel.pahl 30. Okt 2018 09:08

AW: post Json mit REST
 
Zitat:

Zitat von Schokohase (Beitrag 1417033)
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 ?

Schokohase 30. Okt 2018 09:20

AW: post Json mit REST
 
Zitat:

Zitat von mikel.pahl (Beitrag 1417095)
Zitat:

Zitat von Schokohase (Beitrag 1417033)
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.

Bbommel 30. Okt 2018 10:03

AW: post Json mit REST
 
Zitat:

Zitat von mikel.pahl (Beitrag 1417095)
Zitat:

Zitat von Schokohase (Beitrag 1417033)
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? :-)

mikel.pahl 31. Okt 2018 09:57

AW: post Json mit REST
 
Hallo Bbommel,

hab ich guck:
Delphi-Quellcode:
procedure TFormRESTTest.BtnRestAbwertenClick(Sender: TObject);
var
  jValue, jApixResponse: TJSONValue;
  jUpdates: TJSONArray;
  hash : system.hash.THashSHA2;
  Ammount, JsonString: string;

begin
  memo.Clear;
  Ammount:='-'+EDBetrag.Text;
  if (AccountID<>'') and (AccountID<>'keine Karte') then
  begin
    JsonString:=JsonUpate(Ammount,AccountID,Sale);
    Memo.lines.Add('-> '+JsonString);
    Req_Update.Params.Clear;
    Req_Update.AddBody(JsonString,ctNone);
    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');
      ......
    end
    else
    begin
      ShowMessage('Fehler :'+inttostr(Res_Update.StatusCode));
      Memo.lines.Add('<- '+ Res_Update.Content);
    end;
  end;
end;
Leider kommt: BadRequest. Der Server von denen ist zickig. Kann sein, dass er sich schon an den accept Headern stört.
Mit Req_Update.AddBody(JsonString,ctAPPLICATION_JSON); gehts auch nicht, da der Server den ContentType schon garnicht mag.

Wireshark erkennt auch das Json nicht richtig; sieht es so aus:
Code:
Hypertext Transfer Protocol
    POST /ApiXRest/RestAPI/AccountPurses HTTP/1.1\r\n
    Connection: Keep-Alive\r\n
    Content-Type: application/x-www-form-urlencoded\r\n
    Accept: application/json, text/plain; q=0.9, text/html;q=0.8,\r\n
    Accept-Charset: UTF-8, *;q=0.8\r\n
    User-Agent: Embarcadero RESTClient/1.0\r\n
    Content-Length: 580\r\n
    Host: api.countersolutions.com\r\n
    \r\n
    [Full request URI: http://api.countersolutions.com/ApiXRest/XXXXX/XXXXXX]
    [HTTP request 1/1]
    [Response in frame: 9]
    File Data: 580 bytes
HTML Form URL Encoded: application/x-www-form-urlencoded
    Form item: "{"ApiXRequest":{"Hash":"b4909261182c6321d88e5e97dd53564bc7d9fa33fa809b61fd7ff3dda44428c5",".......
        Key [truncated]: {"ApiXRequest":{"Hash":"b4909261182c6321d88e5e97dd53564bc7d9fa33fa809b61fd7ff3dda44428c5",......
        Value:

Schokohase 31. Okt 2018 10:12

AW: post Json mit REST
 
Der Server ist nicht zickig, sondern erwartet ein application/json.
Du schickst ihm aber ein application/x-www-form-urlencoded.

Das ist so wie mit den Birnen und den Äpfel.

UPDATE Ok, ich will dann mal lösen ...

Wenn du nichts angibst
Delphi-Quellcode:
ctNone
, dann wird der Request als
Delphi-Quellcode:
ctAPPLICATION_X_WWW_FORM_URLENCODED
abgeschickt.

Also wäre es geschickt, wenn du den Body so angibst
Delphi-Quellcode:
    Req_Update.Params.Clear;
    Req_Update.AddBody(JsonString,ctAPPLICATION_JSON);
    Req_Update.Execute;
denn dann wird der wird dieser Request auch mit dem Content-Type application/json abgeschickt.

Bbommel 31. Okt 2018 13:01

AW: post Json mit REST
 
Hmh, das mit dem "AddBody(JsonString,ctAPPLICATION_JSON)" hätte ich jetzt auch als Problem noch mal erwähnt (stand z.B. in meinem ersten Post auch so).

Michael schreibt aber ja, dass er es auch damit probiert hat und es damit dennoch auch nicht geht. @Michael: Was sagt denn Wireshark in dem Fall?

Als ich mich selbst erstmals mit dem Posten mit dem RestRequest beschäftigt hatte, hatte ich ähnliche Probleme. Das lag aber letztlich daran, dass der Server hier als eine Variante einen sehr, sehr speziellen Content-Type haben wollte, den ich gerne genutzt hätte. Das unterstützt der RestRequest aber nicht und somit bin ich dann auch immer beim "x-www-form" gelandet. Sobald ich dann aber beschlossen habe, das ganz normale JSON zu nutzen und das auch so wie geschrieben im Body gesetz habe, war auch der Content-Type im HHTP-Header korrekt.

Insofern würde mich wundern, wenn der Header auch mit dem "AddBody(JsonString,ctAPPLICATION_JSON)" noch immer falsch aussehen würde und Wireshark das JSON nicht erkennen würde.

(Ich meine, letztlich hast du ja mit dem Ansatz von Schokohase eine funktionierende Lösung, aber eigentlich sollte es halt auch so problemlos laufen...:-) )

mikel.pahl 5. Nov 2018 13:09

AW: post Json mit REST
 
Zitat:

Zitat von Schokohase (Beitrag 1417156)
Der Server ist nicht zickig, sondern erwartet ein application/json.
Du schickst ihm aber ein application/x-www-form-urlencoded.

Hallo Schokohase und BBommel,

es ist wohl leider so, dass der Server als ContentType nur nix oder application/x-www-form-urlencoded akzeptiert. Im Demoprogramm von denen war es auch mit "application/x-www-form-urlencoded".
Wenn Wireshark das Jsonobject erkennt gehts beim Server nicht. Das ist widersinnig aber ich kann es nicht ändern. Von der Theorie her habt ihr Recht aber der Server hält sich nicht dran.

Wenn ich das mit der RESTKomponten - wie gerade eben von Schokohase beschrieben - mache
- mit ctnone kommt 'Invalid JSON primitive:

- mit ctAPPLICATION_JSON sieht in Wireshark so aus:
Code:
Frame 8: 416 bytes on wire (3328 bits), 416 bytes captured (3328 bits) on interface 0
Ethernet II, Src: WistronI_bc:19:16 (3c:97:0e:bc:19:16), Dst: Cisco_27:f3:80 (00:26:0a:27:f3:80)
Internet Protocol Version 4, Src: 10.203.40.2, Dst: 148.253.138.2
Transmission Control Protocol, Src Port: 56837, Dst Port: 80, Seq: 294, Ack: 1, Len: 362
[2 Reassembled TCP Segments (655 bytes): #7(293), #8(362)]
Hypertext Transfer Protocol
    POST /ApiXRest/RestAPI/AccountPurses HTTP/1.1\r\n
    Connection: Keep-Alive\r\n
    Content-Type: application/json\r\n
    Accept: application/json, text/plain; q=0.9, text/html;q=0.8,\r\n
    Accept-Charset: UTF-8, *;q=0.8\r\n
    User-Agent: Embarcadero RESTClient/1.0\r\n
    Content-Length: 362\r\n
    Host: api.countersolutions.com\r\n
    \r\n
    [Full request URI: http://api.countersolutions.com/ApiXRest/RestAPI/xxxxx]
    [HTTP request 1/1]
    [Response in frame: 15]
    File Data: 362 bytes
JavaScript Object Notation: application/json
und als Fehlermeldung kommt in einem HTML-Body zurück:
The exception message is 'Incoming message for operation 'UpdateAccountPurse' (contract 'IRestAPI' with namespace 'http://tempuri.org/') contains an unrecognized http body format value 'Json'. The expected body format value is 'Raw'. This can be because a WebContentTypeMapper has not been configured on the binding. See the documentation of WebContentTypeMapper for more details.'


Mit
Delphi-Quellcode:
sResponse:=NetHTTPRequest1.Post(BaseURL+'XXXURL',jsonToSend);
und die Proterty auf "application/x-www-form-urlencoded" dann ist der Server zufrieden.
In Wireshark sieht man, dass alle Header bis auf Content-Type fehlen:
Code:
Frame 5: 416 bytes on wire (3328 bits), 416 bytes captured (3328 bits) on interface 0
Ethernet II, Src: WistronI_bc:19:16 (3c:97:0e:bc:19:16), Dst: Cisco_27:f3:80 (00:26:0a:27:f3:80)
Internet Protocol Version 4, Src: 10.203.40.2, Dst: 148.253.138.2
Transmission Control Protocol, Src Port: 55896, Dst Port: 80, Seq: 216, Ack: 1, Len: 362
[2 Reassembled TCP Segments (577 bytes): #4(215), #5(362)]
Hypertext Transfer Protocol
    POST /ApiXRest/RestAPI/AccountPurses HTTP/1.1\r\n
    Connection: Keep-Alive\r\n
    Content-Type: application/x-www-form-urlencoded\r\n
    User-Agent: Embarcadero URI Client/1.0\r\n
    Content-Length: 362\r\n
    Host: api.countersolutions.com\r\n
    \r\n
    [Full request URI: http://api.countersolutions.com/ApiXRest/RestAPI/AccountPurses]
    [HTTP request 1/2]
    [Response in frame: 8]
    [Next request in frame: 11]
    File Data: 362 bytes
HTML Form URL Encoded: application/x-www-form-urlencoded
    Form item: "{"ApiXRequest":{"Hash":"067697798a41bfa6ff2d60f75ca2ca9d987e87010228b9640f288b59795fb3c6","BusinessID":"14","CustomerID":"129","SiteID":"1","LocationID":"40","DeviceID":"206","MessageNumber":"1050","UTC":"2018-11-02T12:04:10Z",
        Key [truncated]: {"ApiXRequest":{"Hash":"067697798a41bfa6ff2d60f75ca2ca9d987e87010228b9640f288b59795fb3c6","BusinessID":"14","CustomerID":"129","SiteID":"1","LocationID":"40","DeviceID":"206","MessageNumber":"1050","UTC":"2018-11-02T12:04
        Value:
So ein Request ohne Header bekomme ich mit der Restkomponten von Delphi nicht hin. Muss auch nicht. Ich bin mit der Lösung so glücklich.

Vielen Dank für den Input von euch.

Michael


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:48 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