TRESTRequest:是否可以在 POST 请求中使用自定义媒体类型?

2024-05-05

例如,我们有一个 API,需要我们自己的供应商特定内容类型application/vnd.xxxx.custom.custom-data+json但查看 REST.Client 的源代码,它似乎总是默认为 REST.Types 中的 ContentType 之一,例如在分配时ctNone在我的正文请求中,它将默认为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.


显然TRestCLient试图在你的场景中表现得太聪明。然而,有一个常规的方法可以解决这个问题。关键是:

  1. 将单个内容添加到请求正文,该内容不能是以下任何内容ctNone, ctMULTIPART_FORM_DATA or 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;

echo 服务的响应是:

{
  "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) https://quality.embarcadero.com/browse/RSP-14001那是已在 RAD Studio 10.2 东京修复 https://edn.embarcadero.com/article/44747.

有多种方法可以解决这个问题。仅举几例:

  1. 调整您的 API丢弃辅助 mime 类型。
  2. 更改您的客户端实施 to TNetHttpClient相反,如果您可以放弃所有额外的福利TRestClient提供。
  3. Upgrade到 RAD Studio 10.2+。
  4. Hack it!然而这个选项强烈劝阻,但它可以帮助你更好地理解TRestClient实施细节。

最简单的破解方法是修补方法TCustomRESTRequest.ContentType(注意我们谈论的是单个参数的不变性)返回ContentType一个参数,如果它的AParamsArray参数包含单一参数类型pkREQUESTBODY。这将允许我们将正文添加到类型的请求中ctNone这样修补后的方法就会返回ctNone以及,这将有效地防止附加另一个值Content-Type header.

另一种选择是 patch 方法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。这是一个巨大的耻辱,因为它还阻止您创建自定义子类。也许有充分的理由..谁知道呢;)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TRESTRequest:是否可以在 POST 请求中使用自定义媒体类型? 的相关文章

  • Yammer API 限制

    Yammer Rest api 文档表示 每个应用程序每个用户在 10 秒内最多可以发出 10 个请求 问题 什么是user这里 是为当前经过身份验证的 yammer 用户发出请求的 IP 地址还是承载令牌 如果我们所有的用户都使用相同的外
  • ASP.NET 中 HTTP 缓存相关标头的有效含义

    我正在 ASP NET 2 0 中开发一个 Web 应用程序 其中涉及通过资源处理程序 ashx 提供图像 我刚刚实现了处理缓存标头和条件 GET 请求 这样我就不必为每个请求提供所有图像 但我不确定我是否完全理解浏览器缓存发生了什么 图像
  • Delphi 5 的哈希表实现 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 您知道 Delphi 5 的良好且免费的哈希表实现吗 我需要在哈希表中组织大量数据 并且我有点担心在网
  • AJAX 发送数据到 Node.js 服务器

    我尝试使用 AJAX 将数据发送到 Node js 服务器 但不断遇到同样的问题 即接收问题 这是客户端 JavaScript AJAX 代码 var objects function return new XMLHttpRequest f
  • 轻松的反应

    我有一个与这里描述的类似的案例 动态更改RESTEasy服务返回类型 https stackoverflow com questions 3786781 dynamically change resteasy service return
  • 是否可以声明长度受限且不从 0/1 开始的字符串类型?

    在 Delphi 中 可以声明整数值的子范围 例如 type myInt 2 150 它将 myInt 类型的值限制为 2 到 150 之间的值 但是如果我想限制字符串的长度怎么办 如果我写 type myString string 150
  • JS 库请求的常见 HTTP 标头是什么?

    使用JavaScript 框架原型 http www prototypejs org 我注意到 Ajax 请求通过一个名为X Requested With 其他 JavaScript 库 如 jQuery dojo 和 YUI 是否会向其
  • 如何使用 Gmail 的 SMTP 和 Indy 10 发送电子邮件?

    我正在使用 Delphi 2009 和 svn 中最新的 Indy 10 通过 SMTP 发送电子邮件 但它不适用于 Gmail Google Apps 托管域 当我尝试发送电子邮件时 我收到 必须首先发出 STARTTLS 命令 我尝试用
  • 如何使用 python 的 http.client 准确读取一个响应块?

    Using http client在 Python 3 3 或任何其他内置 python HTTP 客户端库 中 如何一次读取一个分块 HTTP 响应一个 HTTP 块 我正在扩展现有的测试装置 使用 python 编写 http clie
  • 使用 Unity 在 C# 中发送 http 请求

    如何使用 Unity 在 C 中发送 HTTP GET 和 POST 请求 我想要的是 在post请求中发送json数据 我使用Unity序列化器 所以不需要 新的 我只想在发布数据中传递一个字符串并且能够 将 ContentType 设置
  • 打印 TDBGrid [重复]

    这个问题在这里已经有答案了 如何在不安装或下载组件的情况下打印 DBGrid OR 如何将 DBGrid 的数据放入 RichEdit 以便我可以从那里打印它 数据感知控件从 DataSource 属性获取数据 并使用它 不过 您必须手动遍
  • 如何使 StringGrid 的列适合网格的宽度?

    我已经寻找解决方案很长时间了 但没有任何运气 有谁知道一个简单的方法来做到这一点 例如 我想拉伸网格的第二列以适应网格的宽度 Use the ColWidths财产 像这样 with StringGrid1 do ColWidths 1 C
  • symfony api 平台深度

    到目前为止 我们一直在 Symfony Doctrine 和 Serializer 深度方面苦苦挣扎 我希望能够使用 Symfony 提供一级深度的 JSON REST API 从而允许我直接从视图管理我的 外键 和关系逻辑 GET peo
  • @RestController 没有 @ResponseBody 方法工作不正确

    我有以下控制器 RestController RequestMapping value base url public class MyController RequestMapping value child url method Req
  • 自托管 WCF REST 服务和基本身份验证

    我创建了一个自托管的 WCF REST 服务 带有 WCF REST Starter Kit Preview 2 中的一些额外内容 这一切工作正常 我现在正在尝试向服务添加基本身份验证 但我在 WCF 堆栈中遇到了一些相当大的障碍 这阻止了
  • Flutter http请求上传mp3文件

    我使用这个 api 上传 mp3 文件 使用这种方法 Future
  • 如何比较枚举类型集

    从某个时刻开始 我厌倦了编写设定条件 and or 因为对于更多的条件或更长的变量名 重新编写会变得笨拙且烦人 所以我开始写助手这样我就可以写ASet ContainsOne ceValue1 ceValue2 代替 ceValue1 in
  • 获取 Parse Analytics 自定义仪表板

    是否可以使用 Javascript 或 REST API 从 Parse 获取应用程序分析 我想在我自己的仪表板中显示下载数量和自定义事件 不可以 您只能通过 REST API 推送 https parse com docs rest ht
  • 如何在Delphi中将对象方法作为参数传递,然后调用它?

    我担心这可能是一个有点愚蠢的问题 但这让我很难过 我正在寻找将对象的方法传递到过程中的最简单的方法 以便过程可以调用对象的方法 例如 超时后 或者可能在不同的线程中 所以基本上我想 捕获对对象方法的引用 将该引用传递给过程 使用该引用 从过
  • 使用 WCF 支持“application/x-www-form-urlencoded”发布数据的最佳方式?

    我正在基于 W3C 规范构建 WCF 服务 该规范定义了接受 application x www form urlencoded 发布数据的 RESTful Web 服务端点 默认情况下 WCF 不支持这种类型的消息编码 我发现了许多创建如

随机推荐