I have a webApi set up that has a PostStudent method. The API call works fine and creates a new student in the DB, but I can't get it to return the value in the response body. I have tried returning Ok(newStudent) and Created("~/api/poststudent", newStudent) but neither of them have returned the newStudent value that I need.
I have gone through all of the response and can't find the actual newStudent object. Is it there and I am just missing it or do I have a problem with my code?
This is the PostStudent method from the API;
var newStudent = new Student
{
studentId = nextStudentId,
studentFirstName = studentEntry.StudentFirstName,
studentLastName = studentEntry.StudentLastName,
studentDOB = studentEntry.StudentDob,
studentEmergencyContactName = studentEntry.StudentEmergencyContactName,
studentEmergencyContactNum = studentEntry.StudentEmergencyContactNum,
ticketNumber = studentEntry.TicketNumber
};
db.Student.Add(newStudent);
try
{
db.SaveChanges();
}
catch (DbUpdateException)
{
if (StudentExists(newStudent.studentId))
return BadRequest("That student id already exists");
throw;
}
return Ok(newStudent.studentId);
// return Created("~/api/poststudent", newStudent);
}
This is where I call postasync and try to save the response body;
var response = client.PostAsync("api/poststudent", content);
return response.Result.Content.ReadAsStringAsync().ToString();
And this is where I want to use the value;
var newStudentId = controller.PostStudent(studentFirstName, studentLastName, studentDob, ticketNumber);
var url = "~/AddGuardian/AddGuardian/" + newStudentId;
Response.Redirect(url);
I hope someone can help me. I never thought redirecting to another page would be so damn hard!
Cheers.
You're not awaiting the async calls:
var response = await client.PostAsync("api/poststudent", content);
return (await response.Result.Content.ReadAsStringAsync()).ToString();
There are 2 official tutorials for quickstarting a ASP.NET WebApi project.
Using Web API 2 with Entity Framework 6
Calling a Web API From a .NET Client (C#)
I want to recommend to work through these. If you find your application is doing things wrong, fix your application towards these samples. If your requirements differ from what is given in the samples, you can emphasize on this in your question.
Related
This question already has answers here:
Parsing Json rest api response in C# [duplicate]
(3 answers)
Closed 12 months ago.
tl;dr: I have an API that works in the browser, but I can't figure out how to return a list of items so I can traverse them in a List.
Long version:
I'm still struggling with API's and now I've hit another snag. I have the following code in my API:
[Route("api/[controller]")]
[ApiController]
public class ScheduleController : Controller
{
[HttpGet]
public IEnumerable<Schedule> GetAllSchedules()
{
using (ScheduleDataEntities entities = new ScheduleDataEntities())
{
return entities.Schedule.ToList();
}
}
[HttpGet("{id}")]
public Schedule GetIndividualSchedule(int id)
{
using (ScheduleDataEntities entities = new ScheduleDataEntities())
return entities.Schedule.FirstOrDefault(e => e.ScheduleID == id);
}
}
When I navigate to my localhost (https://localhost:7017/api/schedule) it returns the data just fine:
[{"userID":121,"weekDay":"Monday","startTime":"07:00:00","endTime":"07:15:00","createdDate":"2022-01-18T22:11:34.8966667","modifiedDate":"2022-01-18T22:11:34.8966667","active":false,"scheduleID":14414,"dayOfWeek":1,"tsType":"Normal"},
{"userID":94,"weekDay":"Wednesday","startTime":"08:45:00","endTime":"09:00:00","createdDate":"2021-11-03T13:50:50.26","modifiedDate":"2021-11-03T13:50:50.26","active":false,"scheduleID":13160,"dayOfWeek":3,"tsType":"Normal"}
...]
So far, so good. Now, I want to return the list to the client calling the API. I've made a simple test client that should get the list when I press a button:
client.Dispose();
client = new HttpClient();
client.BaseAddress = new Uri("https://localhost:7017/api/schedule/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
List<Schedule> list = GetScheduleList("https://localhost:7017/api/schedule");
//Do something with the list...
However, I can't figure out how this method should work:
static List<Schedule> GetScheduleList(string path)
{
List<Schedule> schedules = null;
HttpResponseMessage response = client.GetAsync(path);
return schedules;
}
Every tutorial I can find online, says to use GetAsync. However, that only works with the await keyword. Adding the await keyword means I have to add the async keyword to my method, which means I must return a Task<List> or a task-like type, which can't be converted to a List. If I return it as a Task, how can I then break it back down into my list of schedules?
I really feel like I've tried anything I've come across, so any help you can give is much appreciated.
try this
static async Task<List<Schedule>> GetScheduleList(string path)
{
var response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
var stringData = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<List<Schedule>>(stringData);
}
return null;
and fix the method call
List<Schedule> list = await GetScheduleList("https://localhost:7017/api/schedule");
or you can try, if you don't use async
List<Schedule> list = GetScheduleList("https://localhost:7017/api/schedule").Result;
did you try to start the web app in debug mode and find the return of the API ?
You can even use console.log to give more details;
Otherwise, I was there one day. it's CORS problem. you should add the header : Access-Control-Allow-Origin should have wildcard "*" as a value in order to be able to call https APIs on http servers.
I'm sure there are some posts talking about it but so far, nothing works in my case.
I get data from an api and I read it within a console application in C# for testing purpose.
My end goal is to call the data from the API and store the attributes ("analyst" for ex) somewhere where I could pick some of them and dysplay them within a winform app' (I think mostly through a Datagridview than a webbrowser but I'm not sure). I understand I need Json.net but as I use the ReadAsStringAsync() method I'm not sure about the object type I use anymore. Here is my code:
// Get the response from the API
using (var client = new HttpClient())
{
try
{
var response = client.SendAsync(httpRequestMessage).Result;
//Console.WriteLine(response.ToString());
HttpContent responseContent = response.Content;
var responsedata = responseContent.ReadAsStringAsync();
//Console.WriteLine(responseContent);
string data = responsedata.Result;
var BS = JsonConvert.DeserializeObject<dynamic>(data);
string analyst = BS.analyst;
Console.WriteLine(analyst);
Console.ReadKey();
}
catch (HttpRequestException e)
{
Console.WriteLine("\nException Caught!");
Console.WriteLine($"Message: {e.Message}");
}
}
I got an object "data" like this :
{
"result":
{
"3_yr_proj_eps_cagr_prcntg": 47.0,
"analyst": "Jones Dow",
"analyst_email": "Dow.Jones#Theprovider.com",
"business_summary": "<p>COMPANY OVERVIEW. Dow Inc. was formed on April 1, 2019, following the spin- off of the company from DowDupont".
}
}
And I just want to display "analyst" into my datagridview when I click on a button but so far my variable "analyst" is null meaning the porgramm does not see the value "Jones Dow"...
I'm kind of lost within all the information I could read about the topic, any help would be appreciated.
Thanks
So I got a Blazor edit page that needs to update fields. My main issue is trying to return the object "ResponseDto" with the changes made and refresh the screen;
Call from blazor page:
<div class="form-group">
<button class="btn btn-primary edit-btn" style="float: right;" #onclick="#SaveChanges">Save Changes</button>
</div>
protected async Task SaveChanges()
{
clientdto = await apiService.SaveDtoAsync(ClientID, clientdto);
this.StateHasChanged();
}
API service
public async Task<ClientDto> SaveDtoAsync(int ClientID, [FromBody] ClientDto ClientDto)
{
_httpClient.DefaultRequestHeaders.Clear();
var Url = "api/clients/" + Convert.ToInt32(ClientID);
var SerializedClientDto = JsonConvert.SerializeObject(ClientDto);
using (var RequestClientDto = new HttpRequestMessage(HttpMethod.Put, Url))
{
RequestClientDto.Content = new StringContent(SerializedClientDto, System.Text.Encoding.UTF8, "application/json");
RequestClientDto.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
using (var ResponseClientDto = await _httpClient.SendAsync(RequestClientDto))
{
if (!ResponseClientDto.IsSuccessStatusCode)
{
ResponseClientDto.EnsureSuccessStatusCode();
}
else
{
var Response = await ResponseClientDto.Content.ReadAsStringAsync();
var ResponseDto = JsonConvert.DeserializeObject<ClientDto>(Response);
}
}
}
return ResponseDto;
}
This doesn't have anything to do with Blazor, you're simply trying to use a variable in a higher scope than it exists.
But before you can decide on a correction, first you need to decide what the method should return if you never populate ResponseDto. For example, if it should throw an exception then you can move the return to where you define the variable (don't need the variable at all, really) and throw an exception at the end of the method:
//...
else
{
var Response = await ResponseClientDto.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<ClientDto>(Response);
}
}
}
throw new Exception("Some meaningful error message");
}
Or if it should return null or an empty instance of the type, you can do that:
//...
else
{
var Response = await ResponseClientDto.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<ClientDto>(Response);
}
}
}
return null; // or: return new ClientDto();
}
It's really up to you how you want the method to behave, and there are a variety of ways to structure it. All you need to ensure is:
You use variables in the scope that they exist.
All possible logical code paths in the method produce a result. (Either return a value or throw an exception.)
Here is a somewhat simplified version and should be equal to the call above.
public async Task<ClientDto> SaveDtoAsync(int clientID, [FromBody] ClientDto clientDto)
{
_httpClient.DefaultRequestHeaders.Clear();
var response = await _httpClient.PutAsJsonAsync(url, $"api/clients/{clientID}");
if (!response.IsSuccessStatusCode)
{
//TODO: add error handling
return null; //or default;
}
using (MemoryStream ms = await response.Content.ReadAsStreamAsync())
{
return await JsonSerializer.DeserializeAsync<ClientDto>(ms);
}
}
I'm using the Http extension methods that can make http calls more readable and manageble. The other trick to note here I'm not reading the response content as string, instead I use memory stream. With new Json parser we can safe some allocations this way. This means less GC pressure. (Small side note for parsing large json (over 1000 objects) there is known performance bug).
For a less change required version #David already posted an excelente answear.
#ZoltBendes Thank you so much for the help, I managed to get it to work "partially", It runs but I get back a "StatusCode: 204, ReasonPhrase: 'No Content' so technically its not taking the changes even though everything seems to be working fine. This is how I got to work, once again "partially"
HttpResponseMessage ResponseClientDto = await httpClient.PutAsJsonAsync($"api/clients/{ClientID}", ClientDto);
if (!ResponseClientDto.IsSuccessStatusCode)
{
ResponseClientDto.EnsureSuccessStatusCode();
}
var response = await _httpClient.GetAsync($"api/clients/{ClientID}");
var responseContent = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<ClientDto>(responseContent);
I have the following piece of code that retrieves transactions from a Dynamics CRM (querying with OData):
public async Task<IEnumerable<Transaccion>> GetTransactions()
{
var tableName = Transaccion.CrmTableName;
var request = new RestRequest($"/api/data/v8.0/{tableName}");
request.AddHeader("Prefer", "odata.maxpagesize=500");
var responseData = await client.ExecuteGetTaskAsync<ODataResponse<List<Transaccion>>>(request);
var transactions = responseData.Data.Value;
while (responseData.Data.NextLink != null)
{
request = new RestRequest(responseData.Data.NextLink);
request.AddHeader("Prefer", "odata.maxpagesize=500");
responseData = await client.ExecuteGetTaskAsync<ODataResponse<List<Transaccion>>>(request);
transactions.AddRange(responseData.Data.Value);
}
return transactions;
}
once I execute the first "ExecuteGetTaskAsync", I get for my example and as expected a NextLink attribute that points to the next set of entities that I need to retrieve. However, when I try to perform the next RestRequest, I don't get a JSON as response, but a Html page corresponding to a redirect, where I can read the error message "".
It's weird, since the first call could be made correctly because the Restclient was correctly authenticated.
What's going on? How can I do paging with Dynamics CRM in .Net and use the NextLink?
In my case URL in #odata.nextLink was with an error.
How it was:
http://[Organization URI]/api/data/v8.2/[entity]/(68e95f08-d372-e711-966b-defe0719ce9e)/[relation entity]?$select=ne_name
And that did not work, but this did:
http://[Organization URI]/api/data/v8.2/[entity](68e95f08-d372-e711-966b-defe0719ce9e)/[relation entity]?$select=ne_name
There is no "/" between [entity] and (id)
The odada nextlink returns the full URL of the next request so you'll need to parse it to get only the /api/** portion.
I'm still learning c# web API at the moment, and I've faced some problems.
so the code snippet below shows a portion of my codes that will create a new student in the database, what I am trying to do is to create the object and if it succeeded, it will return a HTTP-CREATED http response code and return the STUDENT OBJECT.
if it fails, it should return a HTTP-BADREQUEST response code and ALSO return the STUDENT OBJECT.
HOWEVER, in order to return the response code, I am unable to return a student object and vice-versa due to the return type set, hence, the dilemma.
// POST api/student
public HttpResponseMessage PostStudent(Models.Student student)
{
if (DBManager.createStudent(student) != null)
return new HttpResponseMessage(HttpStatusCode.Created);
// HOW TO RETURN STUDENT OBJECT?
else
return new HttpResponseMessage(HttpStatusCode.BadRequest);
// HOW TO RETURN STUDENT OBJECT?
}
The HttpRequestMessageExtensions.CreateResponse<T> Method has an optional formal paramater called value that can be used to create an HttpResponseMessage that contains both a status code and an object.
return Request.CreateResponse(HttpStatusCode.Created, student);
I'll tell you my own experience of building a web server for my company: DON'T use WebApi. There are so many limitations and in short, supports a very narrow usage scenario. Just stick to the traditional MVC controllers and life is much easier:
public ActionResult PostStudent(Models.Student s){
//do something
Response.StatusCode = 400;
return Json(s);
}