How to consume Web API Get method in C# MVC - c#

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.

Related

ASP.Net Core Web API Actions returning anonymous types

I am using ASP.Net Core 5 to create a web API. I use controllers like this
[Route("[controller]")]
[ApiController]
public class User : ControllerBase
{
...
public async Task<ActionResult<User>> GetUserByID(int id)
{
...
}
...
}
This works fine but means I keep creating defined typed classes for the data I am returning. I am interested in returning an anonymous type sometimes rather than a specific type, is this possible?
You can use IActionResult. For example:
[HttpGet, Route("getUserById/{id}")]
public async Task<IActionResult> GetUserByID(int id)
{
var data = await Something.GetUserAsync(id);
return Ok(new
{
thisIsAnonymous = true,
user = data
});
}
One thing you "could" do is to return a "string" type all the time by serializing the data - either into a JSON sting or XML. And then interpret accordingly on the client. However, you should ideally look at using the "ProducesResponseType" feature as well as several in-built helper methods to produce different responses based on different conditions - that way you can return different types based on different scenarios. See example below:
[HttpGet]
[ProducesResponseType(typeof(User), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(User), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(User), StatusCodes.Status400BadRequest)]
public async Task<ActionResult<User>> GetUserByID(int id)
{
try
{
User model = await _userService.Get(id);
return Ok(model);
}
catch (ApiAccessException apiException)
{
ApiFailureDetail detail = new ApiFailureDetail { ApiError = apiException.ApiError, TechnicalMessage = apiException.TechnicalMessage, UserFriendlyMessage = apiException.UserFriendlyMessage };
//Serialize the exception
string errorOutput = JsonConvert.SerializeObject(detail);
return Unauthorized(errorOutput);
}
catch (ApiException apiException)
{
ApiFailureDetail detail = new ApiFailureDetail { ApiError = apiException.ApiError, TechnicalMessage = apiException.TechnicalMessage, UserFriendlyMessage = apiException.UserFriendlyMessage };
string errorOutput = JsonConvert.SerializeObject(detail);
return BadRequest(errorOutput);
}
catch (Exception e)
{
ApiFailureDetail detail = new ApiFailureDetail { ApiError = ApiError.InternalError, TechnicalMessage = e.Message, UserFriendlyMessage = "Internal unknown error." };
string errorOutput = JsonConvert.SerializeObject(detail);
return BadRequest(errorOutput);
}
}

Passing data from one controller to another in asp.net mvc

I am new to ASP.NET MVC. I have RestCallcontroller class. I want to pass data from Restcontroller to Home Controller.
This is theRestController
public class RestCallController : Controller
{
public string loginJsonString;
Result result = new Result();
// GET: RestCall
public async Task<ActionResult> RunAsync(string a, string b)
{
using (var handler = new HttpClientHandler { UseDefaultCredentials = true })
using (var client = new HttpClient(handler))
{
var byteArray = Encoding.ASCII.GetBytes("username:password");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
client.BaseAddress = new Uri("XXX");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("XXX");
if (response.IsSuccessStatusCode)
{
//Get the response
loginJsonString = await response.Content.ReadAsStringAsync();
result.loginJsonStringop = loginJsonString;
//Json deserialization
VehicleResponse vehicleobj = JsonConvert.DeserializeObject<VehicleResponse>(loginJsonString);
List<string> modelList = new List<string>();
List<string> descriptionList = new List<string>();
foreach (Vehicle veh in vehicleobj.Vehicles)
{
var model = veh.Model;
var Description = veh.Description;
modelList.Add(model);
var modellist = modelList;
descriptionList.Add(Description);
}
}
}
return RedirectToAction("Index", "HomeController",new { resultop =result });
}
}
Following is my HomeController.
public class HomeController : Controller
{
string a;
string b;
// GET: Home
public ActionResult Index(Result resultop)
{
RestCallController restcallController = new RestCallController();
restcallController.RunAsync(a,b);
resultop.loginJsonStringop = restcallController.loginJsonString;
return View(resultop);
}
}
This is my model class.
public class Result
{
public string loginJsonStringop { get; set; }
public string modelop { get; set; }
public string descriptionop { get; set; }
}
I want to pass value of loginJsonString, modelList,descriptionList to index() method in Home Controller and view that in index view. If you have any suggestions please help me.
We have TempData in MVC to pass the data from one controller to another. you can even refer the Answer.
In your first controller you can do something.
TempData["jsonData"] = ANY_OBJECT;
And then in you home controller you can get it.
var object = TempData["jsonData"];
Update
Temp Data Limitation to keep in mind
but there is a catch.. temp data will be available only first call to controller. if you redirect to home pass after your rest controller you will be able to get temp data in home controller, but if you did some redirection and then you directed to home, and tried to get temp data it will not work. if you need that, and think creating proper model, and passing it to home controller is a good solution.
UPDATE
you are trying to pass data using model then you can do something. --
public async Task<ActionResult> RunAsync(string a, string b)
{
...
...
...
Result obj = new Result();
obj.loginJsonStringop = "VALUE_OF_FIELD";
...
...
...
return RedirectToActtion("Index", "HomeController",new { resultop =result });
}
and then you home controller must recieve this model in Index action.
public class HomeController : Controller
{
public ActionResult Index(Result resultop)
{
// do whatever you want to do with your "resultop" instance of type "Result"
var value = resultop.loginJsonStringop; // "VALUE_OF_FIELD"
return View();
}
}

