Pending Requests When Posting Data to ASP.NET web API Controller from ASP.NET MVC Controller - ajax

Im working on a SOA project using, ASP.NET MVC and ASP.NET WEB API. Both projects are inside different solutions, so we have a mvc site and a web api site and the mvc site calls the web api site using http methods operations.
With that cenario in mind, im facing a strange behaviour when POSTING some data to the web API from my mvc controller. When I do GET operations everything works fine. Here's what Im doing:
Inside my cshtml view, i have a js code that do an ajax call to my MVC controller:
> JS Code inside Create.cshtml:
var create = (function () {
var _init = function (container, starter, url) {
$(starter).autocomplete({
minLength: 3,
focus: function () {
return false;
},
select: function (event, ui) {
var terms = this.value.replace(" ", "").split(',');
terms.pop();
terms.push(ui.item.label);
terms.push("");
this.value = terms.join(",");
return false;
},
search: function (event, ui) {
if (_extractLast(this.value).length < 3)
return false;
return true;
},
source: function (request, response) {
_execute(request, response, url);
}
});
};
var _extractLast = function (term) {
var termArray = term.split(',');
var lastTerm = termArray[termArray.length - 1];
return lastTerm.replace(' ', '');
};
var _execute = function (request, response, url) {
var responseCallback = response;
$.ajax({
url: '\Comunication\Contacts',
async: true,
type: 'POST',
dataType: 'json',
data: { termo: _extractLast(request.term) },
success: function (data) {
responseCallback($.map(data, function (item) {
return { label: item.Name, value: item.Id }
}));
}
});
}
return { init: _init };})();
And from inside my controller I call a method from a specialized Service layer to do a POST request to my WEB API SITE doing the SOA thing, as you can see bellow:
> The MVC CONTROLLER CODE:
public class ComunicationController : BaseController{
[HttpPost]
public async Task<JsonResult> Contacts(string termo)
{
var contacts = new ContactServices.ConsumeAsync(new ContactParameter{Term: termo});
return Json(contacts, JsonRequestBehavior.AllowGet);
}
}
> The Layer SERVICE CODE:
public class ContactServices{
public async Task<List<ContactsDTO>> ConsumeAsync(ContactParameter parameter)
{
using (System.Net.Http.HttpClient httpClient = new System.Net.Http.HttpClient())
{
httpClient.BaseAddress = new Uri("http://localhost:123456/api/comunication/contacts");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await httpClient.PostAsJsonAsync(new Uri("http://localhost:123456/api/comunication/contacts"), parameter);
return JsonConvert.DeserializeObjectAsync<ResponseProxy<List<ContactsDTO>>>(response.Content.ReadAsStringAsync().Result).Result.data;
}
}
}
My WEB API SITE accept the request, process the request like expected, no concerns here. But when I return the HttpResponseMessage from my WEB API CONTROLLER to the MVC Site under a POST operation, the request response hangs on forever pending status according to my network monitor....
So basically my request is hanging, the Protocol status is Pending and I never get the request response, heres is a pic:
Pending Status from My Post Request
As I said, when I do GET requests to my web api, everything works fine but when I do POST requests this happens...

Your problem lies in this line:
return JsonConvert.DeserializeObjectAsync<ResponseProxy<List<ContactsDTO>>>(response.Content.ReadAsStringAsync().Result).Result.data;
The use of the Result property of your Task generates a deadlock on you MVC controller. If an API is asynchronous, then always await the task.
var content = await response.Content.ReadAsStringAsync();
var deserialized = await JsonConvert.DeserializeObjectAsync<ResponseProxy<List<ContactsDTO>>>(content);
return deserialized.data;
To learn more about why you should never block on async methods check this great post:
Don't Block on Async Code

Related

Server-side method does not write to database unless client-side caller is paused/interrupted (via alert)

