TRESTRequest: возможно ли использовать пользовательские типы мультимедиа в запросе POST? - PullRequest
2 голосов
/ 03 июня 2019

У нас есть API, который ожидает от нашего собственного конкретного типа контента, например, application/vnd.xxxx.custom.custom-data+json, но, просматривая исходный код REST. Клиент, похоже, всегда использует по умолчанию один из типов ContentTypes в REST.Types, например, при назначении * 1002.* в моем теле запросе по умолчанию будет ctAPPLICATION_X_WWW_FORM_URLENCODED.

Я пытался назначить тип содержимого напрямую свойству TRESTClient.ContentType, но оно перезаписывается значением TRESTRequest.ContentType.Я также добавил пользовательский тип контента в качестве параметра в TRESTRequest, который действительно распознается, но все равно добавляет ctAPPLICATION_X_WWW_FORM_URLENCODED в конце, вызывая недопустимое исключение типа MIME.

begin
  APIClient := TRESTClient.Create(API_URL);
  APIRequest := TRESTRequest.Create(nil);

  try
    JsonToSend := TStringStream.Create(strJson, TEncoding.UTF8);
    APIClient.Accept := 'application/vnd.xxxx.custom.custom-data+json';
    // Below line gets overwritten
    APIClient.ContentType := 'application/vnd.xxxx.custom.custom-data+json';
    APIRequest.Client := APIClient;
    APIRequest.Resource := 'ENDPOINT_URL';
    APIRequest.Accept := 'application/vnd.xxxx.custom.custom-data+json';

    APIRequest.AddParameter(
      'Content-Type',
      'application/vnd.xxxx.custom.custom-data+json',
      pkHTTPHEADER,
      [poDoNotEncode]
      );  // This includes the custom CT in the request but appends the preset one as well so in this case ctAPPLICATION_X_WWW_FORM_URLENCODED when ctNone is set

    APIRequest.AddBody(JsonToSend, ctNone);
    APIRequest.Method := rmPost;
    try
      APIRequest.Execute;
    except
      on E: Exception do
        ShowMessage('Error on request: '#13#10 + e.Message);
    end;
  finally
    JsonToSend.Free;
  end;
end;

Мне бы я ожидал, что тамбыть сценарием, в котором, если в параметрах заголовка указан тип контента, он будет использовать указанный, а не какой-либо из предустановленных.Тем не менее возникает исключение API, потому что был предоставлен неизвестный тип носителя.Исключение API гласит:

Invalid mime type "application/vnd.xxxx.custom.custom-data+json, application/x-www-form-urlencoded": Invalid token character ',' in token "vnd.xxxx.custom.custom-data+json, application/x-www-form-urlencoded"

Насколько я понимаю, оно распознает мой пользовательский тип контента, указанный в параметрах, но все еще добавляет один из предустановленных типов контента из REST.Types в этот заголовок запроса, вызывая его сбой.,Я ожидаю, что он отправит тело с заголовком запроса, равным application/vnd.xxxx.custom.custom-data+json, исключая application/x-www-form-urlencoded.

1 Ответ

2 голосов
/ 03 июня 2019

Очевидно TRestCLient пытается действовать слишком умно в вашем сценарии.Однако есть регулярный путь к этому.Ключ:

  1. для добавления отдельного контента в тело запроса, который не должен быть любым из ctNone, ctMULTIPART_FORM_DATA или ctAPPLICATION_X_WWW_FORM_URLENCODED.
  2. для переопределения Content-Type с использованиемпользовательское значение заголовка.

Пример кода:

uses
  System.NetConsts;

RESTClient1.BaseURL := 'https://postman-echo.com/post';
RESTRequest1.Method := rmPOST;
RESTRequest1.Body.Add('{ "some": "data" }', ctAPPLICATION_JSON);
RESTRequest1.AddParameter(sContentType, 'application/vnd.hmlr.corres.corres-data+json',
  pkHTTPHEADER, [poDoNotEncode]);
RESTRequest1.Execute;

Ответ от службы эха:

{
  "args":{
  },
  "data":{
    "some":"data"
  },
  "files":{
  },
  "form":{
  },
  "headers":{
    "x-forwarded-proto":"https",
    "host":"postman-echo.com",
    "content-length":"18",
    "accept":"application/json, text/plain; q=0.9, text/html;q=0.8,",
    "accept-charset":"UTF-8, *;q=0.8",
    "content-type":"application/vnd.hmlr.corres.corres-data+json",
    "user-agent":"Embarcadero RESTClient/1.0",
    "x-forwarded-port":"443"
  },
  "json":{
    "some":"data"
  },
  "url":"https://postman-echo.com/post"
}

Обратите внимание на отраженные заголовки, особенно Content-Type конечно.Я тестировал образец в Delphi 10.2 Tokyo, поэтому, надеюсь, он также будет работать в XE8.

Edit

Наблюдаемое вами поведение - это ошибка (RSP-14001) , котораябыло исправлено в RAD Studio 10.2 Токио .

Существуют различные способы решения этой проблемы.Чтобы назвать несколько:

  1. Адаптируйте свой API для удаления вторичного типа MIME.
  2. Вместо этого измените реализацию клиента на TNetHttpClient, если вы можете отказаться от всех дополнительных преимуществ, которые предоставляет TRestClient.
  3. Обновите до RAD Studio 10.2 +.
  4. Взломайте! Этооднако опция настоятельно не рекомендуется , но она может помочь вам лучше понять TRestClient подробности реализации.

Самый простой способ взломать его - это пропатчить метод TCustomRESTRequest.ContentType (примечаниемы говорим об инварианте с единственным аргументом), который возвращает ContentType параметра, если его аргумент AParamsArray содержит единственный параметр вида pkREQUESTBODY.Это позволило бы нам добавить тело к запросу типа ctNone, чтобы исправленный метод также возвратил ctNone, и это эффективно предотвратило бы добавление другого значения в заголовок Content-Type.

Другой вариант будетчтобы пропатчить метод TRESTHTTP.PrepareRequest, чтобы предпочесть пользовательский заголовок Content-Type перед предполагаемым типом содержимого запроса.Вот как работает текущая реализация после того, как она была исправлена ​​в RAD Studio 10.2 Tokyo.Эта логика также применяется к другим заголовкам - Accept, Accept-Charset, Accept-Encoding, User-Agent.Метод исправления TRESTHTTP.PrepareRequest немного сложнее реализовать, поскольку он имеет private видимость.

Самым сложным вариантом будет установка исправления TWinHTTPRequest.SetHeaderValue для отбрасывания значения типа вторичного контента.Это также самый опасный вариант, поскольку он может повлиять на все, что связано с HTTP (что зависит от THTTPClient) в вашем приложении.Также сложно, но не невозможно, пропатчить класс, потому что он полностью скрыт в implementation разделе System.Net.HttpClient.Win.pas.Это позор, потому что он также не позволяет создавать собственные подклассы.Может быть, по уважительной причине .. кто знает;)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...