See first how i design my web api action.
[System.Web.Http.RoutePrefix("api/Appointments")]
public class AppointmentsServiceController : ApiController
{
[System.Web.Http.HttpGet, System.Web.Http.Route("UserAppointments/{email}")]
public IHttpActionResult UserAppointments(string email)
{
if (!string.IsNullOrEmpty(email))
{
AppointmentsService _appservice = new AppointmentsService();
IEnumerable<Entities.Appointments> app = _appservice.GetUserWiseAppointments(email);
if (app.Count() <= 0)
{
return NotFound();
}
else
{
return Ok(app);
}
}
else
{
return BadRequest();
}
}
}
Now this way i am calling web api action from my asp.net mvc action by HttpClient.
public async Task<ActionResult> List()
{
var fullAddress = ConfigurationManager.AppSettings["baseAddress"] + "api/Appointments/UserAppointments/" + Session["useremail"];
IEnumerable<Entities.Appointments> app = null;
try
{
using (var client = new HttpClient())
{
using (var response = client.GetAsync(fullAddress).Result)
{
if (response.IsSuccessStatusCode)
{
var customerJsonString = await response.Content.ReadAsStringAsync();
app = JsonConvert.DeserializeObject<IEnumerable<Entities.Appointments>>(customerJsonString);
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(response.Content.ReadAsStringAsync().Result);
//MessageBox.Show(dict["Message"]);
}
}
}
}
catch (HttpRequestException ex)
{
// catch any exception here
}
return View();
}
}
i want capture the return IEnumerable and if not data return that also i have to capture. please show me the right direction.
Where i made the mistake. thanks
Mixing async with blocking calls like .Result
var response = client.GetAsync(fullAddress).Result
And
response.Content.ReadAsStringAsync().Result
can lead to deadlocks, which is possibly why it it not hitting your API.
Refactor the code to be async all the way.
That would mean updating the using to
var response = await client.GetAsync(fullAddress)
and the reading of the content in the else statement to
await response.Content.ReadAsStringAsync()
Reference Async/Await - Best Practices in Asynchronous Programming
It looks like you are not awaiting your GetAsync call, so in the following if (response.IsSuccessStatusCode) is probably always returning false. Try calling your method like this:
using (var response = (await client.GetAsync(fullAddress)).Result)
{
Related
AI am just moving to ASYNC methods and trying to get my data to update. I can select just find so I know the repository is working.
Action
[HttpPost]
public async Task<IActionResult> EditTeam(EmployeeVm empVm)
{
if (!ModelState.IsValid)
{
ModelState.AddModelError("", _errorUpdateMsg);
}
else
{
if (await _teamRepository.UpdateEmployee(empVm.Employee))
{
return RedirectToAction("Index");
}
ModelState.AddModelError("", _errorUpdateMsg);
}
return View(empVm);
}
My Constructor in repo
public TeamRepository(EnvisionDbContext envisionDbContext)
{
_envisonDbContext = envisionDbContext;
}
Here is my Update that does not save
public async Task<bool> UpdateEmployee(Employee employee)
{
var result = await _envisonDbContext.Employees.FirstOrDefaultAsync<Employee>(e => e.Id == employee.Id);
if (result != null)
{
result.FirstName = employee.FirstName;
result.LastName = employee.LastName;
result.Phone = employee.Phone;
result.IsActive = employee.IsActive;
await _envisonDbContext.SaveChangesAsync();
return true;
}
return false;
}
Thanks in advance for the help.
UPDATED: If I add this, it works. Is this because the two await calls are disconnected?
result.IsActive = employee.IsActive;
_envisonDbContext.Entry(result).State = EntityState.Modified;
Seems like you forgot update-method before savingchanges
if (result != null)
{
result.FirstName = employee.FirstName;
result.LastName = employee.LastName;
result.Phone = employee.Phone;
result.IsActive = employee.IsActive;
_envisionDbContext.Update(result); //paste it before you save changes
await _envisonDbContext.SaveChangesAsync();
return true;
}
It was working good, but I made some changes in the api, adding more controllers nothing out of the place, and then it stops working, always thrown an exception: "TaskCanceledException: A task was canceled" in the line GetAsync().result. I increase the timeout and infinitely stays loading.
The code controller APP who make a request to the controller API:
public ActionResult Login(LoginM us)
{
try
{
cuentaM account = new cuentaM();
HttpClient client = new HttpClient();
var result = client.GetAsync("http://localhost:26723/api/Login" + "?email=" + us.email + "&password=" + us.password).Result;
if (result.IsSuccessStatusCode)
{
account = result.Content.ReadAsAsync<cuentaM>().Result;
}
Session["cuenta"] = account;
return RedirectToAction("Index", "Home");
}
catch (Exception ex)
{
throw;
}
}
The controller API code:
public HttpResponseMessage Get(string email, string password)
{
try
{
using (elevationbEntities db = new elevationbEntities())
{
usuario user = db.usuarios.Where(m => m.email == email && m.password == password).SingleOrDefault();
cuentaM account = new cuentaM();
if (user != null)
{
account = (from o in db.cuentas
join cu in db.cuentausuarios on o.idCuenta equals cu.idCuenta
join u in db.usuarios on cu.idUsuario equals u.idUsuario
where u.idUsuario == user.idUsuario
select new cuentaM { idUsuario = user.idUsuario, idCuenta = o.idCuenta, CodigoUnico = o.CodigoUnico })
.FirstOrDefault();
}
else
{
account.Error = "Wrong Password or Email";
}
HttpResponseMessage response;
response = Request.CreateResponse(HttpStatusCode.OK, account);
return response;
}
}
catch (TaskCanceledException ex)
{
HttpResponseMessage response;
response = Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
return response;
}
}
Making blocking calls (.Result) on HttpClinet's async API can cause deadlocks especially if being used asp.net MVC, which may have async operations being invoked when the blocking call was made.
Make the code async all the way through.
Also try to avoid creating an instance of HttpClient on every request. This can cause sockets to be exhausted.
private static HttpClient client = new HttpClient();
public async Task<ActionResult> Login(LoginM us) {
try {
cuentaM account = new cuentaM();
var url = "http://localhost:26723/api/Login" + "?email=" + us.email + "&password=" + us.password
var result = await client.GetAsync(url);
if (result.IsSuccessStatusCode) {
account = await result.Content.ReadAsAsync<cuentaM>();
}
Session["cuenta"] = account;
return RedirectToAction("Index", "Home");
} catch (Exception ex) {
throw;
}
}
You may be deadlocking by blocking on an async call, as described in this article. Here's the problematic line:
account = result.Content.ReadAsAsync<cuentaM>().Result;
Change the method signature for Login to:
public async Task<ActionResult> Login(LoginM us)
Then change the problematic line to use await instead of .Result:
account = await result.Content.ReadAsAsync<cuentaM>();
I am trying to display some results after querying a webapi as below -
public async Task<ActionResult> Get()
{
string _address = "someurl";
string results = null;
HttpClient client = new HttpClient(new OAuthMessageHandler(new HttpClientHandler()));
HttpResponseMessage responseTask = await client.GetAsync(_address);
if (responseTask.IsSuccessStatusCode)
{
results = await responseTask.Content.ReadAsStringAsync();
}
ResultModel model = JsonConvert.DeserializeObject<ResultModel>(results);
return View(model);
}
This code works fine and I am able to read the results from "someurl" url. Now, when I refator the code as below (by moving the async data retrieval to another function), it stops working. It forever takes to query and does not return any result. Is this because of some deadlock or am I not using async correctly.
public ActionResult Get()
{
string _address = "someurl";
Task<string> results = HttpClientService.HttpClientService.GetRequest(_address);
IEnumerable<ResultModel> model = JsonConvert.DeserializeObject<IEnumerable<ResultModel>>(results.Result);
return View(model);
}
public static class HttpClientService
{
public static async Task<string> GetRequest(string Url)
{
HttpClient client = new HttpClient(new OAuthMessageHandler(new HttpClientHandler()));
HttpResponseMessage responseTask = await client.GetAsync(Url);
if (responseTask.IsSuccessStatusCode)
{
return await responseTask.Content.ReadAsStringAsync();
}
else
{
return "Sorry buddy, cannot retrieve the requested data!";
}
}
}
Please let me know what am I doing wrong here. I am new to Async implementation.
You should await the GetRequest method. Otherwise you are defeating the purpose of async processing which is to release the thread so it can do other work. In this case you are still blocking the thread when you ask for a result via results.Result.
public async Task<ActionResult> Get()
{
string _address = "someurl";
string results = await HttpClientService.HttpClientService.GetRequest(_address);
IEnumerable<ResultModel> model = JsonConvert.DeserializeObject<IEnumerable<ResultModel>>(results);
return View(model);
}
Frankly I am not sure why your code is not returning but even if it was working correctly it would be pointless to do async requests in this way.
I'm trying to post a list of objects from my winforms application to my asp.net mvc 4 website. I've tested posting one object, and it works, but does not work for the list. It returns a 500 (Internal Server Error). Here is my code:
ASP.NET MVC Web API
public class PostTraceController : ApiController
{
public HttpResponseMessage Post(List<WebTrace> list)
{
try
{
// Some code
return Request.CreateResponse(HttpStatusCode.Created);
}
catch (Exception ex)
{
HttpContext.Current.Trace.Write("exception", ex.Message);
return Request.CreateErrorResponse(HttpStatusCode.ServiceUnavailable, ex);
}
}
public HttpResponseMessage Post(WebTrace item)
{
try
{
// Some code
return Request.CreateResponse(HttpStatusCode.Created);
}
catch (Exception ex)
{
HttpContext.Current.Trace.Write("exception", ex.Message);
return Request.CreateErrorResponse(HttpStatusCode.ServiceUnavailable, ex);
}
}
}
Win forms application
public class BaseSender
{
public BaseSender()
{
Client = new HttpClient
{
BaseAddress = new Uri(#"http://localhost/mywebsite/")
};
Client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
public string UserCode { get; set; }
protected readonly HttpClient Client;
public HttpResponseMessage PostAsJsonAsync(string requestUri, object value)
{
var response = Client.PostAsJsonAsync(requestUri, value).Result;
response.EnsureSuccessStatusCode();
return response;
}
}
public class WebTraceSender : BaseSender
{
private const string requestUri = "api/posttrace";
public bool Post(List<ArchiveCptTrace> list)
{
try
{
var listWebTrace = new List<WebTrace>();
foreach (var item in list)
{
listWebTrace.Add(new WebTrace
{
DateStart = item.DatePreparation,
DateEnd = item.DateCloture,
UserStart = item.UserPreparation.UserName,
UserEnd = item.UserCloture.UserName,
AmountStart = item.MontantPreparation,
AmountEnd = item.MontantCloture,
TheoricAmountEnd = item.MontantTheorique,
Difference = item.Ecart,
UserCode = UserCode
});
}
var responce = PostAsJsonAsync(requestUri, listWebTrace);
return responce.IsSuccessStatusCode;
}
catch (Exception e)
{
// TODO : Trace the exception
return false;
}
}
}
EDIT :
I've found out the scenario of the error, which is having two methods in my api controller, even thought they have different signature. If I comment one method, the post work fine (item or a list). Any ideas ?
The methods may have different signatures, but Web API can't tell the difference between them without inspecting the body, which it won't do for performance reasons.
You could do two things - either create a new class which just holds a list of WebTrace objects, and put that in a different API controller, or you could map a custom route to one of your existing methods. You could do that with ActionName attribute, however, I would probably take the first approach.
I have a controller method that returns a void because it is building an Excel report for the user to download. The Excel 3rd party library we're using is writing to the response itself. The method looks something like this:
[HttpGet]
public void GetExcel(int id)
{
try
{
var report = _reportService.GetReport(id);
var table = _reportService.GetReportTable(id);
var excelReport = new ExcelReport(table, report.Name);
excelReport.DownloadReport(System.Web.HttpContext.Current.Response);
}
catch (Exception ex)
{
// This is wrong, of course, because I'm not returning an ActionResult
Response.RedirectToRoute("/Report/Error/", new { exceptionType = ex.GetType().Name });
}
}
There are several security checks in place that throw exceptions if the user doesn't meet certain credentials for fetching the report. I want to redirect to a different page and pass along some information about the exception, but I can't figure out how to do this in MVC3....
Any ideas?
You could use the following code
Response.Redirect(Url.Action("Error", "Report", new { exceptionType = ex.GetType().Name }));
But have you taken a look at the FilePathResult or FileStreamResult ?
Instead of letting the 3rd part library write to the response directly get the content use regular ActionResult and return File(...) for the actual file or RedirectToAction(...) (or RedirectToRoute(...)) on error. If your 3rd party library can only write to Response you may need to use some tricks to capture it's output.
[HttpGet]
public ActionResult GetExcel(int id)
{
try
{
var report = _reportService.GetReport(id);
var table = _reportService.GetReportTable(id);
var excelReport = new ExcelReport(table, report.Name);
var content = excelReport.MakeReport(System.Web.HttpContext.Current.Response);
return File(content, "application/xls", "something.xls");
}
catch (Exception ex)
{
RedirectToRoute("/Report/Error/", new { exceptionType = ex.GetType().Name });
}
}
You can return an EmptyActionResult:
[HttpGet]
public ActionResult GetExcel(int id)
{
try
{
var report = _reportService.GetReport(id);
var table = _reportService.GetReportTable(id);
var excelReport = new ExcelReport(table, report.Name);
excelReport.DownloadReport(System.Web.HttpContext.Current.Response);
return new EmptyResult();
}
catch (Exception ex)
{
return RedirectToAction("Error", "Report", rnew { exceptionType = ex.GetType().Name });
}
}
Not sure if it works, haven't tested it.
Another approach would be using an exception filter:
public class MyExceptionFilter : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
var routeValues = new RouteValueDictionary()
{
{ "controller", "Error" },
{ "action", "Report" }
};
filterContext.Result = new RedirectToRouteResult(routeValues);
filterContext.ExceptionHandled = true;
// Or I can skip the redirection and render a whole new view
//filterContext.Result = new ViewResult()
//{
// ViewName = "Error"
// //..
//};
}
}