Consuming Web API in Class library - c#

I'm building plugin in auto-cad and using class library and web API with entity framework
But every time i try to consume web API in my class library the response returns with "Not Found".
This is my code of class library
[CommandMethod("Doit")]
public void Test()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
Checker c = new Checker() { WholeArea = 1000, BuildingArea = 200, Status = 1 };
using (var client = new HttpClient())
{
client.BaseAddress =new Uri("http://localhost:52133/api");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.PostAsJsonAsync("Checker", c).Result;
if (response.IsSuccessStatusCode)
{
ed.WriteMessage("Hello data");
}
else
{
ed.WriteMessage((response.StatusCode).ToString());
}
}
}
This my controller Post Method
// POST: api/Checkers
[ResponseType(typeof(Checker))]
public IHttpActionResult PostChecker(Checker checker)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Checkers.Add(checker);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = checker.ID }, checker);
}
Firstly :Now i don not know what is the problem of that code to return not found
Second- If there is away to build plugins in auto-cad using .net core

Are you seeing a 404 response code?
Have you tried calling the api method from postman or fiddler?
I'd always try calling from one of those so you can be certain the api is responding correctly. This will tell you whether or not the issue lies in your consuming code.
I notice you're using PostAsJsonAsync to call the api method but the api method isn't marked as async, could that be the issue?
The api method also isn't decorated with the HttpPost attribute; I'm not sure if that's a requirement but I'd try each of these separately

Related

How to retrieve custom error object from a failed httpClient call?

I'm quite new to .Net Core Web API and have spent a few days looking for an answer but couldn't find exactly what I am looking for. What I want to know is how to retrieve the custom object that is pass from an API action back to the client via an ActionResult (BadRequest(), NotFound()...etc.)
So I created a new Web API project in VS2019 and updated the default Get method of the WeatherForecastController like this:
[HttpGet]
public ActionResult<IEnumerable<WeatherForecast>> Get()
{
return NotFound(new { Message = "Could not find data", Suggestion = "Refine your search" });
}
When testing in Postman, I can get the expected output of Status = 404 and body is
{
"message": "Could not find data",
"suggestion": "Refine your search"
}
But in the client project, I just don't know how I can retrieve that custom error object.
My client code is like this:
public async Task OnGet()
{
try
{
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://localhost:44377/");
WeatherForcasts = await httpClient.GetFromJsonAsync<WeatherForcast[]>("weatherforecast");
}
catch (HttpRequestException ex)
{
hasError = true;
}
}
I understand that if the API action does not return a success status code (such as 200) then this will raise an HttpRequestException. But I can't find a away to get that custom error object out from the HttpRequestException.
Any help will be very much appreciated!
Change your code to this:
public async Task OnGet()
{
using var client = new HttpClient();
var baseAddress ="https://localhost:44377");
client.BaseAddress = new Uri(baseAddress);
var response= await client.GetAsync(baseAddress);
var statusCode = response.StatusCode.ToString(); // shoud be "NotFound"
var stringData = await response.Content.ReadAsStringAsync();
var data= JsonConvert.DeserializeObject<object>(stringData);// should be
// "{{"message":"Could not find data","suggestion": "Refine your search"}}"
....
}

Consuming WEB API using HttpClient in c# console application

