Forking a json file using fetch method triggers error in my code. Seems the response from the server is not right. Into the details, for the ads model below
//Advertisement model
App.Tasks.Model.Ads = Backbone.Model.extend({
url: "ads/ads.json",
intialize: function () {
},
Next: function () {
var ads = this.get("ads");
return ads[Math.ceil(Math.random(0, ads.legth) * 10)];
}
});
how should the server response be when calling fetch(). Right now it is as below
{ads: ["1.png", "2.png", "3.png"]}
and doing this triggers the error callback
//Advertisement model
App.Tasks.Ads = new App.Tasks.Model.Ads();
App.Tasks.Ads.fetch({
success: function (model, response) {
console.log("Success", arguments);
},
error: function (model, response) {
console.log("Error", arguments);
}
});
Your server responds with an invalid JSON, the left part in a name/value pair must be a string, which means that ads should be wrapped in double quotes:
{"ads": ["1.png", "2.png", "3.png"]}
For the complete reference, check http://www.json.org/
Related
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.
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().
I would like to create webservices returning json. However, I'm always getting 'text/html' as the responses content type.
First shot:
public StringContent Get()
{
List<Cell> list = new List<Cell>();
Cell c = new Cell("Cell1");
Cell c2 = new Cell("Cell2");
list.Add(c);
list.Add(c2);
return new StringContent(
Newtonsoft.Json.JsonConvert.SerializeObject(list),
Encoding.UTF8,
"application/json");
}
Responsecontent: System.Net.Http.StringContent
second shot:
public List<Cell> Get()
{
Cell c = new Models.Cell("Cell1");
List<Cell> list = new List<Cell>();
list.Add(c);
return list;
}
Responsecontent: System.Collections.Generic.List`1[TestApp.Models.Cell]
This is how I access the endpoint:
$.ajax({
url: "http://localhost:54787/Cell/Get",
type: "GET",
contentType:"application/json",
accepts: {
text: "application/json"
},
success: function (response) {
$("#result").html(JSON.parse(response));
},
error: function (xhr, status) {
alert("error");
}
});
If you have no good reason to do serialization manually, you should use Web API default mechanism by returning object instead of StringContent. For example, you can change your method to return List<Cell> directly.
public List<Cell> Get()
{
// return List<Cell> just like you write a typical method
}
This way, you will not get text/html anymore. However, you will still get XML in Chrome. It is because Chrome's default HTTP Accept header contains application/xml, and it is supported by default in Web API. If you have no need to support XML result, so you can remove it by the following code during startup (maybe in Global.asax)
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
PS: If you don't know whether you need XML or not, then you don't need it.
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
I still don't understand how the Promise API works. I want to know if there's a way to get a data whenever I need it without calling multiple HTTP request. Here's an exemple :
Session Service :
All it does is either get the session object (which contains datas) or get session ID which returns a number.
app.factory('sessionFactory', ['$resource', 'requestFactory',
function ($resource, requestFactory) {
var oSession = {};
var session = {
/**
* Get session ID
* #return {Number}
*/
sessionID: function () {
if (typeof oSession.id !== "undefined") {
return oSession.id;
} else {
return requestFactory.getObject('/application/current_session').then(function (response) {
oSession = response;
return oSession.id;
});
}
},
/**
* Get session object (GET)
* #return {Object} data in JSON format
*/
getCurrentSession: function () {
if (!oSession.id) {
return requestFactory.getObject('/application/current_session').then(function (response) {
oSession = response;
return oSession;
});
}
}
};
return session;
}]);
Request HTTP Service :
This service only does HTTP request.
app.factory('requestFactory', ['$http', '$q', '$timeout',
function ($http, $q, $timeout) {
return {
getObject: function (jsonURL, params) {
// $q service object
var deferred = $q.defer();
// regular ajax request
$http({
method: 'GET',
url: jsonURL,
params: params
})
.success(function (result, status, headers, config) {
// promise resolve
deferred.resolve(result);
})
.error(function (result, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
deferred.reject('Erreur request : ' + status);
});
return deferred.promise;
}
};
}]);
So to get my Session Object, I do sessionFactory.getCurrentSession with callback function(then...) and it works perfect. Later on, I only need to get the session ID so I would do sessionFactory.sessionID, but it works only if I add the callback function (then...), why is that? I thought my global JavaScript object oSession already has data since the first HTTP request.
I want to prevent doing a spaghetti code and keep the code as clean as possible, with a more object approach. Is it possible?
It looks like you're trying to do too much with the promise API. It's already built into the $http service so you shouldn't need to invoke it yourself. Try this instead:
app.factory('requestFactory', ['$http',
function ($http) {
return {
getObject: function (jsonURL, params) {
// regular ajax request
return $http({
method: 'GET',
url: jsonURL,
params: params
})
.success(function (result, status, headers, config) {
return result;
})
.error(function (result, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
throw new Error('Erreur request : ' + status);
});
}
};
}]);
By returning the result of the $http call, you are in fact returning a promise. You can then chain additional resolution code onto the return value. See the "Chaining Promises" section of the $q documentation.
IF you want to cache your previous http response so that it will not make an http call again
you can use angular-cached-resource
bower install angular-cached-resource
angular.module('myApp',['ngCachedResource'])
instead of $resource use $cachedResource, it will cache the network call to local storage, every time you make a call it will resolve immediately even though it makes a call to backend and updated the cache.
you can also use angular-cache it will cache all your http get calls you can set timeout as well exclude url in its configuration
bower install angular-cache