Internal Server Error when calling POST method - c#

I am trying to create a console application that is able to push things into a database by calling a certain API. I have control over neither the database or the API. The API is working fine as I already created tested the GET method, but I guess there is something wrong with my POST.
So first, I create the Product object containing the values that I want to send to the API.
Product product = new Product
{
productId = "27795",
amount = 1,
customerid = 0,
returnbasketid = true
};
And this is the method which is supposed to call the API's POST method.
static async Task CreateProductAsync(HttpClient cons, Product product)
{
using (cons)
{
HttpResponseMessage res = cons.PostAsJsonAsync("", product).Result;
res.EnsureSuccessStatusCode();
Console.WriteLine(res.Content.ReadAsStringAsync().Result);
}
}
Upon running the code, line HttpResponseMessage res = cons.PostAsJsonAsync("", product).Result; has the following error:
System.AggregateException: 'One or more errors occurred. (Response status code does not indicate success: 500 (Internal Server Error).)'
HttpRequestException: Response status code does not indicate success: 500 (Internal Server Error).
I guess that the problem is how I send the values to the API and what the API expects. I have this data expected example:
{
"":[{
"productId":"27795",
"amount":2,
"customerid":0
},{
"productId":"27796",
"amount":1,
"customerid":0
}],
"returnbasketid":true
}
I already tested this using Postman and it is working fine, so the problem must be with my code.
https://pastebin.com/pZDehaaD This is a full example of my Program.cs. The API url has been removed, though.
EDIT: So this is the JSON that my console application is sending:
Whereas the JSON that works through Postman is this:
So the question is how should I write my code so the JSON that I am sending is properly configurated?

You're passing a Product to the API (that's the first Fiddler capture above).
But if we look at the required input (the second Fiddlel capture above), and also the "data expected example" (grey background), we see an object that contains an (unnamed) array of Products and field called "returnbasketid".
What we need to do if re-create this object. We can call this class a "Wrapper", or "MyRequestObject" or any other name. The name is not important. I would try code of this form.
public class Wrapper
{
[JsonProperty("")] // rename json property to empty string - don't know if this will work
public List<Product> Products { get; set; } = new List<Product>();
public bool returnbasketid { get; set; }
}
You then pass an instance of this Wrapper, instead of a Product, to the with cons.PostAsJsonAsync() call.
The problem with this is that our Wrapper's Product array has a name, and the server wants an array without a name. I'm trying to rename the array above. If that works great. Otherwise I think you'll be obliged to create the json yourself (easy with Newtonsoft) and then post with HttpClient's PostAsync(Uri, HttpContent).

Related

C# API call from MarketStack and print values - Error 403

I would like to make a successful API call, then print the values in order to see if it works. My main goal is to analyze the data, after I can make a successful API call, and build a systematic strategy for trading.
System.Net.Http.HttpRequestException: "Response status code does not indicate success: 403 (Forbidden)
namespace marketstacktest
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
var options = Options.Create(new MarketstackOptions() { ApiToken = "secretTokenHere" });
var marketstackService = new MarketstackService(options, NullLogger<MarketstackService>.Instance);
var appleSymbol = "AAPL";
var fromDate = DateTime.Now.AddDays(-200);
var toDate = DateTime.Now;
//error at the await System.Net.Http.HttpRequestException: "Response status code does not indicate success: 403 (Forbidden)."
List<Marketstack.Entities.Stocks.StockBar> stock = await marketstackService.GetStockEodBars(appleSymbol, fromDate, toDate);
foreach (var stock_i in stock)
{
Console.WriteLine($"close: {stock_i.Close}");
}
}
}
}
In the API manual, which is directly linked from the github, it gives information about all of the error codes. The relevant ones here are these two:
Code
Type
Description
403
https_access_restricted
HTTPS access is not supported on the current subscription plan.
403
function_access_restricted
The given API endpoint is not supported on the current subscription plan.
Their class library on github is just wrapping a json REST api. Every call to the API is just an http request, returning data as json objects. The 403 error indicates that your request was accepted as a valid request, but intentionally rejected by the server for some reason. And according to the docs, the error was because your account is not allowed access to either https or to the type of request.
Their free-tier subscription only includes end-of-day data, which is what you requested, so it wouldn't make sense for that not to be allowed. So, your app is almost certainly making an https call.
I went to the examples at the very beginning of their quick start guide, and chose the end-of-day example to match your app, and clicked on the link. It worked, and gave a bunch of json records. But, the request they made was using 'http' not 'https'.
Changing the requst to 'https' elicited a 403 response with this content (formatted for readability):
{
"error":
{
"code": "https_access_restricted",
"message": "Access Restricted - Your current Subscription Plan does not support HTTPS Encryption."
}
}
At this point we have enough to be almost certain that this is your issue. The final thing is to go look up how to turn https requests off in their class library. To avoid having to go through the code, I checked the help at the bottom of the page one more time, and found this (formatted for readability):
var options = Options.Create(new MarketstackOptions(){
ApiToken = apiKey,
MaxRequestsPerSecond = 3,
Https = true
});
Welp. This should probably be in their first example, since that's what people are most likely to try first, but it's not. So, to stop trying to make http requests, you just need to set the Https option to false in your code. You just need to add that to the options in your code, like so:
var options = Options.Create(new MarketstackOptions(){
ApiToken = "secretTokenHere",
Https = false
});
I will leave the testing to you, but from the browser test, we know that the request should work, unless there's a bug in their library. Given the information that was available, this is almost certainly the issue.