I am using an Ajax post method to pass a JSON string to a server-side MVC action. The IActionResult method parses the JSON string into an array which is uploaded into SQL Server via Microsoft.Data.SqlClient methods. The IActionResult returns an Ok() result to the caller upon completion.
The anomaly I have observed is that the database upload (server-side) only completes if I pause/interrupt the browser by placing an alert just after the Ajax method (client-side). My code is as follows:
Client-side:
function ExportJSON() {
var myJson = "some JSON stuff goes here";
$.ajax({
type: "POST",
url: "/Dailies/UploadJson/",
dataType: 'json',
data: { jsonString: myJson },
success: function (data) {
console.log(data);
}
});
alert("Your data has been saved.");
}
Server-side action:
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> UploadJson(string jsonString)
{
if (jsonString != null) {
var myJArray = JsonConvert.DeserializeObject<JArray>(jsonString);
TimePunches[] timePunches = myJArray.ToObject<TimePunches[]>();
try
{
string constr = _configuration.GetConnectionString("MYSQLSERVER");
using (SqlConnection sqlConnection = new SqlConnection(constr)) {
await sqlConnection.OpenAsync();
foreach (TimePunches timePunch in timePunches) {
string query = "INSERT INTO TimePunches([Projectid], [CrewLeaderId]) ";
query += "VALUES(#Projectid, #CrewLeaderId) ";
using (SqlCommand cmd = new SqlCommand(query)) {
cmd.Connection = sqlConnection;
cmd.Parameters.AddWithValue("#Projectid", timePunch.Projectid);
cmd.Parameters.AddWithValue("#CrewLeaderId", timePunch.CrewLeaderId);
await cmd.ExecuteNonQueryAsync();
}
}
sqlConnection.Close();
}
}
catch (Exception ex) {
TempData["msg"] = ex.Message;
}
}
return Ok();
}
To reiterate, the server-side action uploads data to the database (as expected) so long as the alert is present in the client-side ExportJSON() method. Conversely, removing the alert causes the database upload to fail.
Any assistance would be greatly appreciated.
I found an answer to this issue. The following link provides an excellent article explaining the nuances of asynchronous JavaScript calls and, in particular, how to manage synchronization issues when using Ajax: https://stackify.com/return-ajax-response-asynchronous-javascript-call/
In my particular case the solution was as simple as adding an async: false qualifier to my Ajax "post" method.

ASP.NET Core API response headers not in expected place

I have an ASP.NET Core API that adds two headers to its response callback_uri and redirect_uri.
The strange thing (to me) is that in my AJAX call to the service, the headers are part of the JSON data, as a headers array, rather than the request object itself. I cannot use jqxhr.getResponseHeader(...) and therefore must interrogate the headers array manually within the response data.
Because the StatusCode is also part of the data it means my AJAX success callback is always called, even when I'm testing for a 400 bad request response, which makes testing less simple.
Web API controller action:
[HttpGet, Route("Authenticate")]
public HttpResponseMessage Authenticate(string applicationId)
{
HttpResponseMessage response;
if(!_security.IsApplicationIdValid(applicationId))
{
response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
response.ReasonPhrase = ErrorMessages.INVALID_APPLICATION_ID;
return response;
}
IAuthenticationProvider authProvider = _security.GetAuthenticationProvider();
response = new HttpResponseMessage(System.Net.HttpStatusCode.Redirect);
response.Headers.Add(HeaderKeyNames.CALLBACK_URI_KEY_NAME, authProvider.GetCallbackUrl());
response.Headers.Add(HeaderKeyNames.AUTHENTICATION_SERVICE_REDIRECT_URI_KEY_NAME, authProvider.GetUrl());
return response;
}
AJAX code:
var settings = {
data: { "applicationId": applicationId },
success: successCallback, // at the moment just writes to console
error: errorCallback, // at the moment just writes to console
method: "GET"
};
$.ajax(url, settings);
Am I doing something wrong on the server-side?
You can use a combination of ResultFilters and ServiceFilterAttribute to add your custom headers. This is particularly useful because:
ServiceFilter enables you to have DI access in your ResultFilter.
You can apply it as an Attribute in the actions you want
You can test it.
Putting all together:
Create the custom result filter class
public class CustomHeadersResultFilter : IResultFilter
{
private readonly IMyService _myService;
public CustomHeadersResultFilter(IMyService myService)
{
_myService = myService;
}
public void OnResultExecuting(ResultExecutingContext context)
{
context.HttpContext.Response.Headers.Add("my-header", _myService.GetData());
// if under CORS, this need to be added otherwise you can't read the headers using xhr.getResponseHeader('my-header')
context.HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "my-header");
}
public void OnResultExecuted(ResultExecutedContext context)
{
// can't add headers here, since it's too late in the pipeline
}
}
Register it in your Startup.ConfigureServices
services.AddTransient<IMyService, MyService>();
// our custom result filter
services.AddTransient<CustomHeadersResultFilter>();
Apply the attribute in the action you want to return the custom headers
[HttpGet("{id}")]
[ServiceFilter(typeof(CustomHeadersResultFilter))]
public ActionResult Get(string id)
{
if (id == "something-bad")
{
return BadRequest("invalid application id");
}
// return a 200 Ok. Check the other types if you want something different
return Ok();
}
Testing all of this with a separate web application, doing an ajax request to the API, you can access the headers:
<script>
var settings = { method: "GET" };
$.ajax('http://localhost:61284/api/values/test', settings)
.done(function (data, textStatus, xhr) {
alert(xhr.getResponseHeader('my-header'));
})
.fail(function () {
alert("error");
});
</script>
Add headers like this: (ofc change the type if needed or define your own)
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
What you're doing is creating a HttpResponseMessage object, serializing it to json and then returning it.
This is why the headers are in the json content, instead of the http response.
What you can do is someting like this:
[HttpGet, Route("Authenticate")]
public IActionResult Authenticate(string applicationId)
{
if(!_security.IsApplicationIdValid(applicationId))
{
return BadRequest(ErrorMessages.INVALID_APPLICATION_ID);
}
IAuthenticationProvider authProvider = _security.GetAuthenticationProvider();
this.Response.Headers.Add(HeaderKeyNames.CALLBACK_URI_KEY_NAME, authProvider.GetCallbackUrl());
this.Response.Headers.Add(HeaderKeyNames.AUTHENTICATION_SERVICE_REDIRECT_URI_KEY_NAME, authProvider.GetUrl());
return StatusCode(302);
}

