可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am trying to figure out how to use Delphi 10 Seattle and Indy to send a POST request to either Plivo or Twilio for sending SMS messages. When I use this code for Twilio efforts, I get an Unauthorized message in return (note that I have redacted my user name and Auth code):
procedure TSendTextForm.TwilioSendSms(FromNumber, ToNumber, Sms: string; Var Response: TStrings);
var
apiurl, apiversion, accountsid, authtoken,
url: string;
aParams, aResponse: TStringStream;
mHTTP : TidHttp;
begin
mHTTP := TIdHTTP.Create(nil);
apiurl := 'api.twilio.com';
apiversion := '2010-04-01';
accountsid := 'AC2f7cda1e6a4e74376***************:2b521b60208af4c*****************';
url := Format('https://%s/%s/Accounts/%s/SMS/Messages/', [apiurl, apiversion, accountsid]);
aParams := TStringStream.Create ;
try
aParams.WriteString('&From=' + FromNumber);
aParams.WriteString('&To=' + ToNumber);
aParams.WriteString('&Body=' + Sms);
aResponse := TStringStream.Create;
try
mHTTP.Post(url, aParams, aResponse);
finally
Response.Text := aResponse.DataString;
end;
finally
aParams.Free;
end;
end;
I have similar code for Plivo. Neither company has any Delphi support. Can anyone tell me what I'm missing above here? Thanks so much.
Mic
回答1:
Twilio evangelist here.
In addition to the Basic Auth suggestion made by @mjn above, there are two other issues in your sample I believe will cause you problems:
First, in the code example above, your URL will be wrong because the accountsid
variable is concatenating together both your sid and auth token.
accountsid := 'AC2f7cda1e6a4e74376***************:2b521b60208af4c*****************';
While you do want to do this in order to use Basic Authentication, you don't want the auth token as part of the URL. When you create the url
property you just want to put the SID into the URL as the parameter like this:
/Accounts/ACXXXXXXX/
Second, I would also suggest not using the /SMS
resources as its deprecated. Instead use /Messages
which is newer and has more features:
/Accounts/ACXXXXXXX/Messages
回答2:
The REST API docs on https://www.twilio.com/docs/api/rest say that basic auth is used:
HTTP requests to the REST API are protected with HTTP Basic
authentication.
TIdHTTP has built-in support for Basic authentication. Simply set the TIdHTTP.Request.BasicAuthentication
property to true, and set IdHTTP.Request.Username
and TIdHTTP.Request.Password
properties as needed.
Other hints:
TIdHTTP.Create(nil)
can be shortened to TIdHTTP.Create
- the var modifier for Response can be changed to const
Your code also leaks memory because the Indy component is not freed.
回答3:
In Delphi 10 Seattle, there is some REST components with TRestClient
And TMS made components for Twillio in their TMS Cloud Pack http://www.tmssoftware.com/site/cloudpack.asp
回答4:
It's funny, but I was just going to post the same question about plivo. I have been also trying to get both twilio and plivo to work. I did manage to finally get twilio to work. Plivo on the other hand would not work with the same code, even though they both are practically the same.
I used REST functions in delphi. The following code is used for both twilio and plivo.
procedure TForm1.FormCreate(Sender: TObject);
begin
// TypeCo
// = T = Twilio
// = P = Plivo
TypeCo := 'T';
if TypeCo='T' then // Twillio
begin
AccountSid := 'ACd27xxxxxxxxxxxxxxxxxxxxxxb106e38'; // x's were replaced to hide ID
AuthToken := '24fxxxxxxxxxxxxxxxxxxxxxxxxf08ed'; // x's were replaced to hide Token
BaseURL := 'https://api.twilio.com';
Resource := '/2010-04-01/Accounts/'+accountSid+'/Messages';
end
else if TypeCO='P' then // Plivo
begin
AccountSid := 'MANTxxxxxxxxxxxxxXYM';
AuthToken := 'ZDg0OxxxxxxxxxxxxxxxxxxxxxxxxxxxxjM5Njhh';
BaseURL := 'https://api.plivo.com';
Resource := '/v1/Account/'+accountSid+'/Message/';
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
RESTClient := TRESTClient.Create(BaseURL);
try
RESTRequest := TRESTRequest.Create(RESTClient);
try
RESTResponse := TRESTResponse.Create(RESTClient);
try
HTTPBasicAuthenticator := THTTPBasicAuthenticator.Create('AC', 'c1234');
try
RESTRequest.ResetToDefaults;
RESTClient.BaseURL := BaseURL;
RESTRequest.Resource := Resource;
HTTPBasicAuthenticator.UserName := AccountSid;
HTTPBasicAuthenticator.Password := AuthToken;
RESTClient.Authenticator := HTTPBasicAuthenticator;
RESTRequest.Client := RESTClient;
RESTRequest.Response := RESTResponse;
// Tried this to fix plivo error with no luck!
// RESTClient.Params.AddHeader('Content-Type', 'application/json');
// "From" number is the send # setup in the twilio or plivo account. The "To" number is a verified number in your twilio or plivo account
if TypeCo='T' then // Twilio
SendSMS( '+1602xxxxx55','+1602xxxxxx7', 'This is a test text from Twilio') // x's were replaced to hide telephone numbers
else if TypeCo='P' then // Plivo
SendSMS( '1602xxxxx66','1602xxxxxx7', 'This is a test text from Plivo'); // x's were replaced to hide telephone numbers
finally
HTTPBasicAuthenticator.Free;
end;
finally
RESTResponse.Free;
end;
finally
RESTRequest.Free;
end;
finally
RESTClient.Free;
end;
end;
function TForm1.SendSMS(aFrom, aTo, aText: string): boolean;
begin
result := True;
RESTRequest.ResetToDefaults;
RESTClient.BaseURL := BaseURL;
RESTRequest.Resource := Resource;
if TypeCo='T' then // Twilio
begin
RESTRequest.Params.AddUrlSegment('AccountSid', accountSid);
RESTRequest.Params.AddItem('From', aFrom);
RESTRequest.Params.AddItem('To', aTo);
RESTRequest.Params.AddItem('Body', aText);
end
else if TypeCo='P' then // Plivo
begin
RESTRequest.Params.AddUrlSegment('AccountSid', accountSid);
RESTRequest.Params.AddItem('src', aFrom);
RESTRequest.Params.AddItem('dst', aTo);
RESTRequest.Params.AddItem('text', aText);
end;
RESTRequest.Method := rmPOST;
RESTRequest.Execute;
// Show Success or Error Message
ErrorMsg.Clear;
ErrorMsg.Lines.Text := RESTResponse.Content;
end;
As mentioned, the code above works fine for Twilio. However, for Plivo I'm getting the following error:
{
"api_id": "b124d512-b8b6-11e5-9861-22000ac69cc8",
"error": "use 'application/json' Content-Type and raw POST with json data"
}
I have been trying to determine how to fix this problem. I contacted Plivo support and I received the following response:
The error "use 'application/json' Content-Type and raw POST with json data" is generated when the Header "Content-Type" is not set the value "application/json"
Please add the Header Content-Type under Items in the Request Tab and set the Description as application/json.
I tried adding the code in the button procedure:
RESTClient.Params.AddHeader('Content-Type', 'application/json');
but it still gives the same error. I think this code is really close to working for Plivo. I'm a newbie at REST functions so I'm not sure what else to try. I have tried assigning "application/json" to just about everything that will accept it and still get the same error. Hopefully, someone else will have an idea of what will make Plivo work.
回答5:
Finally, I was able to get both Twilio and Plivo to send SMS messages. With the help of the Delphi REST Debugger and comments made in this thread, I was finally able to figure it out. Thank you! In the SendSMS function (for Plivo), instead of adding each parameter, I had to add a "Content-Type" header of "application/json" and add the parameters as RAW in the body using RESTRequest.AddBody(). One more note to make, I was originally testing this in Delphi XE5 and it would continue to give me the same error mentioned in an earlier post. When I tried it in Delphi XE7, it finally worked!!! This should work in Delphi 10 Seattle too, but haven't tested it!
Here is the working demo. On the form you need to have the following components:
Button1: TButton;
RESTClient: TRESTClient;
RESTRequest: TRESTRequest;
RESTResponse: TRESTResponse;
HTTPBasicAuthenticator: THTTPBasicAuthenticator;
ResponseMsg: TMemo;
In the OnCreate, change TypeCo from "T" or "P" depending on which company you want to test. Then run it!
Here is the code. Note, I masked out the AccountSid, AuthToken, and phone number fields for privacy.
procedure TForm1.FormCreate(Sender: TObject);
begin
// TypeCo
// = T = Twilio
// = P = Plivo
TypeCo := 'P';
if TypeCo='T' then // Twilio
begin
AccountSid := 'ACd2*************************06e38';
AuthToken := '24f63************************8ed';
BaseURL := 'https://api.twilio.com';
Resource := '/2010-04-01/Accounts/'+accountSid+'/Messages';
end
else if TypeCO='P' then // Plivo
begin
AccountSid := 'MAN*************IXYM';
AuthToken := 'ZDg0*******************************5Njhh';
BaseURL := 'https://api.plivo.com';
Resource := '/v1/Account/'+accountSid+'/Message/';
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var i:integer;
begin
RESTClient.ResetToDefaults;
RESTRequest.ResetToDefaults;
ResponseMsg.Clear;
RESTClient.BaseURL := BaseURL;
RESTRequest.Resource := Resource;
HTTPBasicAuthenticator.UserName := AccountSid;
HTTPBasicAuthenticator.Password := AuthToken;
RESTClient.Authenticator := HTTPBasicAuthenticator;
RESTRequest.Client := RESTClient;
RESTRequest.Response := RESTResponse;
// Here is where you can loop through your messages for different recipients.
// I use the same "To" phone number in this test
for i := 1 to 3 do
begin
if TypeCo='T' then // Twilio
SendSMS( '+1602******0','+1602******7', Format('This is test #%s using Twilio. Sent at %s',[inttostr(i),TimeToStr(Time)]))
else if TypeCo='P' then // Plivo
SendSMS( '1662******2','1602******7', Format('This is test #%s using Plivo. Sent at %s',[inttostr(i),TimeToStr(Time)]));
// Show Success or Error Message
ResponseMsg.Lines.Add(RESTResponse.Content);
ResponseMsg.Lines.Add('');
ResponseMsg.Refresh;
end;
end;
function TForm1.SendSMS(aFrom, aTo, aText: string):Boolean;
begin
result := False;
RESTRequest.Params.Clear;
RESTRequest.ClearBody;
RESTClient.BaseURL := BaseURL;
RESTRequest.Resource := Resource;
if TypeCo='T' then // Twilio
begin
RESTRequest.Params.AddUrlSegment('AccountSid', accountSid);
RESTRequest.Params.AddItem('From', aFrom);
RESTRequest.Params.AddItem('To', aTo);
RESTRequest.Params.AddItem('Body', aText);
end
else if TypeCo='P' then // Plivo
begin
// NOTE: The following Header line needs to be commented out for Delphi Seattle 10 Update 1 (or probably newer) to work. The first original Delphi 10 Seattle version would not work at all for Plivo. In this case the following error would occur: "REST request failed: Error adding header: (87) The parameter is incorrect". This was due to the HTTPBasicAuthenticator username and password character lengths adding up to greater than 56.
RESTRequest.Params.AddItem('Content-Type', 'application/json', pkHTTPHEADER, [], ctAPPLICATION_JSON);
// RESTRequest.Params.AddItem('body', '', pkRequestBody, [], ctAPPLICATION_JSON); // Thought maybe I needed this code, but works without it.
RESTRequest.AddBody(Format('{"src":"%s","dst":"%s","text":"%s"}',[aFrom,aTo,aText]),ctAPPLICATION_JSON);
end;
RESTRequest.Method := rmPOST;
RESTRequest.Execute;
result := True;
end;