Trying to send pagination information in response object but only passing through data property c#

I have been attempting to send pagination information to the front end from a c# backend. I have found this tutorial that I have followed directly: https://codewithmukesh.com/blog/pagination-in-aspnet-core-webapi/
I am using ASP.NET from my understanding and the class I'm working with extends ControllerBase.
I am able to get my backend setup and create the Response and PagedResponse wrapper classes that have been introduced. I don't go further as I don't have a need for the additional features the guide provides.
Just to clarify what I'd like, a response object looks like this when I sent it to the frontend:
Snippet of Response Object
RESPONSE OBJECT
{
config
data
headers
request
status
statusText
}
Usually inside of the data property of this object you would see the actual data that the front end is interested. By following the tutorial I was expecting to learn how to add a property to the response object "pageCount", and result in something like this:
RESPONSE OBJECT
{
config
data
headers
request
status
statusText
pageCount
}
However, the actual result has the same response object, but with a different data property. Inside of the data property, this is what it looks like:
DATA PROPERTY
{
data
errors
message
pageCount
succeeded
}
I don't like this approach as the data property of the response object has additional information inside of it that's not the data. I'd instead like to be able to add the page count to response. Ideally the front end would be able to get pages from assigning a variable like 'response' to the response object and doing something like response.pageCount to determine the available pages as well as response.data to get the actual data.
I have been searching for a way to add the information to the response object and haven't found any luck. I did find a solution talking about adding it to headers but that's not where I want to add it. Additionally, I did find some outdated code that no longer works on my version.
Ideally, once I find a solution to send the pageCount back, the whole response and data structure will look like this:
RESPONSE OBJECT
{
config
data:
[Array of Items] (No additional information inside of data)
headers
request
status
statusText
pageCount
}
Please let me know if any additional information is needed. Any help is appreciated! Thank you in advance!
EDIT Additional Code & Information:
[HttpGet("[action]")]
public IActionResult GetAll([FromHeader(Name = "itemsPerPage")] int? itemsPerPage, [FromHeader(Name = "sortBy")] string? sortBy, [FromHeader(Name = "page")] int? page) {
List<Things> things = repo.GetAll(itemsPerPage, sortBy, page);
return new JsonResult(new PagedResponse<List<Things>>(things, things.Count));
}
If you were to console.log() a regular response from the backend on the front end without any pagination information, the response object would look like this
Snippet of Items
This is what I'm calling the "Response" Object.
As you can see there is a data property on this Response Object:
enter image description here
And as you can see from the picture, it is an array of items. There is no additional information inside of the data property. An example of something extra would be pagination information like "PageCount". I do not want the "Page Count" property inside of the data property. However, when following the guide I linked above, that is the result I get.
Here is what I would like my response object to look like:
Snippet of Ideal Response Object
As you can see the PageCount property is a property of the Response Object, and not inside of the data property.
Now this is what my response object looks like after I what I've attempted through the tutorial:
Snippet of Response after finishing tutorail
As you can see inside of the data property, there is another data property. You can see that the pagesCount is inside of the top level data property. And the additional attributes such as errors and message are additional properties that the tutorial added.
Hopefully that gives you an idea of what I've done and how the result is not what I would've expected.
So my direct question right now is either How can I overwrite the Response Object completely so that I can directly control what it looks like including adding a pageCount property. Or Alternatively, how can I add a single property pageCount to the already existing response object?
#Andrew Williamson was the one who really answered my question. He pointed out that the response Object I was assuming was coming from the backend was actually not the response. And the information inside of the data property was what was actually being returned by the backend.
I've discovered that the reason I'm seeing that wrapper object is actually because I'm using axios through npm to access responses.
Basically the Axios NPM package describes the Response Schema:
https://www.npmjs.com/package/axios#response-schema
This shows the response object I was looking at.
The solution that I settled on was using response headers to add the page count there.
To add the response headers, I did this code:
public List<Things> GetAll([FromHeader(Name = "itemsPerPage")] int? itemsPerPage, [FromHeader(Name = "sortBy")] string? sortBy, [FromHeader(Name = "page")] int? page) {
List<Things> things = repo.GetAll(itemsPerPage, sortBy, page);
Response.Headers.Add("Pages", things.Count.ToString());
return things;
}
And this is how I accessed the pages header on the front end using AXIOS
useEffect(() => {
endPointServiceMethod(query).then((response: any) => {
console.log('Pages Headers');
console.log(response.headers['pages']);
console.log('Data');
console.log(response.data);
setItems(response);
});
}, []);
Once again, thank you Andrew Williamson for pointing out the answer!

Is there a way in Postman to store multiple values for one key?

