ASP.NET Web APi - Passing an object as parameter - c#

MakeUser method in the User controller for creating a username and password.
[HttpGet]
public string MakeUser(UserParameters p)
{
const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
string pass = "";
Random r = new Random();
for (int i = 0; i < p.Number; i++)
{
pass += chars[r.Next(0, 62)];
}
string firstTwoo = p.Name.Substring(0, 2);
string firstThree = p.Surname.Substring(0, 3);
return "Your username is: " + firstTwoo + firstThree + "\nYour password is: " + pass;
}
UserParameter class for sending the parameters as an object.
public class UserParameters
{
public int Number { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
RunAsync method in console client. Can i pass an object with Get method? If yes what is my mistake here? Thank you!
static async Task RunAsync()
{
using (var client = new HttpClient())
{
var p = new UserParameters();
Console.Write("Your username: ");
p.Name = Console.ReadLine();
Console.Write("Your surname: ");
p.Surname = Console.ReadLine();
Console.Write("Please type a number between 5 and 10: ");
p.Number = int.Parse(Console.ReadLine());
client.BaseAddress = new Uri("http://localhost:4688/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//HTTP GET
HttpResponseMessage response = await client.GetAsync("api/user?p=" + p);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsAsync<UserParameters>();
Console.WriteLine("\n*****************************\n\n" + result);
}
}
}

GET requests don't support you passing objects in this way. The only option is to do it as a query string param as others have already demonstrated. From a design perspective, since you are creating a new resource it makes much more sense for this to be a POST or PUT request which both allow for an actual payload to be sent along with the request.
[HttpPost]
public string MakeUser([FromBody]UserParameters p)
{
...
}
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
var response = await client.PostAsJsonAsync(new Uri("http://localhost:4688/"), p);
// do something with response

Your variable p cannot be passed as query string parameter like how you have it. To populate the url and query strings the way you prefer, you would have to write out the rest of the query string and access the object's properties while building the string up.
string queryString = "api/user?name="+p.Name+"&surname="+p.Surname+"&number="+p.Number;
HttpResponseMessage response = await client.GetAsync(queryString);
The MakeUser() method will need to look similar to something below:
[HttpGet]
public string MakeUser(string name, string surname, int number)
{
}
I am not seeing however where you are calling the MakeUser() method. Perhaps in the query string parameter you need to make it 'api/makeuser?'.

You CAN pass the p parameter like you want to, it's perfectly fine, take a look at the FromUri paragraph here where an object is used as a parameter:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
The method takes an object as a parameter, not the indivudual members. You call it by specifying the members though.

Related

Binance API place order

How do I place an order for futures? I am getting two errors: "The required timestamp parameter was not sent, was empty / null, or is not well formed." OR "The signature for this request is not valid."
public static async void Order()
{
string base_uri = "https://fapi.binance.com/fapi/v1/order?";
string API_Key = "bSQQlu2k5tf0oSUGZsNGptisIxXLux8wb............................";
string Secret_Key = "gWPKP66geFL0ryijnlU3TTepS61.............................";
string symbol = "XRPUSDT";
string side = "BUY";
string type = "MARKET";
string timeInForce = "GTC";
decimal quantity = 20;
long recvWindow = 5000;
long timestamp = GetServerTime();
string queryString = "symbol=" + symbol + "&side=" + side + "type=" + type + "&timeInForce=" + timeInForce;
string signature = HMACHASH(queryString, Secret_Key);
var Payload = new Credentials
{
Quantity = quantity,
RecvWindow = recvWindow,
Timestamp = timestamp,
Signature = signature
};
var stringPayload = JsonConvert.SerializeObject(Payload);
var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
httpContent.Headers.Add("X-MBX-APIKEY", API_Key);
using (var httpClient = new HttpClient())
{
var httpResponse = await httpClient.PostAsync(base_uri + queryString, httpContent);
if (httpResponse.Content != null)
{
var responseContent = await httpResponse.Content.ReadAsStringAsync();
Console.WriteLine(responseContent);
}
}
}
This is how I get the timestamp
public static long GetServerTime()
{
string str = BinanceResponse("https://fapi.binance.com/fapi/v1/time");
string[] arr = str.Split('\"');
str = arr[2].Trim(':', '}');
return long.Parse(str);
}
Credentials class
internal class Credentials
{
[JsonProperty("quantity")]
public decimal Quantity { get; set; }
[JsonProperty("recvWindow")]
public long RecvWindow { get; set; }
[JsonProperty("timestamp")]
public long Timestamp { get; set; }
[JsonProperty("signature")]
public string Signature { get; set; }
}
After serialization
stringPayload = "{"quantity":20.0,"recvWindow":5000,"timestamp":1625061703897,"signature":"2794e66d4e5b5b6338782e058747a567db523.........................."}"
If I try like this:
string queryString = "symbol=" + symbol + "&side=" + side + "&type=" + type +
"&timeInForce=" + timeInForce + "&quantity=" + quantity + "&recvWindow=" +
recvWindow + "&timestamp=" + timestamp;
string signature = HMACHASH(queryString, Secret_Key);
queryString += "&signature=" + signature;
Error: "The signature for this request is not valid."
Resolved!
Thank you guys! I used Fiddler and found out that the "timeInForce" parameter is not needed for a type = "MARKET". All the problems were because of him.
string queryString = "symbol=" + symbol + "&side=" + side + "&type=" + type +
̶"̶&̶t̶i̶m̶e̶I̶n̶F̶o̶r̶c̶e̶=̶"̶ ̶+̶ ̶t̶i̶m̶e̶I̶n̶F̶o̶r̶c̶e̶ + "&quantity=" + quantity + "&recvWindow=" +
recvWindow + "&timestamp=" + timestamp;
I highly recommend the Binance Postman collection on GitHub to see how to structure your requests:
Binance Postman Collection
Following this I also recommend the Binance Signature Examples found here: Binance Signature Examples
It looks like your signature is being generated without including all of the parameters of the request.
Binance supports setting your parameters for a post request either in the body or in the URL. Personally I've only used everything in the URL, but the signature has to verify all of the parameters, whereas your queryString variable is being converted into a signature, but other data is being sent in the payload afterwards and is not included in the signature.
The response is in the error report. The Binance API require you to send an timestamp.
So you are probably not sending a correct timestamp or not correctly name it.
You can check your requests with an http sniffer like Fiddler.
It's possible that the API is case sensitive so, timestamp should not be "Timestamp" after the serialization. Check it
EDIT: Can you provide the documentation that you used to create the request ? Because the official binance API is asking for POST parameters only

How do I use the returned variable from a method in another class in c#?

I have a method that returns a long and I want to be able to use it outside said method but I can't seem to figure this out... A little help?
Here's the method:
using System;
using System.IdentityModel.Tokens.Jwt;
namespace API
{
public class UserIDClass
{
protected string responseHeader = "";
protected long id = 0;
public long idMethod(string responseHeader, long id)
{
string[] bearerToken = responseHeader.Split(" ");
var txtJwtIn = bearerToken[1];
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(txtJwtIn);
var decodedToken = handler.ReadToken(txtJwtIn) as JwtSecurityToken;
var sub = decodedToken.Subject;
string[] subSplit = sub.Split("|");
long goodId = Int64.Parse(subSplit[1]);
if (goodId == id)
{
Console.WriteLine("goodId "+ goodId + " = id " + id);
}
else {
Console.WriteLine("goodId = " + goodId + " but the id you searched was " + id);
return goodId;
}
}}
and here's the code I'm instantiating the method in:
[HttpGet("{id}")]
[Authorize]
public async Task<ActionResult<User>> Get(long id)
{
var responseHeader = Request.Headers["Authorization"].FirstOrDefault();
UserIDClass useridclass = new UserIDClass();
useridclass.idMethod(responseHeader, id);
var User = await _context.Users.FindAsync(id);
Console.WriteLine(goodId);
return Ok(User);
}
I want to be able to use the goodId long outside of that method but when I try to write it to the console in the get method I get "the name does not exist in your current context". I feel Like I'm missing something super simple here. I'm still learning so be ever so gentle :)
From what I can gather with your code, it looks like when you instantiating the method, you should create a long variable that holds the output of the method idMethod. What it looks like is you are trying to access the long variable "goodId" but it's out of scope. I would try something like this.
long id = useridclass.idMethod(responseHeader, id);

Passing a message back with response code via class

I am using a web api but I want to be able to pass back a message with my response but I am using this method outside in a class how would one do that in the example of the following.
public HttpStatusCode CreateInvoice(string PumpName,string customerCode, double fuelQty, double price)
{
HttpStatusCode retval = new HttpStatusCode();
SAPbobsCOM.Documents oInvoice = company.GetBusinessObject(BoObjectTypes.oInvoices);
oInvoice.DocDate = DateTime.Now;
oInvoice.CardCode = customerCode;
oInvoice.Lines.ItemCode = "DSL";
oInvoice.Lines.Quantity = fuelQty;
oInvoice.Lines.LineTotal = price;
oInvoice.Lines.Add();
int addInvoice = oInvoice.Add();
if (addInvoice == 0)
{
retval = HttpStatusCode.OK;
}
if (addInvoice < 0)
{
string errorDescription = company.GetLastErrorDescription();
retval = HttpStatusCode.NotModified;
}
return retval;
}
I want to be able to pass back this line as part of the response message I no how to do it in the controller but this function is outside in a class. As there I dont have access to the request object?
string errorDescription = company.GetLastErrorDescription();
Edit 2
Ok so I created the function with httprequest message but i am not seeing the result in the header its showing me status 200 ok for invoice created but not the message.
public HttpResponseMessage CreateInvoice(string PumpName,string customerCode, double fuelQty, double price,string FuelType)
{
HttpResponseMessage retval = new HttpResponseMessage();
SAPbobsCOM.Documents oInvoice = company.GetBusinessObject(BoObjectTypes.oInvoices);
HttpRequestMessage Errordescription = new HttpRequestMessage() ;
oInvoice.DocDate = DateTime.Now;
oInvoice.CardCode = customerCode;
oInvoice.Lines.ItemCode = FuelType;
oInvoice.Lines.Quantity = fuelQty;
oInvoice.Lines.LineTotal = price;
oInvoice.Lines.Add();
int addInvoice = oInvoice.Add();
if (addInvoice == 0)
{
retval.StatusCode = HttpStatusCode.OK;
retval.RequestMessage=new HttpRequestMessage(HttpMethod.Post, "Invoice has been created!");
}
if (addInvoice < 0)
{
retval.StatusCode = HttpStatusCode.NotAcceptable;
retval.RequestMessage = new HttpRequestMessage(HttpMethod.Post,string.Format("Invoice was not created {0} sap code error {1}!", company.GetLastErrorDescription(),addInvoice));
}
HttpResponseMessage response = retval;
return response;
}
Here is how I consume the message in my API Controller.
public HttpResponseMessage Post(string PumpName, string FuelTagNumber,
double FuelQty, double FuelValue, string FuelType, string TransactionDate, string TransActionDate, string systemgroup1, string systemgroup2, string systemgroup3, string systemgroup4)
{
HttpResponseMessage retVal = new HttpResponseMessage();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");
int connect = _boneAPi.Connect();
if (connect == 0)
{
string CustomerCode = _boneAPi.GetCustomerCodeByVechicleTag(FuelTagNumber);
HttpResponseMessage _invoiceStatusCode = _boneAPi.CreateInvoice(PumpName, CustomerCode, FuelQty, FuelValue,FuelType);
retVal = _invoiceStatusCode;
_boneAPi.ImportTransactionToTable("", CustomerCode, TransactionDate, TransactionDate, systemgroup1, systemgroup3, FuelTagNumber, systemgroup2, systemgroup4, FuelQty.ToString(), FuelValue.ToString(), FuelType, "1");
}
return retVal;
}
To show post man result
Edit 2
To Show others how I solved it.
public HttpResponseMessage CreateInvoice(string PumpName, string customerCode, double fuelQty, double price, string FuelType)
{
HttpResponseMessage retval = new HttpResponseMessage();
SAPbobsCOM.Documents oInvoice = company.GetBusinessObject(BoObjectTypes.oInvoices);
HttpRequestMessage Errordescription = new HttpRequestMessage();
oInvoice.DocDate = DateTime.Now;
oInvoice.CardCode = customerCode;
oInvoice.Lines.ItemCode = FuelType;
oInvoice.Lines.Quantity = fuelQty;
oInvoice.Lines.LineTotal = price;
oInvoice.Lines.Add();
int addInvoice = oInvoice.Add();
if (addInvoice == 0)
{
retval.StatusCode = HttpStatusCode.OK;
retval.RequestMessage = new HttpRequestMessage(HttpMethod.Post, "");
retval.Content = new StringContent("Invoice has been created!");
}
if (addInvoice < 0)
{
retval.StatusCode = HttpStatusCode.NotAcceptable;
retval.Content = new StringContent(string.Format("Invoice was not created {0} sap code error {1}!", company.GetLastErrorDescription(), addInvoice));
}
HttpResponseMessage response = retval;
return response;
}
How about making the return a Tuple
If you are using c# 7 it would look like this:
public (HttpStatusCode code, string description) CreateInvoice(string PumpName, string customerCode, double fuelQty, double price)
{
HttpStatusCode retval = new HttpStatusCode();
string errorDescription = string.Empty;
SAPbobsCOM.Documents oInvoice = company.GetBusinessObject(BoObjectTypes.oInvoices);
oInvoice.DocDate = DateTime.Now;
oInvoice.CardCode = customerCode;
oInvoice.Lines.ItemCode = "DSL";
oInvoice.Lines.Quantity = fuelQty;
oInvoice.Lines.LineTotal = price;
oInvoice.Lines.Add();
int addInvoice = oInvoice.Add();
if (addInvoice == 0)
{
retval = HttpStatusCode.OK;
}
if (addInvoice < 0)
{
errorDescription = company.GetLastErrorDescription();
retval = HttpStatusCode.NotModified;
}
return (code: retval, description: errorDescription);
}
If an older version you would need to return a Tuple<HttpStatusCode, string>
I'd strongly recommend that a function that is creating invoices shouldn't know/care about http status codes at all.
All you need to know is whether it created a new invoice or not, and if not, why not. One option is to use an exception, but if "not modified, for reason xxx" is something you expect to happen reasonably often then that's probably not the best way forward. What you arguably want is some sort of discriminated union, but C# doesn't have a nice built in way of achieving that, so you could define a "response" type that can hold either a successful response or an error description. Then in your controller layer (that does need to know about http status codes etc), determine what sort of response to return to the client based on the contents of the response object. If you'd like the response object itself to be responsible for determining whether the error message should be exposed, ít could have a method that takes two lambdas, calling either one if in 'success' state, or the other (with the error description as a parameter) in the failed state. But that's arguably overkill.

Cosmos DB Azure Table API oData Authentication REST / C#?

I'm trying to access Azure Cosmos DB using Table API.
The challenge is, despite creating SharedKeyLite, server is still returning Unauthorized - seems like SharedKeyLite is not supported or I'm generating the signature or headers wrong.
Here is the code
static readonly string storageAccountName = "accountName";
static readonly string storageAccountKey = "xxxx";
static readonly string uri = "https://accountName.table.cosmosdb.azure.com/Contacts()";
static readonly string utc_date = DateTime.UtcNow.ToString("r");
static void Main(string[] args)
{
Console.WriteLine(GetResult().Result);
}
static async Task<string> GetResult()
{
// Set this to whatever payload you desire. Ours is null because
// we're not passing anything in.
Byte[] requestPayload = null;
var requestDateString = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture);
var requestUri = new Uri(uri);
DateTime now = DateTime.UtcNow;
//Instantiate the request message with a null payload.
using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri)
{ Content = (requestPayload == null) ? null : new ByteArrayContent(requestPayload) })
{
ConstructHeaders(httpRequestMessage.Headers, requestDateString);
string authorizationHeader = GenerateSharedKeyLite(storageAccountKey, storageAccountName, uri,requestDateString);
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("SharedKeyLite", authorizationHeader);
// Send the request.
using (HttpResponseMessage httpResponseMessage = await new HttpClient().SendAsync(httpRequestMessage))
{
string json = await httpResponseMessage.Content.ReadAsStringAsync();
return json;
}
}
}
These are the headers I"m adding, expansion of ConstructHeaders method.
Refer this link for request parameters
//Construct the headers
static void ConstructHeaders(HttpRequestHeaders headers, string now)
{
headers.Add("x-ms-date", now);
headers.Add("x-ms-version", "2017-04-17");
// If you need any additional headers, add them here before creating
// the authorization header.
headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (headers.Contains("DataServiceVersion"))
headers.Remove("DataServiceVersion");
headers.Add("DataServiceVersion", "3.0;NetFx");
if (headers.Contains("MaxDataServiceVersion"))
headers.Remove("MaxDataServiceVersion");
headers.Add("MaxDataServiceVersion", "3.0;NetFx");
}
And this is the method that creates the SharedKeyLite
//Created Shared Key Lite
static string GenerateSharedKeyLite(string accessKey, string account, string url, string date)
{
var uri = new Uri(url);
var canonicalizedResourceString = uri.PathAndQuery;
var queryStart = canonicalizedResourceString.IndexOf('?');
if (queryStart > -1)
{
if (queryStart < canonicalizedResourceString.Length - 1)
{
var path = canonicalizedResourceString.Substring(0, queryStart);
var parameters = HttpUtility.ParseQueryString(canonicalizedResourceString.Substring(queryStart + 1));
var sb = new StringBuilder();
foreach (var keyOri in parameters.Keys)
{
var value = parameters[keyOri];
var key = keyOri.ToLowerInvariant();
sb.Append("\n");
sb.Append(key);
sb.Append(":");
sb.Append(value);
}
canonicalizedResourceString = canonicalizedResourceString + sb.ToString();
}
else
{
canonicalizedResourceString = canonicalizedResourceString.Substring(0, canonicalizedResourceString.Length - 1);
}
}
canonicalizedResourceString = $"/{account}{canonicalizedResourceString}";
var stringToSign = $"{date}\n{canonicalizedResourceString}";
var signedSignature = string.Empty;
using (var hmac = new HMACSHA256(Convert.FromBase64String(accessKey)))
{
var outputBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
var signature = Convert.ToBase64String(outputBytes);
return $"{account}:{signature}";
}
}
Any Help? Ideally I want to perform the odata query using simple.odata, but first trying to make this work using HttpClient
Just copy your code and it works on my side. If you haven't modified your code, please make sure your storageAccountName and storageAccountKey are correct.
BTW, in method GenerateSharedKeyLite there's no need to add query parameters to canonicalizedResourceString for entity operation. You only need to add comp if you want to operate component info for table or service. See constructing-the-canonicalized-resource-string.
The query string should include the question mark and the comp parameter (for example, ?comp=metadata). No other parameters should be included on the query string.