I have created a web API in visual studio 2015 using a MySQL database. The API is working perfect.
So I decided to make a console client application in which I can consume my web-service (web API). The client code is based on HttpClient, and in the API I have used HttpResponse. Now when I run my console application code, I get nothing. Below is my code:
Class
class meters_info_dev
{
public int id { get; set; }
public string meter_msn { get; set; }
public string meter_kwh { get; set; }
}
This class is same as in my web API model class:
Model in web API
namespace WebServiceMySQL.Models
{
using System;
using System.Collections.Generic;
public partial class meters_info_dev
{
public int id { get; set; }
public string meter_msn { get; set; }
public string meter_kwh { get; set; }
}
Console application code
static HttpClient client = new HttpClient();
static void ShowAllProducts(meters_info_dev mi)
{
Console.WriteLine($"Meter Serial Number:{mi.meter_msn}\t Meter_kwh: {mi.meter_kwh}", "\n");
}
static async Task<List<meters_info_dev>> GetAllRecordsAsync(string path)
{
List<meters_info_dev> mID = new List<meters_info_dev>();
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
mID = await response.Content.ReadAsAsync<List<meters_info_dev>>();
}
else
{
Console.WriteLine("No Record Found");
}
return mID;
}
static void Main()
{
RunAsync().Wait();
}
static async Task RunAsync()
{
client.BaseAddress = new Uri("http://localhost:2813/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var m = await GetAllRecordsAsync("api/metersinfo/");
foreach(var b in m)
{
ShowAllProducts(b);
}
}
In my API I have 3 GET methods under a single controller, so I have created different routes for them. Also the URL for them is different.
http://localhost:2813/api/metersinfo/ will return all records
While debugging the code, I found that List<meters_info_dev> mID = new List<meters_info_dev>(); is empty:
While the response is 302 Found, the URL is also correct:
Update 1
After a suggestion I have done the following:
using (var client = new HttpClient())
{
List<meters_info_dev> mID = new List<meters_info_dev>();
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
mID = await response.Content.ReadAsAsync<List<meters_info_dev>>();
}
else
{
Console.WriteLine("No Record Found");
}
return mID;
}
When I run the application, I get the exception "An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set."
Update 2
I have added a new piece of code:
using (var cl = new HttpClient())
{
var res = await cl.GetAsync("http://localhost:2813/api/metersinfo");
var resp = await res.Content.ReadAsStringAsync();
}
And in the response I am getting all the records:
I don't know why it's not working with the other logic and what the problem is. I have also read the questions Httpclient consume web api via console app C# and Consuming Api in Console Application.
Any help would be highly appreciated.
The code needs quite a bit of work.
The line you highlighted will always be empty because that's where you initialise the variable. What you want is run thorugh the code until you get the result back form the call.
First, make sure your api actually works, you can call the GET method you want in the browser and you see results.
using (var client = new HttpClient())
{
var result = await client.GetAsync("bla");
return await result.Content.ReadAsStringAsync();
}
that's an example of course, so replace that with your particular data and methods.
now, when you check the results just because your response.IsSuccessStatusCode is false that doesn't mean there are no records. What it means is that the call failed completely. Success result with an empty list is not the same thing as complete failure.
If you want to see what you get back you can alter your code a little bit:
if(response.IsSuccessStatusCode)
{
var responseData = await response.Content.ReadAsStringAsync();
//more stuff
}
put a breakpoint on this line and see what you actually get back, then you worry about casting the result to your list of objects. Just make sure you get back the same thing you get when you test the call in the browser.
<------------------------------->
More details after edit.
Why don't you simplify your code a little bit.
for example just set the URL of the request in one go :
using (var client = new HttpClient())
{
var result = await client.GetAsync("http://localhost:2813/api/metersinfo");
var response = await result.Content.ReadAsStringAsync();
//set debug point here and check to see if you get the correct data in the response object
}
Your first order of the day is to see if you can hit the url and get the data.
You can worry about the base address once you get a correct response. Start simple and work your way up from there, once you have a working sample.
<----------------- new edit ---------------->
Ok, now that you are getting a response back, you can serialise the string back to the list of objects using something like Newtonsoft.Json. This is a NuGet package, you might either have it already installed, if not just add it.
Add a using statement at the top of the file.
using Newtonsoft.Json;
then your code becomes something like :
using (var client = new HttpClient())
{
var result = await client.GetAsync("bla");
var response = await result.Content.ReadAsStringAsync();
var mID = JsonConvert.DeserializeObject<List<meters_info_dev>>(response);
}
At this point you should have your list of objects and you can do whatever else you need.

WebAPI is returning 200 but the SendAsync call shows 500

I have an MVC application that calls a WebAPI async, both on POST and GET. When running both the WebAPI and MVC applications locally, the WebAPI response shows successful but the SendAsync request returns 500. Also, Fiddler doesn't show the API call. I have a suspicion that it has to do with how the async request is being handled, but I'm not sure what I'm missing.
MVC Controller call to the API:
public Model UploadFile(Model formCollection)
{
var documentModel = formCollection.ToString();
var client = new HttpClient();
var uri = new Uri(BaseUri + "document/");
var content = new StringContent(documentModel);
var request = new HttpRequestMessage(HttpMethod.Post, uri) {Content = content};
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = client.SendAsync(request);
response.Wait();
try
{
var returned = response.Result;
if (returned.StatusCode != HttpStatusCode.OK)
{
throw new Exception(returned.RequestMessage.ToString());
}
var model = JsonConvert.DeserializeObject<Model> (returned.Content.ReadAsStringAsync().Result);
model.FileContents = "";
return model;
}
catch(Exception e)
{
var error = new Exception("Service failure - ", e);
throw error;
}
}
The WebAPI Post:
[HttpPost]
public async Task<HttpResponseMessage> Post([FromBody]Model model)
{
var response = await SubmitNew(model);
return Request.CreateResponse(response);
}
Setting the breakpoint on the return in the Post shows a valid response, but on the response.Result in the MVC Controller it shows 500. I've even tried returning an OK request no matter the response as below:
return Request.CreateResponse(HttpStatusCode.OK, result);
Any ideas as to why the client is showing 500?
In your Global.asax.cs
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// Try adding the following lines:
var configuration = GlobalConfiguration.Configuration;
var formatters = configuration.Formatters;
formatters.Clear();
formatters.Add(new JsonMediaTypeFormatter());
}
// A good idea to have this:
protected void Application_Error()
{
Exception unhandledException = Server.GetLastError();
// Set a breakpoint here or do something to log the exception
}
The code added to Application_Start will ensure that serialization is only to/from JSON. While it may not be what you want in your final code, it may be helpful as a temporary measure to isolate the cause of your problem.
Adding Application_Error should help to catch issues which occur within the WebAPI layer, such as when it serializes the object you've returned from your controller method.
My suspicion is that SubmitNew is returning something which cannot be serialized such as an infinitely recursive reference (example: parent/child structure with mutual references).
I found the issue, there was a timeout error in the WebAPI's logging service that was happening after the initial POST and GET calls result.. Issue is resolved.
I had a similar issue with some code that I "improved" - I saw HttpResponseMessage implements the IDisposable interface so decided to wrap the Return Request.CreateResponse method is a using statement, leading to a 500 error.
Removing the using statement fixed the issue for me.

