I want to send a POST request in c# and i need the following sent through
"jsonrpc": "2.0",
"id": "12345",
"method": "my method",
"params": {
"api_key": "my api key",
"preset_id": "my preset id"
}
I tried using
using (WebClient client = new WebClient ())
{
byte [] response =
client.UploadValues ("my url", new NameValueCollection ()
{
{ "jsonrpc", "2.0" },
{ "id", "12345"},
{ "method", "my method"},
{ "params", ""}
});
string result = System.Text.Encoding.UTF8.GetString (response);
}
But i couldnt make the params an array, Please help, Thank you
It appears that you are asking for the parameters to be in an array, but they are actually shown as a "subclass". If the values were in an array, they should have square brackets around them.
However, both results are easy to achieve using anonymous (or real) classes (which I much prefer over embedding the property names in quoted text (makes future modifications much easier to implement).
var parameters = new
{
api_key = "my api key",
preset_id = "my preset id"
};
var json = new
{
jsonrpc = "2.0",
id = "12345",
method = "my method",
#params = parameters
};
string sResult = (new System.Web.Script.Serialization.JavaScriptSerializer()).Serialize(json);
The above code will result in the same output that you have shown. If you want an actual array instead, you can change the parameters definition to:
var parameters = new NameValueCollection();
parameters.Add("api_key", "my api key");
parameters.Add("preset_id", "my preset id");
Note that I used the .Net framework json serializer (from System.Web.Extensions), but you can use the serializer of your choice (we generally use NewtonSoft's JsonConvert).
Related
I am sending request to an api with HttpClient. The response from the request works without any problem, but I cannot parse the key values I want in the response. According to the research I have done, I tried such a code, but the incoming data returns empty in this way. How can I get the values I want from the incoming data?
using LoggerApi.Methods;
using System;
using System.Net.Http;
using System.Text;
using Newtonsoft.Json;
using System.Linq;
namespace LoggerApi.Methods
{
public class ApiMethods
{
public async static Task<object> GetClientInformations(string authenticationCode, string username = "username")
{
var client = new HttpClient();
var userInformationEndpoint = new Uri("https://myurl.com/url");
var userInformationPayload = new UserInformationPayload()
{
Login = username
};
var serializePayload = JsonConvert.SerializeObject(userInformationPayload);
var payload = new StringContent(serializePayload, Encoding.UTF8, "application/json");
var res = await client.PostAsync(userInformationEndpoint, payload).Result.Content.ReadAsStringAsync();
var responseResultJson = JsonConvert.DeserializeObject<object>(res);
return responseResultJson;
}
}
}
this code output is empty looks like this
{
"HasError": [],
"AlertType": [],
"AlertMessage": [],
"ModelErrors": [],
"Data": [
[
[]
],
[
[
[
[
[]
],
[
[]
],
[
[]
],
[
[]
]
]
]
]
]
}
But when I return var res directly instead of var responseResultJson from the function, the result is like this. What I want to do here is to access values such as Login, FirstName, LastName, Id from the incoming data. How can I do that?
{"HasError":false,"AlertType":"success","AlertMessage":"Operation has completed successfully","ModelErrors":[],"Data":{"Count":1,"Objects":[{"Id":291031530,"CurrencyId":"TRY","FirstName":"Scott","LastName":"Marshall","MiddleName":"William","Login":"scotty3"}]}}
There are multiple possible solutions. Once of them is the following.
using System.Net.Http.Json;
using System.Text.Json;
namespace Program
{
class Program
{
// We need this to create a new ToDo
record TodoDto(int UserId, string Title, string Body);
// That's what's stored in the backend service and returned by the API
record TodoModel(int Id, int UserId, string Title, string Body);
public static async Task Main(string[] args)
{
// HTTP Client
var httpClient = new HttpClient();
// required as Properties are PascalCase in C# but JSON properties here are camelCase (that's usually the case)
var serializerOptions = new JsonSerializerOptions {
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
// create new todo
var newTodo = new TodoDto(1, "My new todo", "I need to...");
// Add todo in the backend
var response = await httpClient.PostAsJsonAsync<TodoDto>("https://jsonplaceholder.typicode.com/posts", newTodo, serializerOptions);
if(response.IsSuccessStatusCode){
// if we have a succesful request we can deserialize the response
var newTodoModel = JsonSerializer.DeserializeAsync<TodoModel>(await response.Content.ReadAsStreamAsync(), serializerOptions);
Console.WriteLine(newTodoModel);
}
else
{
Console.WriteLine($"Request failed with status code {(int)response.StatusCode} ({response.StatusCode})");
}
}
}
Some remarks:
I am using JsonPlaceholder for some sample API endpoints to provide a working example here
Deserializing the API response to JSON could fail e.g. when the API returns something unexpected, so you should check for that especially if you don't control the API that you're calling.
For this to work you need to include the System.Net.Http.Json namespace at it contains the PostAsJsonAsync() extension method (see HttpClienJsonExtensions Class - Microsoft Learn for more nice and convenient extension methods).
There is no need for Newtonsoft JSON parser, as you can now use the built-in JSON Parser in the System.Text.Json namespace.
I prefer working with Streams as you can then use async all the way throughout your code which is a best practice (see Async best practices - Async All the Way)
By default a case-sensitive matching between your C# class and the JSON will be attempted. JSON usually uses camelCase and this API does so as well. Therefore we need to tell the (de-)serializer to use camelCase as well using JsonSerializerOptions.
I have a basic Pulumi build for keycloak where I set up a realm, create a scope, create a client, and update teh scopes for my client.
class RealmBuild : Stack
{
public RealmBuild()
{
var realm = new Realm("ExampleRealm-realm", new RealmArgs
{
RealmName = "ExampleRealm"
});
var recipemanagementScope = ScopeFactory.CreateScope(realm.Id, "recipe_management");
var recipeManagementPostmanMachineClient = ClientFactory.CreateClientCredentialsFlowClient(realm.Id,
"recipe_management.postman.machine",
"974d6f71-d41b-4601-9a7a-a33084484682",
"RecipeManagement Postman Machine",
"https://oauth.pstmn.io");
recipeManagementPostmanMachineClient.ExtendDefaultScopes(recipemanagementScope.Name);
}
}
public static class ClientExtensions
{
public static void ExtendDefaultScopes(this Client client, params Output<string>[] scopeNames)
{
var defaultScopeName = $"default-scopes-for-{client.Name.Apply(x => x)}";
var defaultScopes = new ClientDefaultScopes(defaultScopeName, new ClientDefaultScopesArgs()
{
RealmId = client.RealmId,
ClientId = client.Id,
DefaultScopes =
{
"openid",
"profile",
"email",
"roles",
"web-origins",
scopeNames,
},
});
}
}
public class ClientFactory
{
public static Client CreateClientCredentialsFlowClient(Output<string> realmId,
string clientId,
string clientSecret,
string clientName,
string baseUrl)
{
return new Client($"{clientName.ToLower()}-client", new ClientArgs()
{
RealmId = realmId,
ClientId = clientId,
Name = clientName,
StandardFlowEnabled = false,
Enabled = true,
ServiceAccountsEnabled = true,
AccessType = "CONFIDENTIAL",
BaseUrl = baseUrl,
AdminUrl = baseUrl,
ClientSecret = clientSecret,
BackchannelLogoutSessionRequired = true,
BackchannelLogoutUrl = baseUrl
});
}
}
The problem is, I am getting this error around my scopes:
Diagnostics:
keycloak:openid:ClientDefaultScopes (default-scopes-for-Calling [ToString] on an [Output<T>] is not supported.
To get the value of an Output<T> as an Output<string> consider:
1. o.Apply(v => $"prefix{v}suffix")
2. Output.Format($"prefix{hostname}suffix");
See https://pulumi.io/help/outputs for more details.
This function may throw in a future version of Pulumi.):
error: Duplicate resource URN 'urn:pulumi:dev::KeycloakPulumiStack::keycloak:openid/clientDefaultScopes:ClientDefaultScopes::default-scopes-for-Calling [ToString] on an [Output<T>] is not supported.
To get the value of an Output<T> as an Output<string> consider:
1. o.Apply(v => $"prefix{v}suffix")
2. Output.Format($"prefix{hostname}suffix");
See https://pulumi.io/help/outputs for more details.
This function may throw in a future version of Pulumi.'; try giving it a unique name
I tried something like this as well var defaultScopeName = Output.Format($"default-scopes-for-{client.Name}");, but I can't pass that into the name for ClientDefaultScopes
I did look at the docs to see if anything stuck out as an issue, but I'm clearly missing something.
Rule number 1 with Pulumi outputs: Anything you return from an apply() will still be an Output, even if it looks like it should be a string.
In other words, on this line of code:
var defaultScopeName = $"default-scopes-for-{client.Name.Apply(x => x)}";
defaultScopeName is Output<string>.
However, the x variable in the lambda is in fact a string rather than an output.
The other item to note is that the name of a resource (so the first argument) cannot be an Output. So in your code:
var defaultScopeName = $"default-scopes-for-{client.Name.Apply(x => x)}";
var defaultScopes = new ClientDefaultScopes(defaultScopeName, new ClientDefaultScopesArgs()
{
RealmId = client.RealmId,
ClientId = client.Id,
DefaultScopes =
{
"openid",
"profile",
"email",
"roles",
"web-origins",
scopeNames,
},
});
because defaultScopeName is an Output, this won't work.
You could create the resource inside of the apply():
var defaultScopea = $"default-scopes-for-{client.Name.Apply(x =>
return new ClientDefaultScopes(x, new ClientDefaultScopesArgs()
{
RealmId = client.RealmId,
ClientId = client.Id,
DefaultScopes =
{
"openid",
"profile",
"email",
"roles",
"web-origins",
scopeNames,
},
});
)}";
however, this may mean that the resource won't appear in any previews (see the note in the Apply section of the Inputs and Outputs page in the Pulumi docs).
So what's the answer here? it looks like you're setting the ClientName to be a string value earlier in the code, so I'd use the same variable that you're setting there.
You can't mix and match string and Output<string> values. Instead, you need to transform any output and append your static list to the list of resolved values:
var defaultScopeName = Output.Format($"default-scopes-for-{client.Name}");
var defaultScopes = new ClientDefaultScopes("some-scope-name", new ClientDefaultScopesArgs()
{
RealmId = client.RealmId,
ClientId = client.Id,
DefaultScopes = Output.All(scopeNames).Apply(names =>
new[] { "openid", "profile", "email", "roles", "web-origins", }
.Concat(names)),
});
Note that Output.Format is used for string formatting, Output.All is used to convert to Output<string[]> and .Apply is used to transform the array. You can learn more in Inputs and Outputs.
Currently, Pulumi only supports string types for the name of a resource.
Since
var defaultScopeName = $"default-scopes-for-{client.Name.Apply(x => x)}";
is using an output of a resource, defaultScopeName is type Output<string> and can't be used for the resource name in the line,
var defaultScopes = new ClientDefaultScopes(defaultScopeName, new ClientDefaultScopesArgs()
If I'm reading the code correctly, you specify clientName and use it to set client.Name. So, I would just pass in clientName and use that instead of client.Name. And, that should work since it's a basic type all the way through.
I'm struggling with creating a message from a device to the IotHub in the correct format.
I'm using the Azure Client SDK (Microsoft.Azure.Devices.Client)
For better understanding lets start with a small example, we have the following string:
var TableName = "table01";
var PartitionKey = "key01";
string messagePayload = $"{{\"tablename\":\"{TableName}\",\"partitionkey\":\"{PartitionKey}\"}}";
( Taken from the example Send device to cloud telemetry) we create an eventMessage
using var eventMessage = new Microsoft.Azure.Devices.Client.Message(Encoding.UTF8.GetBytes(messagePayload))
{
ContentEncoding = Encoding.UTF8.ToString(),
ContentType = "application/json"
};
And then send it to the Cloud:
Console.WriteLine(messagePayload);
await deviceClient.SendEventAsync(eventMessage);
Output from the writeline, which is what I wanted in the first place:
{"tablename":"table01","partitionkey":"key01"}
What I can see in the shell after following the answer about watching incoming IotHub Messages:
{
"event": {
"origin": "WinSensorTest",
"module": "",
"interface": "",
"component": "",
"payload": "{\"tablename\":\"table01\",\"partitionkey\":\"key01\"}"
}
}
The Problem is, that I want it to either look like the code below or completely without the "event" etc, just the string above.
{
"event":{
"origin":"WinSensorTest",
"module":"",
"interface":"",
"component":"",
"payload":{
"tablename":"table01",
"partitionkey":"key01"
}
}
}
Where did I go wrong, how can the payload be correct json format?
Edit:
I just tried the same in Java, with the same result. Why does this not work, or is the data seen in the shell not correctly parsed?
If you create a proper Json object first it works and also shows up correct in the shell - interestingly only for this c# project, I tried doing the same in Java on Android and the same wierd formatting stuff still happens even after making an object with gson.
For the solution:
class JsonMessage
{
public string tablename { get; set; }
public string partitionkey { get; set; }
}
And then Used JsonMessage and JsonConvert to get the desired payload.
JsonMessage newMsg = new JsonMessage()
{
tablename = "table01",
partitionkey = "key01",
};
string payload = JsonConvert.SerializeObject(newMsg);
using var eventMessage = new Microsoft.Azure.Devices.Client.Message(Encoding.UTF8.GetBytes(payload))
{
ContentEncoding = Encoding.UTF8.ToString(),
ContentType = "application/json"
};
I am trying to send selenium tests to saucelabs using the DriverOptions class. According to this link, you need a sauce:options configuration, and according to this post a Dictionary will do. Here is my setup:
DriverOptions options = new ChromeOptions
{
PlatformName = "Windows 10",
BrowserVersion = "latest"
};
IDictionary<string, string> sauceOptions = new Dictionary<string, string>
{
{ "username", SauceUsername },
{ "accessKey", SauceAccessKey },
{ "name", TestContext.TestName },
{ "seleniumVersion", "3.11.0" }
};
options.AddAdditionalCapability("sauce:options", sauceOptions);
_driver = new RemoteWebDriver(new Uri("http://#ondemand.saucelabs.com:80/wd/hub"),
options.ToCapabilities(), TimeSpan.FromSeconds(600));
I get a WebDriverException on the RemoteWebDriverinit, saying Misconfigured -- Sauce Labs Authentication Error. You used username 'None' and access key 'None' to authenticate. This is weird because
I am given back the desired caps I used, which were:
The following desired capabilities were received:
{'browserName': 'chrome',
'browserVersion': 'latest',
'goog:chromeOptions': {'sauce:options': {'accessKey': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXX163edf42',
'name': 'DriverOptionsTest',
'seleniumVersion': '3.11.0',
'username': 'kroe761'}},
'platformName': 'Windows 10'}
The last few digits of my accesskey are right and that is my username, so clearly I sent the correct credentials
If I remove the dictionary and pass the username and accesskey directly into the RemoteDriver uri (http://{SauceUsername}:{SauceAccessKey}#ondemand...) it works, However, I can't pass in any other sauce options.
Thanks!
Use the AddAdditionalCapability overload that takes three arguments, not two. This tells the ChromeOptions instance to add the dictionary to the top-level of the JSON payload, rather than as part of the goog:chromeOptions property. Here is what that would look like:
// Note, you must use the specific class here, rather than the
// base class, as the base class does not have the proper method
// overload. Also, the UseSpecCompliantProtocol property is required.
ChromeOptions options = new ChromeOptions
{
PlatformName = "Windows 10",
BrowserVersion = "latest",
UseSpecCompliantProtocol = true
};
Dictionary<string, object> sauceOptions = new Dictionary<string, object>
{
{ "username", SauceUsername },
{ "accessKey", SauceAccessKey },
{ "name", TestContext.TestName },
{ "seleniumVersion", "3.11.0" }
};
options.AddAdditionalCapability("sauce:options", sauceOptions, true);
_driver = new RemoteWebDriver(new Uri("http://ondemand.saucelabs.com:80/wd/hub"),
options.ToCapabilities(), TimeSpan.FromSeconds(600));
I am trying to implement the send message functionality using third party api. API-https://api.txtlocal.com/send/
But when we are testing the implementation, we are facing an issue with error code 3 and giving a message as "invalid user details."
C# code:
string UserId = "1234";
String message = HttpUtility.UrlEncode("OTP");
using (var wb = new WebClient())
{
byte[] response = wb.UploadValues("https://api.txtlocal.com/send/", new NameValueCollection()
{
{"username" , "<TextLocal UserName>"},
{"hash" , "<API has key>"},
{"sender" , "<Unique sender ID>"},
{"numbers" , "<receiver number>"},
{"message" , "Text message"}
});
string result = System.Text.Encoding.UTF8.GetString(response);
//return result;
Error Details:
{
"errors": [{
"code": 3,
"message": "Invalid login details"
}],
"status": "failure"
}
Even though I am passing valid credentials.
Please assist me and let me know in case you require any more details.
Thanks and appreciate your help in advance.
The documentation for the API states that you should pass your parameter values either in the header for POST requests or in the url for GET requests. WebClient.UploadValue does a POST per default, but you don't set the header accordingly. So no credentials are found.
You could try to use the WebClient.UploadValues(name, method, values) overload and specify GET as method.
NameValueCollection values = ...;
byte[] response = wb.UploadValues("https://api.txtlocal.com/send/", "GET", values);
I believe you should either send the API Key OR the username and password.
Remove the username from your request and just leave the API key, sender, numbers and message. All should work OK then.
This is what worked for me:
[HttpGet]
public async Task<JObject> SendOtp(string number)
{
using (var client = _httpClientFactory.CreateClient())
{
client.BaseAddress = new Uri("https://api.textlocal.in/");
client.DefaultRequestHeaders.Add("accept","application/json");
var query = HttpUtility.ParseQueryString(string.Empty);
query["apikey"] = ".....";
query["numbers"] = ".....";
query["message"] = ".....";
var response = await client.GetAsync("send?"+query);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return JObject.Parse(content);
}
}
its bit late ...... Try replacing {"hash" , ""} with {"apikey" , ""}