Dot Net Nuke Ajax Response Returning HTML - ajax

I can't seem to get a JSON response from an Ajax post within a Dot Net Nuke site. It returns HTML as a response instead.
I was able to get this to work in a normal test site just fine and am wondering if anybody may know what I need to do.
Below is the code I'm testing with for now:
JavaScript:
$("#ClearTaxFormButton").click(function (e) {
e.preventDefault();
var testValue = 7;
$.ajax({
type: "GET",
url: "localhost/mywebsite/tabid/100/Default.aspx/SumbitByAjaxTest",
data: '{ "taxRate":' + testValue + '}',
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
// Replace the div's content with the page method's return.
//$("#Result").text(msg.d);
alert(msg.d);
}
});
});
C# Function:
//just using ths for testing
[WebMethod]
public static string SumbitByAjaxTest(string taxRate)
{
return taxRate;
}
Like I said, this exact code (aside from a different URL) works fine in a normal .NET site but when I move it over to the Dot Net Nuke site, it returns HTML.
Any ideas??

DNN's service layer allows you to follow a Webapi like approach, I think you'll find that easier for controlling the data to/from.
Here's an example of a controller for an open source articles module
https://dnnsimplearticle.codeplex.com/SourceControl/latest#cs/services/DnnSimpleArticleController.cs
Something like
public HttpResponseMessage GetAllArticles(int portalId, bool sortAsc)
{
try
{
//todo: get the latest X articles?
var articles = ArticleController.GetAllArticles(portalId, sortAsc);
//because of the circular reference when cerealizing the taxonomy within content items we have to build out our article view models manually.
var cleanArticles = new List<ArticleViewModel>();
foreach (Article a in articles)
{
var newArt = new ArticleViewModel
{
ArticleId = a.ArticleId,
Body = WebUtility.HtmlDecode(a.Body),
CreatedByUser = a.CreatedByUser,
CreatedByUserId = a.CreatedByUserId,
CreatedOnDate = a.CreatedOnDate,
Description = WebUtility.HtmlDecode(a.Description),
LastModifiedByUser = a.LastUpdatedByUser,
LastModifiedByUserId = a.LastModifiedByUserId,
LastModifiedOnDate = a.LastModifiedOnDate,
ModuleId = a.ModuleId,
Title = a.Title,
url = DotNetNuke.Common.Globals.NavigateURL(a.TabID, "", "&aid=" + a.ArticleId)
};
cleanArticles.Add(newArt);
}
var articleViewModels = new ArticleViewModels
{
Articles = cleanArticles
};
return Request.CreateResponse(HttpStatusCode.OK, articles);
}
catch (Exception exc)
{
DnnLog.Error(exc); //todo: obsolete
return Request.CreateResponse(HttpStatusCode.BadRequest, "error in request"); //todo: probably should localize that?
}
}

Related

How to download the model as a JSON file?

