【问题标题】:Delphi REST GET but not POST works with AusPost eParcel APIDelphi REST GET 但不是 POST 与 AusPost eParcel API 一起使用
【发布时间】:2020-03-01 08:04:57
【问题描述】:

我正在为一个需要使用来自 Delphi 10.2 Tokyo 服务程序的 eParcel REST API 创建 AusPost 货件的客户进行集成项目。

这是我班级的Constructor

constructor TAusPostRESTServices.Create;
begin
  // Create the REST objects

  HTTPBasicAuthenticator := THTTPBasicAuthenticator.Create(API_KEY, API_PASSWORD);
  RESTClient := TRESTClient.Create(BASE_URL);
  RESTResponse := TRESTResponse.Create(nil);
  RESTRequest := TRESTRequest.Create(nil);

  // Set the initial properties

  RESTClient.Params.Clear;
  RESTClient.HandleRedirects := true;
  RESTClient.Authenticator := HTTPBasicAuthenticator;
  RESTClient.RaiseExceptionOn500 := false;

  RESTRequest.Client := RESTClient;
  RESTRequest.Response := RESTResponse;
  RESTRequest.SynchronizedEvents := false;
end;

以及它使用的常量(请注意,帐户和 API 值来自参考 API,可能实际上不起作用)。

const
  BASE_URL = 'https://digitalapi.auspost.com.au/test';
  ACCOUNT = '0000123456';
  API_KEY = '601a4032-6dbd-46aa-9c6c-8c6dacca5e61';
  API_PASSWORD = 'password';

Uses 子句至少有这些:

REST.Client, REST.Authenticator.Basic, IPPeerClient, JSON, REST.Types

我尝试使用的 eParcel API 方法是 Create ShipmentsGet Shipments

这是我的 Get Shipments Delphi 代码的样子(不要担心 AJSONValue 响应。仍在研究最终会做什么):

function TAusPostRESTServices.GetShipments(var AError: String): Boolean;
var
  AStatusCode: Integer;
  AJSONString: String;
  AJSONValue: TJSonValue;
  AParameter: TRESTRequestParameter;
begin
  // Set the request header info

  RESTRequest.Method := TRESTRequestMethod.rmGET;
  RESTRequest.Resource := '/shipping/v1/shipments';
  RESTRequest.Accept := 'application/json';

  AParameter := RESTRequest.Params.AddItem;
  AParameter.ContentType := ctAPPLICATION_JSON;
  AParameter.name := 'Content-Type';
  AParameter.Value := 'application/json';
  AParameter.Kind := TRESTRequestParameterKind.pkHTTPHEADER;

  RESTRequest.Params.AddHeader('account-number', ACCOUNT);

  try
    try
      RESTRequest.Execute;
    except on E: Exception do
      ;
    end;

    // If the StatusCode is 200, then the REST method worked

    AStatusCode := RESTRequest.Response.StatusCode;

    if AStatusCode <> 200 then
      AError := RESTRequest.Response.Content
    else
    begin
      AJSONValue := TJSonObject.ParseJSONValue(RESTRequest.Response.Content);

      AJSONValue.Free;

      AError := '';
    end;
  finally
    Result := (AError = '');
  end;
end;

这工作正常,并从 AusPost API 文档中包含的示例 JSON 返回我使用 Postman 创建的一个试用演示货件。

