correct method PUT WebRequest() in MQL4 - mql4

I attempted to update a JSON at myjson.com, using the WebRequest(), resulting in a failure.
return result: 2018.05.22 23:54:44.401 webreq NZDCAD,H1: Status code: 404, error: 4000
I want to change name from "John" to "Labu"
My code is like the following:
void postJson()
{
char post[], result[];
string headers = "Content-Type: application/json\r\n";
string obj = "'data:{\"name\":\"Labu\"}'";
StringToCharArray( obj, post, 0, WHOLE_ARRAY ); // Must specify string length;
// otherwise array has
// terminating null character in it
int res = WebRequest( "POST",
"https://api.myjson.com/bins/tj8e2",
"PUT",
NULL,
10000,
post,
ArraySize( post ),
result,
headers
);
Print( "Status code: " , res, ", error: ", GetLastError() );
Print( "Server response: ", CharArrayToString( result ) );
}

string obj="'data:{\"name\":\"Labu\"}'";
you do not need the '-chars
you must make sure that the address is valid and that it is added to the list of allowed web sites ( inside the MetaTrader Terminal configuration setup panel ).

remove POST and put PUT,
read your own code: WebRequest( "POST",
should be, WebRequest( "PUT",
You are using POST method still.
Here:
int WebRequest(
const string method, // HTTP method
const string url, // URL
const string cookie, // cookie
const string referer, // referer
int timeout, // timeout
const char &data[], // the array of the HTTP message body
int data_size, // data[] array size in bytes
char &result[], // an array containing server response data
string &result_headers // headers of server response
);
Now would be a good time to also mention mt4 don't support PUT or DELETE only POST and GET ;)

Related

How to get a value from SecretString returned from AwsCustomResource (service: 'SecretsManager', action: 'getSecretValue') as plain text?

I'm modifying the code from this workshop to do cross-account rather than cross-region.
https://cdk-advanced.workshop.aws/start.html
The first thing I did was install and configure cdk-assume-role-credential-plugin and bootstrapped.
In the workshop they use a AwsCustomResource to get around the cross-region limitation of StringParameter.valueFromLookup.
static valueFromLookup(scope, parameterName) Requires that the stack this scope is defined in will have explicit account/region information. Otherwise, it will fail during synthesis.Using an AwsCustomResource allows them to override the region from stack.
const targetKeyLookupCR = new cr.AwsCustomResource(this, 'TargetKeyLookup', {
onUpdate: { // will also be called for a CREATE event
service: 'SSM',
action: 'getParameter',
parameters: {
Name: props.targetKeyIdSsmParameterName
},
region: props.targetRegion,
physicalResourceId: cr.PhysicalResourceId.of(Date.now().toString())
},
policy: cr.AwsCustomResourcePolicy.fromSdkCalls({resources: [parameterArn]})
});
Unfortunately you cannot override account of stack in a similar fashion. Adding an assumedRole does not help either. It always looks for parameter in stack's account.
I tried to use SecretsManager & AwsCustomResource to get around that cross-account limitation:
const targetKeyLookupCR = new cr.AwsCustomResource(this, 'TargetKeyLookup', {
onUpdate: {
service: 'SecretsManager',
action: 'getSecretValue',
parameters: {
SecretId: props.targetKeyIdSsmParameterName
},
assumedRoleArn: 'arn:aws:iam::111111111111:role/MultiRegionS3CrrKmsCmkXacct',
region: props.targetRegion,
physicalResourceId: cr.PhysicalResourceId.of(Date.now().toString())
},
policy: cr.AwsCustomResourcePolicy.fromSdkCalls({resources: [parameterArn]})
});
const secretString = targetKeyLookupCR.getResponseField(dataPath);
The code above returns a string that looks like this.
"{"password":"/];asdf(5;{ASDF=L%UuVxasDFHg`(:l","MyKeyID":"arn:aws:kms:us-west-1: 111111111111:key/1111a22b-3c44-5ddd-66e7-f8f9999a1111"}"
I can see it in CloudWatch:
2021-08-14T04:34:15.641Z c5ae47f7-1e4a-43a3-a475-d0d16ea7e1be INFO Responding {"Status":"SUCCESS","Reason":"OK",...
"SecretString":"{"password":"/];asdf(5;{ASDF=L%UuVxasDFHg`(:l","MyKeyID":"arn:aws:kms:us-west-1: 111111111111:key/1111a22b-3c44-5ddd-66e7-f8f9999a1111}"...}
In the workshop they use the response directly:
role.addToPolicy(new iam.PolicyStatement({
resources: [targetKeyLookupCR.getResponseField('Parameter.Value')],
actions: ['kms:Encrypt'] }));
To get the Value of MyKeyID, I've tried...
JSON.parse and splitting the string. No matter what I try ends up with an error like 'Unexpected token $ in JSON at position 0' or 'TypeError: Cannot read property 'split' of undefined'
This led me to https://docs.aws.amazon.com/cdk/latest/guide/tokens.html#tokens_json, which I definitely thought was going to be the answer! It was not. More 'Unexpected token $ in JSON at position 0' errors.
const stack = cdk.Stack.of(this);
const secretString = targetKeyLookupCR.getResponseField('SecretString');
const jsonString = stack.toJsonString(secretString);
const jsonObj = JSON.parse(jsonString);
Update:
From this https://docs.aws.amazon.com/cdk/api/latest/docs/core-readme.html#cfnjson I got a different error that indicates it has the right string, but just can't parse it properly. Unexpected token p in JSON at position 3 at JSON.parse ().
const secretString = targetKeyLookupCR.getResponseField('SecretString');
const cfnJson = new CfnJson(this, 'cfn-json', {
value: secretString,
});
// #ts-ignore
const myKeyId = cfnJson.MyKeyID || '*';
This is because CfnJson adds quotes around string:
"Value": {
"Fn::Join": [
"",
[
"\"",
{
"Fn::GetAtt": [
"MySourceTargetKeyLookupACF65EAE",
"SecretString"
]
},
"\""
]
]
}
I'm not sure how to proceed. How would I get the ARN from secretString as plain text?
I guess I was looking for a more elegant solution, but this works...
const secretString = targetKeyLookupCR.getResponseField('SecretString');
const myKeyId = cdk.Fn.select(7,cdk.Fn.split('"', secretString));
It splits the secretString into an array using double quote as delimiter
cdk.Fn.split('"', secretString)
and selects the 7th item from array produced by split
cdk.Fn.select(7, <array>)

Flutter Dio doesn't accept int value as queryParameters

I'm making a call using Dio that send some query parameters, using map constructor
response = await Dio().get(url, queryParameters: {
"apikey": publicKey,
"hash": hash,
"ts": timestamp,
"nameStartsWith": searchTerm
});
Everything works correctly, but when try to send a int value, Dio throw a error
response = await Dio().get(url, queryParameters: {
"apikey": publicKey,
"hash": hash,
"ts": timestamp,
"nameStartsWith": searchTerm,
"page" : 10
});
type 'int' is not a subtype of type 'Iterable < dynamic > '#0
And i can't just convert the int value to string, because api is expcting int type.
Any help ?
There is no such thing as an int type in an URL and therefore in query parameters.
An URL can only be a String.
Just convert it to String and be done.

Flutter - Handle status code 302 in POST request

I'm trying to send a post request in Flutter with DIO package.
Here is the request:
getSessionId() async {
var csrf = await getCsrftoken();
var dio = new Dio(new Options(
baseUrl: "http://xxxxxxx/accounts/login/",
connectTimeout: 5000,
receiveTimeout: 100000,
// 5s
headers: {
'Cookie': "csrftoken=" + csrf
},
contentType: ContentType.JSON,
// Transform the response data to a String encoded with UTF8.
// The default value is [ResponseType.JSON].
responseType: ResponseType.PLAIN
));
var response;
response = await dio.post("http://xxxxxxx/accounts/login/",
data: {
"username": "xxxxx",
"password": "xxxxx",
"csrfmiddlewaretoken" : csrf
},
options: new Options(
contentType: ContentType.parse("application/x-www-form-urlencoded")),
);
print("StatusCode: ");
print(response.statusCode);
print("Response cookie: "); //THESE ARE NOT PRINTED
print(response.headers);
}
After the request i get:
E/flutter ( 4567): [ERROR:flutter/shell/common/shell.cc(181)] Dart Error: Unhandled exception:
E/flutter ( 4567): DioError [DioErrorType.RESPONSE]: Http status error [302]
E/flutter ( 4567): #0 getSessionId (file:///C:/get_order/lib/main.dart:36:14)
E/flutter ( 4567): <asynchronous suspension>
From this request i only need to get the sessionid cookie, but the function stop with unhandled exception.
I solved this way:
Add followRedirects: false and validateStatus: (status) { return status < 500;} to the request. Like this:
var response = await Dio().post("http://myurl",
data: requestBody,
options: Options(
followRedirects: false,
validateStatus: (status) { return status < 500; }
),
);
This way you can get from the 302 every headers and other.
The Dart HTTP client won't follow redirects for POSTs unless the response code is 303. It follows 302 redirects for GET or HEAD.
You could see if you can stop the server sending the redirect in response to a (presumably) valid login request, and send a 200 instead.
Or you could try sending the login request as a GET by encoding the form fields into the URL, for example:
http://xxxxxxx/accounts/login/?username=xxxx&password=yyyy&csrfmiddlewaretoken=zzzz
You would have to URL encode any special characters in the parameters. Presumably, you'll want to use HTTPS too.
Finally, is the URL meant to end with /? It might be worth trying /accounts/login.
i got a similar problem and i solved it with adding header with "Accept":"application/json" . henceforth it will only return json data otherwise it will prompt to redirect with html url.
Redirections for 302 are made in response to GET or HEAD requests, never for POST. Sometimes server sends 302 in response to POST (that was in my case). In this case Dio throws exception you can catch - remember to check if server status code is 302 or maybe it's another error.
try{
await dio.post( _urlLogin,
data:{...},
options: Options(
contentType: ContentType.parse("application/x-www-form-urlencoded"),
)
);
}on DioError catch(error){
if(error.response.statusCode == 302){
// do your stuff here
}
I'm trying to use this to a webscraping... don't ask me why lol. I came from python/golang and I've already tried the http package, however i recommend you to use the dio package.
With dio I'm doing the following:
Scrapers.client = Dio();
// create a local cookie to handle with sites that force you to use it
var cookieJar = CookieJar();
// assign middlewares to be "session like"
Scrapers.client?.interceptors.add(CookieManager(cookieJar));
// assign a logging middleware
Scrapers.client?.interceptors.add(LogInterceptor(responseBody: false));
// set the default content type to this client
Scrapers.client?.options.contentType = Headers.formUrlEncodedContentType;
...
static Future<Response> handleRedirects(Response r, int statusCode) async {
var redirects = 0;
while (r.statusCode == statusCode) {
print("redirect #${redirects++}");
final redirecturl = r.headers['location']![0];
r = await Scrapers.client!.post(redirecturl,
options: Options(
followRedirects: false,
validateStatus: (status) {
return status! < 500;
}));
}
return r;
}
...
Response r = await Scrapers.client!.post(url,
data: payload,
options: Options(
followRedirects: false,
validateStatus: (status) {
return status! < 500;
}));
r = await Scrapers.handleRedirects(r, 302);
Note that it's just a simple approach. You can change it according with you needs.
in my case, this problem was solved by send the cookie with the header in the post method
and the problem is the API was response to me with HTML login page rather than JSON data.
and you will find the cookie key in the response header when you perform a si/log - in
and the status error code was 302

HttpResponseMessage ReasonPhrase max length?

I have this code:
public void Put(int id, DistributionRuleModelListItem model)
{
CommonResultModel pre = new BLL.DistributionRules().Save(id, model, true);
if(!pre.success){
DAL.DBManager.DestroyContext();
var resp = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(string.Format("Internal server error for distruleId: {0}", id)),
ReasonPhrase = pre.message.Replace(Environment.NewLine, " ")//.Substring(0,400)
};
throw new HttpResponseException(resp);
}
}
There is logic that can set the value of pre.message to be an exception.ToString() and if it is too long i receive the following application exception:
Specified argument was out of the range of valid values. Parameter
name: value
But if I uncomment .Substring(0,400) everything works fine and on client side I receive the correct response and it is possible to show it to the user.
What is the max length of ReasonPhrase? I can't find any documentation that specifies this value.
I couldn't find the max value documented anywhere, however through trial and error, I found it to have a maximum length of 512 bytes.

Delete entry from database with WinJS and OData

I'm trying to delete an entry from the database by odata. I get the error message
{"error":{"code":"","message":{"lang":"en-US","value":"Bad Request - Error in query syntax."}}}
my code:
function deleteMonthEntry() {
var item = actMonthEntries.getItem(listIndex);
var queryString = "Stundens(" + item.data.datensatz_id + ")?$format=json";
var requestUrl = serviceUrl + queryString;
WinJS.xhr({
type: "delete",
url: requestUrl,
headers: {
"Content-type": "application/json"
}
}).done(
function complete(response) {
},
function (error) {
console.log(error);
}
);
}
My request URL looks like this:
requestUrl = "http://localhost:51893/TimeSheetWebservice.svc/Stundens(305233)?$format=json"
Thanks
Marlowe
At least I found the solution:
I've entered an filter request to my service like this:
TimeSheetWebservice.svc/Stundens?$filter=datensatz_id eq 305221
this returned the correct entry with this link:
TimeSheetWebservice.svc/Stundens(305221M)
So if I enter a M after the ID, everything works fin. But I have no idea where this M comes from.
Can anyone tell me the reason for this M? It does not belong to the ID. The ID is this
305221
Marlowe
Are you sure the server you're talking to supports the $format query option? Many don't. I would try removing that part of the request URI, and instead modify your headers value to specify an Accept header:
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}
For servers where $format is allowed, giving it a json value is equivalent to providing an Accept header with the application/json MIME type.
In general, for a DELETE operation, the Accept header or $format value only matters for error cases. With a successful DELETE, the response payload body will be empty, so there's no need for the server to know about your format preference.

Resources