Web Api Post method is receiving NULL parameter always

I am trying to pass List as a parameter to web Api , Using below code;
Client Side
public async Task<ActionResult>BatchUpdatePartial(MVCxGridViewBatchUpdateValues<NewWorkItem, int> batchValues)
{
var updatedItems = new List<NewWorkItem>();
string url = "http://localhost:9198/api/values";
foreach (var item in batchValues.Update)
{
if (batchValues.IsValid((item)))
{
var updatedVals = new NewWorkItem();
updatedVals.CPK_ID = item.CPK_ID;
updatedVals.BYR_ID = item.BYR_ID;
updatedVals.P_ID = item.P_ID;
updatedVals.CPK_PRI_FLG = item.CPK_PRI_FLG;
updatedItems.Add(updatedVals);
}
else
batchValues.SetErrorText(item, "Correct Vallidation Errors");
}
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/json";
client.Encoding = System.Text.Encoding.UTF8;
string serialisedData = JsonConvert.SerializeObject(updatedItems);
string response = client.UploadString(url, serialisedData);
Object result = JsonConvert.DeserializeObject(response);
}
return PartialView("_GridViewPartial", NewWorkItem.GridData);
}
Server Side
public string Post([FromBody]string[] values)
{
string seperator = ",";
string data = string.Join(seperator, values.ToList<string>());
string result = string.Format("Succesfully uploaded: {0}", data);
return result;
}
But I am always getting NULL inside the values at server side ?
Can you please suggest me solution ?
Thanks
Unfortunately, you are not actually sending a string[] the way your POST method expects. You are sending serializedData, which is, by your own definition, a serialization of updatedItems. updatedItems is a list of a reference type - you do not provide the definition of it here, but I guarantee you it is not going to serialize the same way a string does.
You will need to change updatedItems to be List<string> or something similar.

Categories