{
  "shipments": [
    {
      "shipment_id": "T7sK0EA9HGcAAAFufLYgUxkK",
      "shipment_reference": "My second shipment ref",
      "shipment_creation_date": "2019-10-30T06:42:42+11:00",
      "customer_reference_1": "cb1234",
      "customer_reference_2": "cb2345",
      "sender_references": [
        "cb1234",
        "cb2345"
      ],
      "from": {
        "type": "MERCHANT_LOCATION",
        "lines": [
          "111 Bourke St"
        ],
        "suburb": "Melbourne",
        "postcode": "3000",
        "state": "VIC",
        "apcn": "2468013579",
        "name": "Carl Block",
        "country": "AU"
      },
      "to": {
        "type": "STANDARD_ADDRESS",
        "lines": [
          "PO Box 123"
        ],
        "suburb": "Rye",
        "postcode": "3941",
        "state": "VIC",
        "apcn": "1234567890",
        "name": "Blocked Carl",
        "business_name": "In debt",
        "country": "AU",
        "email": "carl@gmai.co",
        "phone": "0356567567",
        "delivery_instructions": "please leave behind shed"
      },
      "items": [
        {
          "weight": 1,
          "height": 10,
          "length": 10,
          "width": 10,
          "contains_dangerous_goods": false,
          "authority_to_leave": true,
          "safe_drop_enabled": true,
          "allow_partial_delivery": false,
          "item_id": "3mQK0EA9CksAAAFui7YgUxkK",
          "item_reference": "blocked",
          "tracking_details": {
            "article_id": "111JD538925401000930800",
            "consignment_id": "111JD5389254"
          },
          "product_id": "7E55",
          "item_summary": {
            "total_cost": 11.48,
            "total_cost_ex_gst": 11.48,
            "total_gst": 0,
            "status": "Created"
          },
          "item_contents": [],
          "label": {
            "status": "Pending",
            "errors": []
          },
          "postage_details": {
            "price": {
              "calculated_price": 11.48,
              "calculated_price_ex_gst": 11.48,
              "calculated_gst": 0
            }
          }
        },
        {
          "weight": 2.123,
          "height": 10,
          "length": 100.1,
          "width": 10,
          "contains_dangerous_goods": false,
          "authority_to_leave": true,
          "safe_drop_enabled": true,
          "allow_partial_delivery": false,
          "item_id": "9cgK0EA9smIAAAFufrYgUxkK",
          "item_reference": "blocked",
          "tracking_details": {
            "article_id": "111JD538925403000930804",
            "consignment_id": "111JD5389254"
          },
          "product_id": "7E55",
          "item_summary": {
            "total_cost": 23.34,
            "total_cost_ex_gst": 23.34,
            "total_gst": 0,
            "status": "Created"
          },
          "item_contents": [],
          "label": {
            "status": "Pending",
            "errors": []
          },
          "postage_details": {
            "price": {
              "calculated_price": 23.34,
              "calculated_price_ex_gst": 23.34,
              "calculated_gst": 0
            }
          }
        },
        {
          "weight": 1,
          "height": 10,
          "length": 10,
          "width": 10,
          "contains_dangerous_goods": false,
          "authority_to_leave": true,
          "safe_drop_enabled": true,
          "allow_partial_delivery": false,
          "item_id": "_hEK0EA9zIIAAAFuirYgUxkK",
          "item_reference": "blocked",
          "tracking_details": {
            "article_id": "111JD538925402000930807",
            "consignment_id": "111JD5389254"
          },
          "product_id": "7E55",
          "item_summary": {
            "total_cost": 11.48,
            "total_cost_ex_gst": 11.48,
            "total_gst": 0,
            "status": "Created"
          },
          "item_contents": [],
          "label": {
            "status": "Pending",
            "errors": []
          },
          "postage_details": {
            "price": {
              "calculated_price": 11.48,
              "calculated_price_ex_gst": 11.48,
              "calculated_gst": 0
            }
          }
        }
      ],
      "options": {},
      "shipment_summary": {
        "total_cost": 47.41,
        "total_cost_ex_gst": 43.1,
        "fuel_surcharge": 1.11,
        "total_gst": 4.31,
        "status": "Created",
        "tracking_summary": {
          "Created": 3
        },
        "number_of_items": 3
      },
      "movement_type": "DESPATCH",
      "charge_to_account": "1001746337"
    }
  ],
  "pagination": {
    "total_number_of_records": 1,
    "number_of_records_per_page": 1000,
    "current_page_number": 1
  }
}

我的 Create Shipment 几乎是一样的:

function TAusPostRESTServices.CreateShipment(var AError: String): Boolean;
var
  AStatusCode: Integer;
  AJSONString: String;
  AJSONValue: TJSonValue;
  AParameter: TRESTRequestParameter;