I am working on side-project using ASP.Net Core web api. I am currently using Postman so I can interact with custom middleware. As you can see in the picture I have a User id and would like the request header to have more than one value for the user id key. Everytime I debug the api, the request header only counts one value instead of two values. I have looked at the Postman help page but it doesn't really cover any material regarding my issue. So to condense my question, is there a way in Postman that a key (For my scenario, User Id) can hold more than one value.
Your question doesn't really make sense. Postman will send the data you put, to the server. The data you put is "1,2". At the server end, if you pull the userId value and split it on the comma, you have your two values, no?
I find it incredibly unlikely that when you pull userId at the server, the value of the header is "1" and the other id has disappeared. If the web did that loads of headers (such as your gzip, deflate, br there) would get lost and stuff wouldn't work properly
In java with spring boot framework i have idea about it we have to send List userIds from request controller method this method you have to take as post method and send that data into body part with
#PostMapping("/listUsers")
public String getList(#RequestBody List<Integer> userIds) {
// call service method
return "page";
}
Json Request from postman
{
1,2,3,4,5............
}
In dot net core
[Produces("application/json")]
[Route("api/accounts")]
public class AccountsController : Controller
{
[HttpGet]
[Route("servicesbycategoryids")]
public IActionResult ServicesByCategoryIds([FromQuery] int[] ids)
{
return Ok();
}
}

HTTP Error 406 after execting a restsharp request

I'm trying to use RestSharp to connect to a server. but I get HTTP Error 406 Not acceptable.
Here is the web service response:
{"metaInfo":{"ErrorString":" ","ErrorCode":" ","Result":true},"userModel":
{"UserId":"123qwe","UserName":"aaa","FirstName":"ALi","LastName":"TbT"}}
The rest service is actually a simple php file that I created for test:
<?php
$a = array("metaInfo"=>array("ErrorString"=>" ","ErrorCode"=>" ","Result"=>true),"userModel"=>array("UserId"=>"123qwe","UserName"=>"aaa","FirstName"=>"ALi","LastName"=>"TbT"));
echo json_encode($a);
?>
And Here is the RegisterResult and MetaInfo classes:
public class RegisterResult
{
public MetaInfo metaInfo { get; set; }
}
public class MetaInfo
{
public string ErrorString { get; set; }
public string ErrorCode { get; set; }
public bool Result { get; set; }
}
The code that runs the rest request is as follows:
var client = new RestClient(Configuration.PortalUri);
var request = new RestRequest(requestUri, HttpMethod.Post);
var asyncHandle = await client.Execute<RegisterResult>(request);
Could you please tell me what the problem is? I think something is wrong with headers or encoding or something like that. Am I right?
This error is specific to the way the programmers have created the service you are calling. There's nothing wrong with your code from a C# perspective, but the way the service author has chosen to implement their service doesn't like something in the service call. You will have to refer to their documentation or ask one of their programmers.
Here's an explanation of http error 406
(taken from http://www.checkupdown.com/status/E406.html):
Fixing 406 errors - general
This error occurs very infrequently in Web browsers, because most
browsers will accept any data returned from the Web server.
If the client is not a Web browser, then anyone can only investigate
the problem by looking at the Accept headers generated by the client
system
Consider these headers to modify:
•Accept: The MIME types accepted by the client. For example, a browser
may only accept back types of data (HTML files, GIF files etc.) it
knows how to process.
•Accept-Charset: The character sets accepted by
the client.
•Accept-Encoding: The data encoding accepted by the
client e.g. the file formats it understands. •Accept-Language: The
natural languages (English, German etc.) accepted by the client.
•Accept-Ranges: Whether the client accepts ranges of bytes from the
resource i.e. a portion of the resource.
For example, you may need to add this to your request:
request.AddHeader("Accept", "application/json");

Posting data to asp.net Web API

I'm trying to figure out the new ASP.NET Web API.
So far I've been able to create this method signature and connect to it just fine and get a valid response...
[HttpPost]
public HttpResponseMessage CreateAccount()
I am able to send a request to this method with fiddler and have verified that it is receiving the request.
However, when I try to pass data is when I am running into a problem.
The first thing I tried was...
[HttpPost]
public HttpResponseMessage CreateAccount([FromBody]string email, [FromBody]string password)
And I type
email:xyz,password:abc
into the body of the request in fiddler. When I do this I get a 500 error stating
'Can't bind multiple parameters ('email' and 'password') to the request's content.'
I have also tried this as a method signature...
[HttpPost]
public HttpResponseMessage CreateAccount([FromBody]UserAccountRequestData data)
with the UserAccountRequestData being a simple POCO
public class UserAccountRequestData
{
public string Email { get; set; }
public string Password { get; set; }
}
And I put
{Email:xyz,Password:abc}
or
data:{Email:xyz,Password:abc}
into the body of the request. In both cases trying to populate the POCO I am able to reach the method while debugging, but the data object is always null.
I need to understand how to create API methods that accept both strongly typed POCOs and others that accept multiple primitive types like strings and ints.
Thanks
You need to set the Content-Type header to application/json and then provide valid JSON.
{"Email":"xyz","Password":"abc"}

Categories