My model is held in a JavaScript object on the client side, where the user can edit its properties via the UI controls. I want to offer the user an option to download a JSON file representing the model they're editing. I'm using MVC core with .net 6.
What I've tried
Action method (using Newtonsoft.Json to serialize the model to JSON):
public IActionResult Download([FromForm]SomeModel someModel)
{
var json = JsonConvert.SerializeObject(someModel);
var characters = json.ToCharArray();
var bytes = new byte[characters.Length];
for (var i = 0; i < characters.Length; i++)
{
bytes[i] = (byte)characters[i];
}
var stream = new MemoryStream();
stream.Write(bytes);
stream.Position = 0;
return this.File(stream, "APPLICATION/octet-stream", "someFile.json");
}
Code in the view to call this method:
<button class="btn btn-primary" onclick="download()">Download</button>
And the event handler for this button (using jQuery's ajax magic):
function download() {
$.ajax({
url: 'https://hostname/ControllerName/Download',
method: 'POST',
data: { someModel: someModel },
success: function (data) {
console.log('downloading', data);
},
});
}
What happened
The browser console shows that my model has been posted to the server, serialized to JSON and the JSON has been returned to the browser. However no file is downloaded.
Something else I tried
I also tried a link like this to call the action method:
#Html.ActionLink("Download", "Download", "ControllerName")
What happened
This time a file was downloaded, however, because ActionLink can only make GET requests, which have no request body, the user's model isn't passed to the server and instead the file which is downloaded represents a default instance of SomeModel.
The ask
So I know I can post my model to the server, serialize it to JSON and return that JSON to the client, and I know I can get the browser to download a JSON-serialized version of a model, but how can I do both in the same request?
Edit: What I've done with the answer
I've accepted Xinran Shen's answer, because it works as-is, but because I believe that just copying code from Stack Overflow without understanding what it does or why isn't good practice, I did a bit of digging and my version of the saveData function now looks like this:
function saveData(data, fileName) {
// Convert the data to a JSON string and store it in a blob, a file-like
// object which can be downloaded without it existing on the server.
// See https://developer.mozilla.org/en-US/docs/Web/API/Blob
var json = JSON.stringify(data);
var blob = new Blob([json], { type: "octet/stream" });
// Create a URL from which the blob can be downloaded - see
// https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
var url = window.URL.createObjectURL(blob);
// Add a hidden hyperlink to the page, which will download the file when clicked
var a = document.createElement("a");
a.style = "display: none";
a.href = url;
a.download = fileName;
document.body.appendChild(a);
// Trigger the click event on the hyperlink to download the file
a.click();
// Release the blob's URL.
// Browsers do this when the page is unloaded, but it's good practice to
// do so as soon as it's no longer needed.
window.URL.revokeObjectURL(url);
// Remove the hidden hyperlink from the page
a.remove();
}
Hope someone finds this useful
First, Your code is right, You can try to access this method without ajax, You will find it can download file successfully,But You can't use ajax to achieve this, because JavaScript cannot interact with disk, you need to use Blob to save the file. change your javascript like this:
function download() {
$.ajax({
url: 'https://hostname/ControllerName/Download',
method: 'Post',
data: { someModel: someModel },,
success: function (data) {
fileName = "my-download.json";
saveData(data,fileName)
},
});
}
var saveData = (function () {
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
return function (data, fileName) {
var json = JSON.stringify(data),
blob = new Blob([json], {type: "octet/stream"}),
url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
};
}());
I think you may need FileStreamResult, also you need to set the MIME type to text file or json file.
// instead of this
return this.File(stream, "APPLICATION/octet-stream", "someFile.json");
// try this
return new FileStreamResult(stream, new MediaTypeHeaderValue("text/plain"))
{
FileDownloadName = "someFile.txt"
};
// or
return new FileStreamResult(stream, new MediaTypeHeaderValue("application/json"))
{
FileDownloadName = "someFile.json"
};
Reference: https://www.c-sharpcorner.com/article/fileresult-in-asp-net-core-mvc2/

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.

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().

Why is asp.net webAPI always returning text/html?

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.

ajax post not mapping parameters to asp.net controller action

I have this weird issue. I just switched from develoment pc (win7 to win10) and when I run my website now (in debug) the parameters being posted by an ajax call are not being mapped to the parameters on the controller action. On my old development pc it is being mapped, but on my new one it just won't do it.
this is my ajax call:
var obj = { companies: activeCompanyFilters, vesselTypes: activeVesselTypeFilters, vessels: activeVesselFilters, mapFilterType: getMapFilterType(), flushSession: flushSession };
var json = JSON.stringify(obj);
$.ajax({
type: "POST",
url: $("#filter-section").attr("data-source-link"),
data: json,
contentType: "application/json; charset=utf-8",
success: retrieveSuccess
}).done(() => {
layerChanged();
if (indicator) {
indicator.close();
}
});
this is the content being sent (captured with fiddler):
{"companies":[],"vesselTypes":[],"vessels":[2317],"mapFilterType":1,"flushSession":false}
and this is my controller:
[AjaxOnly]
public JsonResult FilterMarkers(long[] companies, long[] vesselTypes, long[] vessels, int? mapFilterType, bool? flushSession)
{
if (flushSession.HasValue && flushSession.Value)
Session["HomeSession"] = null;
MapFilterType filterType = HomeControllerHelper.CastToMapFilterTypeOrDefault(mapFilterType, GetSidUser());
var session = HomeControllerHelper.GetCurrentSession(Session, filterType);
var filtered = MapFilter.FilterData(session.VesselDetails, companies, vesselTypes, vessels).ToList();
return new JsonResult()
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new
{
VesselDetails = filtered
}
};
}
I don't know what I'm missing. And I know it works because it works on my old pc. Is there a setting in IIS I'm missing?
I found the problem. Via visual studio you can create a virtual directory if you're using a local iis. i configured it like 'localhost/test'.
Things in the website are configured to Test.
so it was just a casing problem. Just strange only mapping failed but everything else worked

Resources