begin
  // Set the request header info

  RESTRequest.Method := TRESTRequestMethod.rmPOST;
  RESTRequest.Resource := '/shipping/v1/shipments';
  RESTRequest.Accept := 'application/json';

  AParameter := RESTRequest.Params.AddItem;
  AParameter.ContentType := ctAPPLICATION_JSON;
  AParameter.name := 'Content-Type';
  AParameter.Value := 'application/json';
  AParameter.Kind := TRESTRequestParameterKind.pkHTTPHEADER;

  RESTRequest.Params.AddHeader('account-number', ACCOUNT);

  try
    AJSONString := '{'+
                   '  "shipments": ['+
                   '    {'+
                   '      "shipment_reference": "139301",'+
                   '      "customer_reference_1": "1",'+
                   '      "from": {'+
                   '        "name": "MERCHANT_LOCATION",'+
                   '        "lines": ['+
                   '          "111 Bourke St"'+
                   '        ],'+
                   '        "suburb": "MELBOURNE",'+
                   '        "postcode": "3000",'+
                   '        "state": "VIC"'+
                   '      },'+
                   '      "to": {'+
                   '        "name": "STANDARD_ADDRESS",'+
                   '        "lines": ['+
                   '          "PO Box 123"'+
                   '        ],'+
                   '        "suburb": "Rye",'+
                   '        "state": "VIC",'+
                   '        "postcode": "3941",'+
                   '        "delivery_instructions": "please leave behind shed",'+
                   '        "email": "carl@gmai.co"'+
                   '      },'+
                   '      "items": ['+
                   '        {'+
                   '          "width": 10,'+
                   '          "height": 10,'+
                   '          "length": 10,'+
                   '          "weight": 0.19,'+
                   '          "product_id": "7E55",'+
                   '          "authority_to_leave": true'+
                   '        }'+
                   '      ]'+
                   '    }'+
                   '  ]'+
                   '}';

    RESTRequest.Body.Add(AJSONString, ctAPPLICATION_JSON);

    try
      RESTRequest.Execute;
    except on E: Exception do
      ;
    end;

    // If the StatusCode is 200, then the REST method worked

    AStatusCode := RESTRequest.Response.StatusCode;

    if AStatusCode <> 200 then
      AError := RESTRequest.Response.Content
    else
    begin
      AJSONValue := TJSonObject.ParseJSONValue(RESTRequest.Response.Content);

      AJSONValue.Free;

      AError := '';
    end;
  finally
    Result := (AError = '');
  end;
end;

运行时,状态码为404,内容文本为

{
  "code": "404",
  "name": "Not Found",
  "message": "Invalid HTTP method POST or request path /shipments. Please refer to the Developer Centre reference at https://developers.auspost.com.au/apis/shipping-and-tracking/reference for correct usage."
}

但是,如果我在 Postman 中设置 Create Shipment POST,原始正文是相同的 JSON 字符串,它可以正常工作,并且我会得到适当的响应:

{
    "shipments": [
        {
            "shipment_id": "QdkK0EA99JoAAAFuba0gUzkF",
            "shipment_reference": "139301",
            "shipment_creation_date": "2019-11-05T11:45:03+11:00",
            "items": [
                {
                    "weight": 0.190,
                    "authority_to_leave": true,
                    "safe_drop_enabled": true,
                    "allow_partial_delivery": true,
                    "item_id": "kwgK0EA9Es8AAAFubq0gUzkF",
                    "tracking_details": {
                        "article_id": "111JD539319101000931501",
                        "consignment_id": "111JD5393191"
                    },
                    "product_id": "7E55",
                    "item_summary": {
                        "total_cost": 17.14,
                        "total_cost_ex_gst": 17.14,
                        "total_gst": 0.00,
                        "status": "Created"
                    },
                    "item_contents": []
                }
            ],
            "options": {},
            "shipment_summary": {
                "total_cost": 17.55,
                "total_cost_ex_gst": 15.95,
                "fuel_surcharge": 0.41,
                "total_gst": 1.60,
                "status": "Created",
                "tracking_summary": {
                    "Created": 1
                },
                "number_of_items": 1
            },
            "movement_type": "DESPATCH",
            "charge_to_account": "1001746337"
        }
    ]
}

我不明白为什么 Delphi 会给我这个 404 错误。如果我在使用 Postman 时没有在请求中包含正文,它会给我这个响应:

{
    "errors": [
        {
            "code": "400",
            "name": "INVALID_PAYLOAD",
            "message": "Unable to deserialize payload.  Please ensure that the payload is valid JSON. Please note that when assigned, boolean fields can only accept 'true' or 'false' as values."
        }
    ]
}

但是如果我没有在 Delphi 的请求中包含正文,我会得到相同的 404 错误。有谁知道我可以尝试我的代码的不同 REST API 吗?或者有没有人成功使用过 Delphi 的 AusPost eParcel API,即使它是用 Indy 完成的?

【问题讨论】:

  • 最好提供 CreateShipment 的完整代码

标签: rest delphi


【解决方案1】:

发现了问题,即使我无法解释。一旦我删除了 Content-Type 参数代码,post 方法就可以正常工作了。

// AParameter := RESTRequest.Params.AddItem;
// AParameter.ContentType := ctAPPLICATION_JSON;
// AParameter.name := 'Content-Type';
// AParameter.Value := 'application/json';
// AParameter.Kind := TRESTRequestParameterKind.pkHTTPHEADER;

不知何故,包括在请求中只是把事情搞砸了。没有解释为什么,但这可能只是 Delphi REST 库的怪癖之一。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-14
    • 1970-01-01
    • 1970-01-01
    • 2019-07-21
    • 2012-01-17
    • 2012-07-22
    相关资源
    最近更新 更多