XML Parsing Error: no root element found in ASP.NET Core 2.0 API

I've encountered this problem, and I couldn't figure it out. I'm using ASP.NET Core 2 and Ajax.
This is what the JavaScript debugger says:
XML Parsing Error: no root element found Location:
http://localhost:52617/api/favorites/ Line Number 1, Column 1:
This is my JavaScript code:
$(".js-toggle-fav").click(function (e) {
function sendPost() {
console.log("inside post send");
var button = $(e.target);
$.ajax({
type: 'POST',
url: "http://localhost:52617/api/Favorites/",
data: {"EventId": #Model.Event.EventId},
contentType: "application/json; charset=utf-8"
});
}
$.getJSON("http://localhost:52617/api/favorites/#Model.Event.EventId", function (data) {
if (data == null) {
console.log("fav is null");
sendPost();
fav.addClass(toggling);
fav.text("unfav");
}
else {
console.log("fav is NOT null");
sendPost();
fav.removeClass(toggling);
fav.text("fav");
}
);
});
And my API:
[HttpPost]
public async Task<IActionResult> PostFavorite([FromBody] FavoriteDto favorite)
{
if (!ModelState.IsValid)
{
Console.WriteLine(ModelState.ValidationState.ToString());
return BadRequest(ModelState);
}
var uid = _userManager.GetUserId(HttpContext.User);
var fav = await _context.Favourites.SingleOrDefaultAsync(x => x.EventId == favorite.EventId && x.UserId == uid);
if (fav == null)
{
_context.Favourites.Add(new Favorite { EventId = favorite.EventId, UserId=uid });
}
else
{
_context.Favourites.Remove(fav);
}
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (FavoriteExists(favorite.EventId))
{
return new StatusCodeResult(StatusCodes.Status409Conflict);
}
else
{
throw;
}
}
return Ok(favorite);
}
When I do this with Postman or any restclient, everything works like a charm! With Ajax, that's not the case.
NOTES:
In the same .cshtml file, there's more jQuery and JavaScript code which does something like this, and it's just working! All the solutions
I've checked on the internet didn't work, unfortunately.
The Get methods (for returning List, or single element are working!)
The problem is coming from
data: {"EventId": #Model.Event.EventId},
Instead of passing it in directly, use JSON.Stringify
var payload = {EventId: #Model.Event.EventId};
$.ajax({
type: 'POST',
url: "http://localhost:52617/api/Favorites/",
data: JSON.stringify(payload),
contentType: "application/json; charset=utf-8"
});
I'm assuming your FavoriteDto class looks something like this
public class FavoriteDto
{
public int EventId { get; set; }
}
The reason why you were getting an xml error is that the controller action
public async Task<IActionResult> PostFavorite([FromBody] FavoriteDto favorite)
could not parse 'favorite' so it was never initialised and was null. You then return ok(null) which caused a parsing error on your client side ajax when it recieved the response back from your server.
I encountered this error during a DELETE call to an ASP.Net Core 2.1 Web API. I removed the [FromBody] from the model that was expected from the DELETEcall, such as
public async Task<IActionResult> Delete(MyModel body)
{
// Just an example
await DoSomeWork(body);
return StatusCode(200);
}
but that did not solve the problem. However, after I returned some content with StatusCode, in this example just a simple string, the error was gone. So the following code should avoid the error in Firefox:
public async Task<IActionResult> Delete(MyModel body)
{
// Just an example
await DoSomeWork(body);
return StatusCode(200, "Deletion Successfull");
}
To add to the other answers, this error can also happen if jQuery ajax gets an empty string response where it expects valid JSON, or no content at all.
Check the value of the data in the done callback, if it's an empty string "" rather than undefined, you'll have to fix your server to return status code 204 (No Content) instead of an 200 (OK) with no content.
In WebAPI2 this would mean returning StatusCode(HttpStatusCode.NoContent) instead of Ok().

HttpPost to web api is always a null parater

I'm trying to post some data to a web api app from an angular 2 app. Its only a small json string and when I hit the web api controller, the item is always null - no matter what I've tried.
The json is (I can see this from alerting the data)
{"firstname":"Joe","lastname":"Bloggs"}
The angular function is
createUser(user: Profile): Observable<Response> {
let url = this.usersUrl;
let data = JSON.stringify(user);
let headers = new Headers({'Content-Type': 'application/json; charset=utf-8' });
let options = new RequestOptions({ headers: headers });
return this.http.post(url, data, options);
}
The web api conroller method is:
[HttpPost]
public IActionResult Create(User item)
{
if (item == null)
{
return BadRequest();
}
Users.Add(item);
return CreatedAtRoute("GetUser", new { id = item.Id }, item);
}
but when I break point at Create the User item is null.
I've tried :
public IActionResult Create([FromBody] User item)
but the error message in the Chrome console window is
Failed to load resource: the server responded with a status of 415 (Unsupported Media Type)
I've tried
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' });
but the error is the same as above.
Any ideas?:)
I imagine that your problem is your user model in API. The properties need to be exactly equals.

ASP.NET MVC controller actions that return JSON or partial html

I am trying to create controller actions which will return either JSON or partial html depending upon a parameter. What is the best way to get the result returned to an MVC page asynchronously?
In your action method, return Json(object) to return JSON to your page.
public ActionResult SomeActionMethod() {
return Json(new {foo="bar", baz="Blech"});
}
Then just call the action method using Ajax. You could use one of the helper methods from the ViewPage such as
<%= Ajax.ActionLink("SomeActionMethod", new AjaxOptions {OnSuccess="somemethod"}) %>
SomeMethod would be a javascript method that then evaluates the Json object returned.
If you want to return a plain string, you can just use the ContentResult:
public ActionResult SomeActionMethod() {
return Content("hello world!");
}
ContentResult by default returns a text/plain as its contentType.
This is overloadable so you can also do:
return Content("<xml>This is poorly formatted xml.</xml>", "text/xml");
I think you should consider the AcceptTypes of the request. I am using it in my current project to return the correct content type as follows.
Your action on the controller can test it as on the request object
if (Request.AcceptTypes.Contains("text/html")) {
return View();
}
else if (Request.AcceptTypes.Contains("application/json"))
{
return Json( new { id=1, value="new" } );
}
else if (Request.AcceptTypes.Contains("application/xml") ||
Request.AcceptTypes.Contains("text/xml"))
{
//
}
You can then implement the aspx of the view to cater for the partial xhtml response case.
Then in jQuery you can fetch it passing the type parameter as json:
$.get(url, null, function(data, textStatus) {
console.log('got %o with status %s', data, textStatus);
}, "json"); // or xml, html, script, json, jsonp or text
Another nice way to deal with JSON data is using the JQuery getJSON function. You can call the
public ActionResult SomeActionMethod(int id)
{
return Json(new {foo="bar", baz="Blech"});
}
Method from the jquery getJSON method by simply...
$.getJSON("../SomeActionMethod", { id: someId },
function(data) {
alert(data.foo);
alert(data.baz);
}
);
I found a couple of issues implementing MVC ajax GET calls with JQuery that caused me headaches so sharing solutions here.
Make sure to include the data type "json" in the ajax call. This will automatically parse the returned JSON object for you (given the server returns valid json).
Include the JsonRequestBehavior.AllowGet; without this MVC was returning a HTTP 500 error (with dataType: json specified on the client).
Add cache: false to the $.ajax call, otherwise you will ultimately get HTTP 304 responses (instead of HTTP 200 responses) and the server will not process your request.
Finally, the json is case sensitive, so the casing of the elements needs to match on the server side and client side.
Sample JQuery:
$.ajax({
type: 'get',
dataType: 'json',
cache: false,
url: '/MyController/MyMethod',
data: { keyid: 1, newval: 10 },
success: function (response, textStatus, jqXHR) {
alert(parseInt(response.oldval) + ' changed to ' + newval);
},
error: function(jqXHR, textStatus, errorThrown) {
alert('Error - ' + errorThrown);
}
});
Sample MVC code:
[HttpGet]
public ActionResult MyMethod(int keyid, int newval)
{
var oldval = 0;
using (var db = new MyContext())
{
var dbRecord = db.MyTable.Where(t => t.keyid == keyid).FirstOrDefault();
if (dbRecord != null)
{
oldval = dbRecord.TheValue;
dbRecord.TheValue = newval;
db.SaveChanges();
}
}
return Json(new { success = true, oldval = oldval},
JsonRequestBehavior.AllowGet);
}
To answer the other half of the question, you can call:
return PartialView("viewname");
when you want to return partial HTML. You'll just have to find some way to decide whether the request wants JSON or HTML, perhaps based on a URL part/parameter.
Alternative solution with incoding framework
Action return json
Controller
[HttpGet]
public ActionResult SomeActionMethod()
{
return IncJson(new SomeVm(){Id = 1,Name ="Inc"});
}
Razor page
#using (var template = Html.Incoding().ScriptTemplate<SomeVm>("tmplId"))
{
using (var each = template.ForEach())
{
<span> Id: #each.For(r=>r.Id) Name: #each.For(r=>r.Name)</span>
}
}
#(Html.When(JqueryBind.InitIncoding)
.Do()
.AjaxGet(Url.Action("SomeActionMethod","SomeContoller"))
.OnSuccess(dsl => dsl.Self().Core()
.Insert
.WithTemplate(Selector.Jquery.Id("tmplId"))
.Html())
.AsHtmlAttributes()
.ToDiv())
Action return html
Controller
[HttpGet]
public ActionResult SomeActionMethod()
{
return IncView();
}
Razor page
#(Html.When(JqueryBind.InitIncoding)
.Do()
.AjaxGet(Url.Action("SomeActionMethod","SomeContoller"))
.OnSuccess(dsl => dsl.Self().Core().Insert.Html())
.AsHtmlAttributes()
.ToDiv())
You may want to take a look at this very helpful article which covers this very nicely!
Just thought it might help people searching for a good solution to this problem.
http://weblogs.asp.net/rashid/archive/2009/04/15/adaptive-rendering-in-asp-net-mvc.aspx
PartialViewResult and JSONReuslt inherit from the base class ActionResult. so if return type is decided dynamically declare method output as ActionResult.
public ActionResult DynamicReturnType(string parameter)
{
if (parameter == "JSON")
return Json("<JSON>", JsonRequestBehavior.AllowGet);
else if (parameter == "PartialView")
return PartialView("<ViewName>");
else
return null;
}
For folks who have upgraded to MVC 3 here is a neat way
Using MVC3 and Json
public ActionResult GetExcelColumn()
{
List<string> lstAppendColumn = new List<string>();
lstAppendColumn.Add("First");
lstAppendColumn.Add("Second");
lstAppendColumn.Add("Third");
return Json(new { lstAppendColumn = lstAppendColumn, Status = "Success" }, JsonRequestBehavior.AllowGet);
}
}
Flexible approach to produce different outputs based on the request
public class AuctionsController : Controller
{
public ActionResult Auction(long id)
{
var db = new DataContext();
var auction = db.Auctions.Find(id);
// Respond to AJAX requests
if (Request.IsAjaxRequest())
return PartialView("Auction", auction);
// Respond to JSON requests
if (Request.IsJsonRequest())
return Json(auction);
// Default to a "normal" view with layout
return View("Auction", auction);
}
}
The Request.IsAjaxRequest() method is quite simple: it merely checks the HTTP headers for the incoming request to see if the value of the X-Requested-With header is XMLHttpRequest, which is automatically appended by most browsers and AJAX frameworks.
Custom extension method to check whether the request is for json or not so that we can call it from anywhere, just like the Request.IsAjaxRequest() extension method:
using System;
using System.Web;
public static class JsonRequestExtensions
{
public static bool IsJsonRequest(this HttpRequestBase request)
{
return string.Equals(request["format"], "json");
}
}
Source : https://www.safaribooksonline.com/library/view/programming-aspnet-mvc/9781449321932/ch06.html#_javascript_rendering

Resources