我正在使用 Delphi 10.2 和
TRESTRequest
、TRESTClient
、TRESTResponse
组件以及 REST.Response.Adapter
、REST.Client
、System.JSON
和 REST.Types
库来进行 API 调用。
第一个 API 调用需要用户名和密码,并返回带有两个键值对的数据对象:
token
:表示 JWT(JSON Web 令牌)的字符串。
expiresIn
也是一个字符串,指示令牌过期之前的持续时间,例如30分钟。
procedure TfrmMain.btnAuth2Click(Sender: TObject);
var
JsonRequest: TJSONObject;
ResponseJson: TJSONObject;
DataJson: TJSONObject;
TestUserName: string;
TestPassword: string;
JsonBody: string;
I: Integer;
begin
TestUserName := frmLogIn.UserName;
TestPassword := frmLogIn.Password;
// Set up the Base URL
RESTClient1.BaseURL := '';
RESTClient1.BaseURL := 'https://dev-api.nameofwebsite.com/function';
RESTRequest1.Resource := '/auth/login';
// Set request headers
RESTRequest1.Method := rmPOST;
RESTRequest1.Params.Clear; // Clear any existing parameters
RESTRequest1.Params.AddHeader('accept', 'application/json');
// Prepare the JSON request body.
JsonRequest := TJSONObject.Create;
JsonRequest.AddPair('email', TJSONString.Create(TestUserName));
JsonRequest.AddPair('password', TJSONString.Create(TestPassword));
// Convert JSON request body to string
JsonBody := JsonRequest.ToString;
// Save JSON body to file for inspection
SaveJsonBodyToFile('AuthRequest_body.json', JsonBody);
// Set the JSON request body
RESTRequest1.AddBody(JsonBody, ctAPPLICATION_JSON);
// Used to strip out and inspect the header and body
mmoDisplay.Lines.Add('');
mmoDisplay.Lines.Add('Request Method: ' + RESTRequestMethodToString
(RESTRequest1.Method));
mmoDisplay.Lines.Add('Request URL: ' + RESTRequest1.Resource);
mmoDisplay.Lines.Add('Request Headers:');
for I := 0 to RESTRequest1.Params.Count - 1 do
begin
if (RESTRequest1.Params[I].Kind = pkHTTPHEADER) OR
(RESTRequest1.Params[I].Kind = pkURLSEGMENT) OR
(RESTRequest1.Params[I].Kind = pkREQUESTBODY) then
begin
mmoDisplay.Lines.Add(RESTRequest1.Params[I].Name + ': ' +
RESTRequest1.Params[I].Value);
end;
end;
RESTRequest1.Execute;
// Log the response content
mmoDisplay.Lines.Add('Response Content: ' + RESTResponse1.Content);
// Handle the response
if RESTResponse1.StatusCode = 200 then
begin
ResponseJson := TJSONObject.ParseJSONValue(RESTResponse1.Content)
as TJSONObject;
try
if Assigned(ResponseJson) then
begin
DataJson := ResponseJson.GetValue('data') as TJSONObject;
if Assigned(DataJson) then
begin
mmoDisplay.Lines.Add('Token: ' + DataJson.GetValue('token').Value);
OriginalAuthToken := DataJson.GetValue('token').Value;
end;
end;
finally
ResponseJson.Free;
end;
end
else
begin
// Handle error response
raise Exception.Create('Error: ' + RESTResponse1.StatusText);
end;
mmoDisplay.Lines.Add('Server Response: ' + IntToStr(RESTResponse1.StatusCode)
+ ' ' + RESTResponse1.StatusText);
end;
然后,我对同一服务器进行另一个 API 调用,其中的想法是传递第一个 API 调用中的
token
以及从适用于 Android 手机的 Microsoft Authenticator 应用程序生成的 OTP:
procedure TfrmMain.btnVerifyOTPClick(Sender: TObject);
var
OTPJsonRequest: TJSONObject;
ResponseJson: TJSONObject;
DataJson: TJSONObject;
TestOTP: string;
kjd: string;
KJsonBody: string;
I: Integer;
begin
frmotp.ShowModal;
TestOTP := frmotp.OTP;
// Set up the Base URL
RESTClient1.BaseURL := 'https://dev-api.nameofwebsite.com/function';
// Set the resource path
RESTRequest1.Resource := '/auth/verify-otp';
// Display the auth token.
mmoDisplay.Lines.Add('');
mmoDisplay.Lines.Add('OTP: ' + TestOTP);
mmoDisplay.Lines.Add('Auth Token to be submitted: ' + OriginalAuthToken);
// // Set request headers
RESTRequest1.Method := rmPOST;
RESTRequest1.Params.Clear; // Clear any existing parameters
RESTRequest1.Params.AddHeader('accept', 'application/json');
RESTRequest1.Params.AddHeader('Authorization', OriginalAuthToken);
// Prepare the JSON request body
OTPJsonRequest := TJSONObject.Create;
OTPJsonRequest.AddPair('otp', TJSONString.Create(TestOTP));
// OTPJsonRequest.AddPair('otp', TJSONNumber.Create(TestOTP));
// Convert JSON request body to string
KJsonBody := OTPJsonRequest.ToString;
// Save JSON body to file for inspection
SaveJsonBodyToFile('request_body.json', KJsonBody);
RESTRequest1.AddBody(KJsonBody, ctAPPLICATION_JSON);
// Before executing the request
// Log the complete request
mmoDisplay.Lines.Add('');
mmoDisplay.Lines.Add('Request Method: ' + RESTRequestMethodToString
(RESTRequest1.Method));
mmoDisplay.Lines.Add('Request URL: ' + RESTRequest1.Resource);
mmoDisplay.Lines.Add('Request Headers:');
for I := 0 to RESTRequest1.Params.Count - 1 do
begin
mmoDisplay.Lines.Add(RESTRequest1.Params[I].Name + ': ' +
RESTRequest1.Params[I].Value);
end;
// Execute the request
try
RESTRequest1.Execute;
// Log the response content
mmoDisplay.Lines.Add('Response Content: ' + RESTResponse1.ContentEncoding +
' ' + RESTResponse1.Content);
except
on E: Exception do
begin
// Log any exceptions that occur during the request
mmoDisplay.Lines.Add('Exception: ' + E.Message);
end;
end;
// Handle the response
if RESTResponse1.StatusCode = 200 then
begin
ResponseJson := TJSONObject.ParseJSONValue(RESTResponse1.Content)
as TJSONObject;
try
if Assigned(ResponseJson) then
begin
DataJson := ResponseJson.GetValue('data') as TJSONObject;
if Assigned(DataJson) then
begin
mmoDisplay.Lines.Add('Token: ' + DataJson.GetValue('token').Value);
OriginalAuthToken := DataJson.GetValue('token').Value;
end;
end;
finally
OTPJsonRequest.Free;
end;
end
else
begin
// Handle error response
raise Exception.Create('Error: ' + RESTResponse1.StatusText);
end;
mmoDisplay.Lines.Add('Server Response: ' + IntToStr(RESTResponse1.StatusCode)
+ ' ' + RESTResponse1.StatusText);
end;
我已经在两个不同的 API 测试环境中测试了 API 调用,例如Swagger 和 Postman,这两个 API 都可以工作。
我目前无法让第二个 API 在 Delphi 中工作,因为它在
RESTRequest1.Execute;
上生成异常,例如:
异常:响应内容不是有效的 JSON
对于我在第二次 API 调用中出错的任何想法,我将不胜感激。
这是我第一次尝试从 Delphi 编写 API 调用,因此如果有人可以推荐任何资源,了解一下也很好。
非常感谢@RemyLebeau 的回复。 在身份验证/登录时,我得到以下合法回复:
{"data":{"token":"Bearer
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVSUQiOiI2NWJhMWI1ZTY4NzY5NTMxYTkzZTFiMTgiLCJpYXQiOjE3MTUyNDc5ODYsImV4cCI6MTcxNTI0OTc4Nn0.JgkfN4var05APlSga3NzggLJ5rX1AxrBj0QZtHSs0cw",
"expiresIn":"30m"}}
在第二个 verify-otp API 调用中,我在将授权添加到标头时遇到问题。
本来我是这样做的:
RESTRequest1.Params.AddHeader('Authorization', 'Bearer ' + OriginalAuthToken);
删除标题内容,我可以看到“Bearer”关键字被添加了两次,例如
Authorization: Bearer Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVSUQiOiI2NWJhMWI1ZTY4NzY5NTMxYTkzZTFiMTgiLCJpYXQiOjE3MTUyNDgyMjIsImV4cCI6MTcxNTI1MDAyMn0.uhwG_coQ4EBYzhymwVM1X7KPHCQid0Y_qYP4-eHjgLs
body219E405A2A624330AA4C092D6892C1A3: {"otp":"683937"}
似乎有东西在标题中添加了关键字“Bearer”,所以我将代码更改为:
RESTRequest1.Params.AddHeader('Authorization', OriginalAuthToken);
这似乎可以正确构造标题,例如
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVSUQiOiI2NWJhMWI1ZTY4NzY5NTMxYTkzZTFiMTgiLCJpYXQiOjE3MTUyNDgzNDgsImV4cCI6MTcxNTI1MDE0OH0.jlVONiopNLNk6avIqgxn6f7oTsftMAeAECBLNC7VikQ
body48EED8480CA643BBA56411C646D76D90: {"otp":"134513"}
然而,在与网络开发人员沟通后,他看到的是 Bearer 和 Token 值之间的“%20”。
授权:'承载%20eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVSUQiOiI2NWJhMWI1ZTY4NzY5NTMxYTkzZTFiMTgiLCJpYXQiOjE3MTUyNDgzNDgsImV4cCI6MTcxNTI1MDE0OH0.jlVO NiopNLNk6avIqgxn6f7oTsftMAeAECBLNC7VikQ
有什么方法可以确保标头不包含这个“%20”吗?