ASP HttpClient GetAsync is not responding, nor timing out

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);
}
}

Call web API from client app

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.

Unit Test ASP.NET Web API controller with fake HTTPContext

I'm using the following approach to upload files through ASP.NET Web API controllers.
[System.Web.Http.HttpPost]
public HttpResponseMessage UploadFile()
{
HttpResponseMessage response;
try
{
int id = 0;
int? qId = null;
if (int.TryParse(HttpContext.Current.Request.Form["id"], out id))
{
qId = id;
}
var file = HttpContext.Current.Request.Files[0];
int filePursuitId = bl.UploadFile(qId, file);
}
catch (Exception ex)
{
}
return response;
}
In my unit tests I've created an HTTPContext class manually before calling the UploadFile action:
var request = new HttpRequest("", "http://localhost", "");
var context = new HttpContext(request, new HttpResponse(new StringWriter()));
HttpContext.Current = context;
response = controller.UploadFile();
Unfortunately, I wasn't able to add custom values to the Form collection, since it's read-only. Also I couldn't change the Files collection.
Is there any way to add custom values to the Form and Files properties of the Request to add needed data (id and file content) during the unit test?
Use some mocking framework like Moq instead. Create a mock HttpRequestBase and mock HttpContextBase with whatever data you need and set them on the controller.
using Moq;
using NUnit.Framework;
using SharpTestsEx;
namespace StackOverflowExample.Moq
{
public class MyController : Controller
{
public string UploadFile()
{
return Request.Form["id"];
}
}
[TestFixture]
public class WebApiTests
{
[Test]
public void Should_return_form_data()
{
//arrange
var formData = new NameValueCollection {{"id", "test"}};
var request = new Mock<HttpRequestBase>();
request.SetupGet(r => r.Form).Returns(formData);
var context = new Mock<HttpContextBase>();
context.SetupGet(c => c.Request).Returns(request.Object);
var myController = new MyController();
myController.ControllerContext = new ControllerContext(context.Object, new RouteData(), myController);
//act
var result = myController.UploadFile();
//assert
result.Should().Be.EqualTo(formData["id"]);
}
}
}
Since you have no control over those classes why not wrap/abstract the functionality behind one do control
IRequestService request;
[HttpPost]
public HttpResponseMessage UploadFile() {
HttpResponseMessage response;
try {
int id = 0;
int? qId = null;
if (int.TryParse(request.GetFormValue("id"), out id)) {
qId = id;
}
var file = request.GetFile(0);
int filePursuitId = bl.UploadFile(qId, file);
} catch (Exception ex) {
//...
}
return response;
}
Where request is one of your custom defined types IRequestService
public interface IRequestService {
string GetFormValue(string key);
HttpPostedFileBase GetFile(int index);
//...other functionality you may need to abstract
}
and can be implemented like this to be injected into your controller
public class RequestService : IRequestService {
public string GetFormValue(string key) {
return HttpContext.Current.Request.Form[key];
}
public HttpPostedFileBase GetFile(int index) {
return new HttpPostedFileWrapper(HttpContext.Current.Request.Files[index]);
}
}
in your unit test
var requestMock = new Mock<IRequestService>();
//you then setup the mock to return your fake data
//...
//and then inject it into your controller
var controller = new MyController(requestMock.Object);
//Act
response = controller.UploadFile();

Categories

Resources