I used to use ASMX web services, however have since read (and been told) that a better way to request data from a client etc is to use web API's with MVC.
I have created an MVC 4 web api application and getting to grips with how it works.
Currently I have a single public string in my valuesControllers -
public class ValuesController : ApiController
{
// GET api/values/5
public string Get(int id)
{
return "value";
}
}
And I am currently trying to call this in my client like this -
class Product
{
public string value { get; set; }
}
protected void Button2_Click(object sender, EventArgs e)
{
RunAsync().Wait();
}
static async Task RunAsync()
{
using (var client = new HttpClient())
{
try
{
client.BaseAddress = new Uri("http://localhost:12345/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP GET
HttpResponseMessage response = await client.GetAsync("api/values/5");
if (response.IsSuccessStatusCode)
{
Product product = await response.Content.ReadAsAsync<Product>();
Console.WriteLine("{0}", product.value);
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message.ToString());
}
}
}
On debugging I can step through the request and enter the web API code successfully however on the line -
Product product = await response.Content.ReadAsAsync<Product>();
This fails and enters my catch with the exception -
Error converting value "value" to type 'myDemo.Home+Product'. Path '', line 1, position 7.
Why is this?
Why is this?
Because from your controller action you are returning a string, not a Product which are 2 quite different types:
public string Get(int id)
{
return "value";
}
so make sure that you are consistently reading the value on the client:
if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsAsync<string>();
Console.WriteLine("{0}", result);
}
Of course if you modified your API controller action to return a Product:
public Product Get(int id)
{
Product product = ... go fetch the product from the identifier
return product;
}
your client code would work as expected.
Related
I have set up a backend server with multiple endpoints using NodeJS with the ExpressJS framework. These REST Api Endpoints are connected to a Mongodb Database.
Due to project requirements, I had to write some code on C# that will also be calling and posting data into the database. However, I am now having trouble accessing the collections created by the ExpressJS Api Endpoint on my C# code.
May I know how is it normally done? Eg. how do I create a GET request from C# to access a collection created by ExpressJS Any help is greatly appreciated! Thank you!
restful request is just a format of HTTP request. so you should send the request first.
then when you get restful response, you should deserialize it in JSON format.
show the example code
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace HttpClientSample
{
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
class Program
{
static HttpClient client = new HttpClient();
static void ShowProduct(Product product)
{
Console.WriteLine($"Name: {product.Name}\tPrice: " +
$"{product.Price}\tCategory: {product.Category}");
}
static async Task<Uri> CreateProductAsync(Product product)
{
HttpResponseMessage response = await client.PostAsJsonAsync(
"api/products", product);
response.EnsureSuccessStatusCode();
// return URI of the created resource.
return response.Headers.Location;
}
static async Task<Product> GetProductAsync(string path)
{
Product product = null;
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
product = await response.Content.ReadAsAsync<Product>();
}
return product;
}
static async Task<Product> UpdateProductAsync(Product product)
{
HttpResponseMessage response = await client.PutAsJsonAsync(
$"api/products/{product.Id}", product);
response.EnsureSuccessStatusCode();
// Deserialize the updated product from the response body.
product = await response.Content.ReadAsAsync<Product>();
return product;
}
static async Task<HttpStatusCode> DeleteProductAsync(string id)
{
HttpResponseMessage response = await client.DeleteAsync(
$"api/products/{id}");
return response.StatusCode;
}
static void Main()
{
RunAsync().GetAwaiter().GetResult();
}
static async Task RunAsync()
{
// Update port # in the following line.
client.BaseAddress = new Uri("http://localhost:64195/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
try
{
// Create a new product
Product product = new Product
{
Name = "Gizmo",
Price = 100,
Category = "Widgets"
};
var url = await CreateProductAsync(product);
Console.WriteLine($"Created at {url}");
// Get the product
product = await GetProductAsync(url.PathAndQuery);
ShowProduct(product);
// Update the product
Console.WriteLine("Updating price...");
product.Price = 80;
await UpdateProductAsync(product);
// Get the updated product
product = await GetProductAsync(url.PathAndQuery);
ShowProduct(product);
// Delete the product
var statusCode = await DeleteProductAsync(product.Id);
Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
}
}
reference call web api
As I am new to WebAPI I have been experimenting with samples from the web. I have an ApiController based class which handles Post, Get, etc. From my client application, I can perform a Get and a Delete successfully but when I do a Post or a Put of a string, I can see the string value is null at the server and I get StatusCode 204, No Content at the client. Using Postman I can successfully do a Post or Put so it seems to be a problem with my client app.
Have tried basing the client on .Net 4.7.2 as well as .NET Core 2.2
Here is the entire controller class of my WebAPI program:
namespace WebApplication1.Controllers
{
public class ValuesController : ApiController
{
static List<string> strings = new List<string>()
{
"value0", "value1", "Value2"
};
// GET api/values
public IEnumerable<string> Get()
{
return strings;
}
// GET api/values/5
public string Get(int id)
{
return strings[id];
}
// POST api/values
public void Post([FromBody]string value)
{
strings.Add(value);
}
// PUT api/values/5
public void Put(int id, [FromBody]string value)
{
strings[id] = value;
}
// DELETE api/values/5
public void Delete(int id)
{
strings.RemoveAt(id);
}
}
}
Here is code from my client Program:
class Program
{
static HttpClient client = new HttpClient();
static void Main()
{
RunAsync().GetAwaiter().GetResult();
}
static async Task RunAsync()
{
client.BaseAddress = new Uri("http://localhost:56037/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
var response = await client.PutAsJsonAsync($"api/values/2", new StringContent("zzz"));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
I set breakpoints in the Put and Post methods but the value of the string being passed is null, not the value I sent to the server.
Id maybe try
var response = await client.PutAsJsonAsync($"api/values/2", new StringContent("zzz"), System.Text.Encoding.UTF8, "application/json");
Turns out that I needed to enclose the string in quotes as follows:
var response = await client.PutAsync("api/values/0", "\"zzz\"", new StringTypeFormatter());
response = await client.PostAsync("api/values", "\"zzz\"", new StringTypeFormatter());
If you want the item at the index, try:
strings.ElementAt(id) = value;
I am trying to get the list of employees using Web API Get method in C# MVC and display in the view. But my list is coming null. I am not sure what i am missing. I am referring to this resource http://www.tutorialsteacher.com/webapi/consume-web-api-get-method-in-aspnet-mvc
Home Controller :
namespace Sample.Controllers
{
public class HomeController : Controller
{
private readonly EmployeeDBEntities _db = new EmployeeDBEntities();
public ActionResult Index()
{
IEnumerable<Employee> employees = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:62141/api/");
//HTTP GET
var responseTask = client.GetAsync("employee");
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsAsync<IList<Employee>>();
readTask.Wait();
employees = readTask.Result;
}
else //web api sent error response
{
//log response status here..
employees = Enumerable.Empty<Employee>();
ModelState.AddModelError(string.Empty, "Server error. Please contact administrator.");
}
}
return View(employees);
}
}
}
Employee API Controller :
namespace Sample.Controllers
{
public class EmployeeController : ApiController
{
public IHttpActionResult GetAllEmployees()
{
IList<Employee> employees = null;
using (var ctx = new EmployeeDBEntities())
{
employees = ctx.Employees.ToList<Employee>();
}
if (employees.Count == 0)
{
return NotFound();
}
return Ok(employees);
}
}
You first should check the status code on you response.
If it is NotFound then there was no results (the way your code is done).
But your problem may be related to the fact that the result of ctx.Employees.ToList<Employee>(); is being disposed and terminated before the response is completed, but even that would give a DisposedException.
You should consider adding You database context instance object in a IoC container with transient lifetime, and inject does dependencies to the controller constructor, because the request does not end when the action method ends.
I'm creating an Instagram API client on ASP MVC using HttpClient, I'm trying to make a get request but it fails without throwing exception or responding and doesn't respond to my timeout. Here is my code:
public class InstagramService
{
private HttpClient Client = new HttpClient {
BaseAddress = new Uri("https://api.instagram.com/v1/"),
Timeout = TimeSpan.FromMilliseconds(500)
};
public async Task<InstagramUser> GetInstagramUser(long? userId = null)
{
InstagramUser User = null;
string Parameter = (userId == null) ? "self" : userId.ToString();
try {
var response = await Client.GetAsync("users/" + Parameter + "/" + GetAccessToken());
if (response.IsSuccessStatusCode)
{
User = await response.Content.ReadAsAsync<InstagramUser>();
}
}catch(Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.InnerException.Message);
}
return User;
}
private string GetAccessToken()
{
return "?access_token=" + DB.config_det_sys.Single(i => i.codigo == "ACCESS_TOKEN_INSTAGRAM" && i.estado == true).Valor;
}
}
EDIT
Here I add how I call my service on the Home Controller, I will still test changing the controller to async Task
public class HomeController : Controller
{
private InstagramService IGService = new InstagramService();
public ActionResult About()
{
var apiCall = IGService.GetInstagramUser();
var model = apiCall.Result;
return View(model);
}
}
I tested on Postman trying to make the API call and it indeed worked, so where I'm failing to catch errors?
Your problem is here:
var model = apiCall.Result;
As I describe on my blog, you shouldn't block on asynchronous code. It can cause a deadlock.
Instead of Result, use await:
var model = await apiCall;
Adding to Stephen's answer, update the controller's action to be async all the way.
public class HomeController : Controller {
private InstagramService IGService = new InstagramService();
public async Task<ActionResult> About() {
var model = await IGService.GetInstagramUser();
return View(model);
}
}
I have the following Web Api
public class ApiTestController : ApiController
{
// GET api/<controller>
[HttpGet]
public string UploadImage(int id)
{
return "You entered = " + id;
}
}
which when run and I enter /api/ApiTest/3 it hits it and returns You entered 3
Now in my seperate MVC application I'm trying to reference the same api method by doing the following
private const string WebUrl = "http://localhost:1769/api/ApiTest/";
//
// GET: /Home/
public ActionResult Index()
{
try
{
var test = GetInvoiveNo(3);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return View();
}
public string GetInvoiveNo(int id)
{
var uri = WebUrl + id;
using (var httpClient = new HttpClient())
{
Task response = httpClient.GetStringAsync(uri);
return JsonConvert.DeserializeObjectAsync<string>(response.ToString()).Result;
}
}
But I get the error:
one or more errors occurred
so I take a look into the inner exception and this is what it says:
Unexpected character encountered while parsing value: S. Path '', line 0, position 0."}
Now I'm not sure what I've done wrong here so if someone can kindly tell me or give me a simple example I'd appreciate it.
Your problem is that your response is not in proper format.
In below code you try to user response.ToString() which is wrong and you have to user result of response.
public string GetInvoiveNo(int id)
{
var uri = WebUrl + id;
using (var httpClient = new HttpClient())
{
var response = httpClient.GetStringAsync(uri);
return JsonConvert.DeserializeObjectAsync<string>(response.Result).Result;
}
}