Calling a GetAll method from a Web Api from your C# client

I was looking at examples online and found a tutorial on retrieving products that uses a C# WEB API service and a C# console application as the client however, the tutorial defines a function in the service to get all products however, it does not tell you how to call it from the client:
public IEnumerable<Product> GetAllProducts()
{
...
}
The other CRUD methods declare using IHttpActionResult so I was confused how to call it.
So in my client I blindly attempted to call doing the following which is obviously incorrect:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:59888/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP GET ALL
HttpResponseMessage response = await client.GetAsync("api/products");
if (response.IsSuccessStatusCode)
{
List<Model.Product> products = await.response.Content.ReadAsAsync<IEnumerable<Model.Product>();
}
}
This gives me a syntax error. So how do I code it them? Do I need to change the server or the client code or both?
For a single product the code is and this works:
// HTTP GET Specific Product
response = await client.GetAsync("api/products/1");
if (response.IsSuccessStatusCode)
{
Model.Product product = await response.Content.ReadAsAsync<Model.Product>();
}
and the server is:
public IHttpActionResult GetProduct(int id)
{
var product = repository.GetByID(id);
if (product != null)
return Ok(product);
else
return NotFound();
}
List<Model.Product> products = await.response.Content.ReadAsAsync<IEnumerable<Model.Product>();
You missed a closing > at the end. Correct:
var products = await.response.Content.ReadAsAsync<IEnumerable<Model.Product>>();

Return to MVC controller after posting to Web Api

I'm posting some data to an web api controller method from an MVC controller with this method..
private static async Task<HttpResponseMessage> SendDataToApi (List<TogglRow> input)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:****/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.PostAsJsonAsync("api/service", input);
//if (response.StatusCode == HttpStatusCode.OK)
//{
// var resultUri = response.Headers.Location;
//}
return response;
}
}
This is the Web Api method i'm posting to..
public HttpResponseMessage Post(HttpRequestMessage request, List<Dagsrapport> value)
{
if (value != null)
{
var rapporter = value.ToList();
//send rapporter to DB
var response = new HttpResponseMessage(HttpStatusCode.OK);
return response;
}
return request.CreateResponse(HttpStatusCode.BadRequest);
}
Now, the post works fine and i'm returning HttpStatusCode.OK. But i'm not beeing redirected back to the method i'm performing the post from (SendDataToApi()). I'm beeing returned back to the page from wich the post was triggered. I can see the page is working (waiting for localhost..) but nothing happens.
I should probably mention that this is two separate projects (MVC & WebApi), but in the same solution.
What am i missing?
EDIT - Solved
The problem I had was due to the method that ran the task "SendDataToApi" was not set to async. Therefore, it did not wait for an results from the post, but instead ran synchronously and the control never returned to the method that ran SendDataToApi, instead it returned to the original caller - the UI.
Here is the method that is runnig the SendDataToApi task..
[HttpPost]
public async Task<ActionResult> Edit(IEnumerable<TogglRow> tr)
{
var listToExport = tr.Where(x => x.Export.Equals(true));
var result = listToExport.ToList();
var response = await SendDataToApi(result);
return RedirectToAction("Index", "Home",
response.StatusCode == HttpStatusCode.OK ? new { message = "Record(s) were successfully stored." } : new { message = "No records selected." });
}
It seems you have some fundamental misunderstandings about how all this works. MVC actions and WebAPI actions work very differently, which is why they're actually in entirely different namespaces, even though they both implement similar components.
If you need to connect to a Web API from an MVC action, you shouldn't be receiving the response as an HttpResponseMessage. That's a return value for a WebAPI action, similar to how a ViewResult is a return value for an MVC action. It has no meaning to anything in MVC. Rather, your actual response from HttpClient, for example, will be a string (technically a byte array) with a content type indicating that it should be interpreted as plain text, JSON, XML, etc.
Based on the content type, you'll process this response accordingly. If it's JSON, for example, then you can use something like JObject from Newtonsoft.Json (default JSON interpreter in MVC). Then, you could use this data object to construct your response for your MVC action. If you have something indicating that a redirect should be made, then the MVC action can return on of the Redirect* family of results. Importantly, you can't just make the redirect the response of the Web API action, because that merely affects the HttpClient response object.

Categories

Resources