ASP.NET WebApi POST not recognizing form-urlencoded int array - javascript

So this post gives an answer to how to send a post for downloading a file using forms. But my API endpoint isn't recognizing the form data.
[HttpPost]
public async Task<IHttpActionResult> DownloadPdfs([FromBody] IEnumerable<int> ids)
{
if (ids == null || ids.Count() < 1)
{
return BadRequest("No ids supplied.");
}
...
'ids' always has a count of 0.
Here's the JS code:
function downloadFile(ids) {
var win = 'w' + Math.floor(Math.random() * 1000000000000);
window.open('', win, 'width=250, height=100');
var f = $('<form></form>')
.attr({ target: win, method: 'post', action: 'api/pdfs' })
.appendTo(document.body);
_.each(ids, function (id) {
$('<input></input>')
.attr({ type: 'hidden', name: 'ids', value: id })
.appendTo(f);
});
f[0].submit();
f.remove();
}
Initially, I just had it the same as the other linked answer which was just one input appended to the form, in which I set the value to 'ids'. I tried this next, but that still didn't work.
Does anyone know how to adapt this to still use post data to provide my ids, which are needed to create and supply the downloaded file? Or is there a better way to do this?
I've tried doing it just using Ajax, which returns the file content in the responseText, but I can't make use of it in there and I can't get the browser to open it.
Edit 1
I also just tried setting the value to this:
value: '{ids: [' + ids.toString() + ']}'
That didn't work.
Edit 2
Just tried changing the endpoint parameter to this:
([FromBody] int[] ids)
...to no avail.

Related

Field 'id' expected a number but got <SimpleLazyObject: <User: username>>

I have been stuck on this error for some time and I can't wrap my head around the problem or what it even means.
I found some answers but none really solved my issue.
Here is a brief desciption of what I do:
In Javascript, I call this function with an int as a parameter such as:
function follow (user_id) {
// retrieves post for current selection
fetch(`/follow/${user_id}`, {
method: 'PUT',
body: JSON.stringify({
follow: true
})
})
}
My url path, from url.py, is as follow:
path('follow/<int:user_id>', views.follow, name="follow")
finally, in views.py, this function is called:
def follow(request, user_id):
user = User.objects.get(id = user_id)
if request.method == "PUT":
data = json.loads(request.body)
if data.get("follow") is not None:
followed = Followed(
user_id = user.id,
followed_by_id = request.user,
)
followed.save()
return HttpResponseRedirect(reverse("index"))
else:
return HttpResponseRedirect(reverse("index"))
I have tried a few different approaches such as removing the .value but I keep getting the following error:
Field 'id' expected a number but got <SimpleLazyObject: <User: username>>
I check along the way and the ID is an int all the way until it is passed to the model to be saved.
I am using the abstractUser model.
Let me know if more information is needed.
Kind regards,

Twitch TV JSON API Issue

So,I am trying to use the twitch API:
https://codepen.io/sterg/pen/yJmzrN
If you check my codepen page you'll see that each time I refresh the page the status order changes and I can't figure out why is this happening.
Here is my javascript:
$(document).ready(function(){
var ur="";
var tw=["freecodecamp","nightblue3","imaqtpie","bunnyfufuu","mushisgosu","tsm_dyrus","esl_sc2"];
var j=0;
for(var i=0;i<tw.length;i++){
ur="https://api.twitch.tv/kraken/streams/"+tw[i];
$.getJSON(ur,function(json) {
$(".tst").append(JSON.stringify(json));
$(".name").append("<li> "+tw[j]+"<p>"+""+"</p></li>");
if(json.stream==null){
$(".stat").append("<li>"+"Offline"+"</li>");
}
else{
$(".stat").append("<li>"+json.stream.game+"</li>");
}
j++;
})
}
});
$.getJSON() works asynchronously. The JSON won't be returned until the results come back. The API can return in different orders than the requests were made, so you have to handle this.
One way to do this is use the promise API, along with $.when() to bundle up all requests as one big promise, which will succeed or fail as one whole block. This also ensures that the response data is returned to your code in the expected order.
Try this:
var channelIds = ['freecodecamp', 'nightblue3', 'imaqtpie', 'bunnyfufuu', 'mushisgosu', 'tsm_dyrus', 'esl_sc2'];
$(function () {
$.when.apply(
$,
$.map(channelIds, function (channelId) {
return $.getJSON(
'https://api.twitch.tv/kraken/streams/' + encodeURIComponent(channelId)
).then(function (res) {
return {
channelId: channelId,
stream: res.stream
}
});
})
).then(function () {
console.log(arguments);
var $playersBody = $('table.players tbody');
$.each(arguments, function (index, data) {
$playersBody.append(
$('<tr>').append([
$('<td>'),
$('<td>').append(
$('<a>')
.text(data.channelId)
.attr('href', 'https://www.twitch.tv/' + encodeURIComponent(data.channelId))
),
$('<td>').text(data.stream ? data.stream.game : 'Offline')
])
)
})
})
});
https://codepen.io/anon/pen/KrOxwo
Here, I'm using $.when.apply() to use $.when with an array, rather than list of parameters. Next, I'm using $.map() to convert the array of channel IDs into an array of promises for each ID. After that, I have a simple helper function with handles the normal response (res), pulls out the relevant stream data, while attaching the channelId for use later on. (Without this, we would have to go back to the original array to get the ID. You can do this, but in my opinion, that isn't the best practice. I'd much prefer to keep the data with the response so that later refactoring is less likely to break something. This is a matter of preference.)
Next, I have a .then() handler which takes all of the data and loops through them. This data is returned as arguments to the function, so I simply use $.each() to iterate over each argument rather than having to name them out.
I made some changes in how I'm handling the HTML as well. You'll note that I'm using $.text() and $.attr() to set the dynamic values. This ensures that your HTML is valid (as you're not really using HTML for the dynamic bit at all). Otherwise, someone might have the username of <script src="somethingEvil.js"></script> and it'd run on your page. This avoids that problem entirely.
It looks like you're appending the "Display Name" in the same order every time you refresh, by using the j counter variable.
However, you're appending the "Status" as each request returns. Since these HTTP requests are asynchronous, the order in which they are appended to the document will vary each time you reload the page.
If you want the statuses to remain in the same order (matching the order of the Display Names), you'll need to store the response data from each API call as they return, and order it yourself before appending it to the body.
At first, I changed the last else condition (the one that prints out the streamed game) as $(".stat").append("<li>"+jtw[j]+": "+json.stream.game+"</li>"); - it was identical in meaning to what you tried to achieve, yet produced the same error.
There's a discrepancy in the list you've created and the data you receive. They are not directly associated.
It is a preferred way to use $(".stat").append("<li>"+json.stream._links.self+": "+json.stream.game+"</li>");, you may even get the name of the user with regex or substr in the worst case.
As long as you don't run separate loops for uploading the columns "DisplayName" and "Status", you might even be able to separate them, in case you do not desire to write them into the same line, as my example does.
Whatever way you're choosing, in the end, the problem is that the "Status" column's order of uploading is not identical to the one you're doing in "Status Name".
This code will not preserve the order, but will preserve which array entry is being processed
$(document).ready(function() {
var ur = "";
var tw = ["freecodecamp", "nightblue3", "imaqtpie", "bunnyfufuu", "mushisgosu", "tsm_dyrus", "esl_sc2"];
for (var i = 0; i < tw.length; i++) {
ur = "https://api.twitch.tv/kraken/streams/" + tw[i];
(function(j) {
$.getJSON(ur, function(json) {
$(".tst").append(JSON.stringify(json));
$(".name").append("<li> " + tw[j] + "<p>" + "" + "</p></li>");
if (json.stream == null) {
$(".stat").append("<li>" + "Offline" + "</li>");
} else {
$(".stat").append("<li>" + json.stream.game + "</li>");
}
})
}(i));
}
});
This code will preserve the order fully - the layout needs tweaking though
$(document).ready(function() {
var ur = "";
var tw = ["freecodecamp", "nightblue3", "imaqtpie", "bunnyfufuu", "mushisgosu", "tsm_dyrus", "esl_sc2"];
for (var i = 0; i < tw.length; i++) {
ur = "https://api.twitch.tv/kraken/streams/" + tw[i];
(function(j) {
var name = $(".name").append("<li> " + tw[j] + "<p>" + "" + "</p></li>");
var stat = $(".stat").append("<li></li>")[0].lastElementChild;
console.log(stat);
$.getJSON(ur, function(json) {
$(".tst").append(JSON.stringify(json));
if (json.stream == null) {
$(stat).text("Offline");
} else {
$(stat).text(json.stream.game);
}
}).then(function(e) {
console.log(e);
}, function(e) {
console.error(e);
});
}(i));
}
});

Dynamically run Model method with Ajax in C# MVC?

I have a method in a Model which takes over 15 seconds to calculate. This occurs multiple times on the same page and is really causing some slowdown. I would like to run this method asynchronously with the page so that the value appears once it is calculated.
Currently this is how the value is called and displayed (within the Index.cshtml view):
<div>Blocked: #item.GetNumJiraIssuesByStatus("Blocked")</div>
<div>Open: #item.GetNumJiraIssuesByStatus("Open")</div>
I would like the #item.GetNumJiraIssuesByStatus (both Open and Blocked) to be done with Ajax. Here is the method within the Model:
public int GetNumJiraIssuesByStatus(string status)
{
var sJql = "project = \"" + JiraProjectKey + "\" and status = " + status;
//apply a 'result field' filter simply to reduce the amount of data being returned. Any field will do - use of 'resolution' is entirely arbitrary.
var resultFieldNames = new List<string> {"resolution"};
Issues issues = new JiraClient(new JiraAccount()).GetIssuesByJql(sJql, 0, 1000, resultFieldNames);
return issues.total;
}
I have been trying to instead get this done by having a span which populates with the value when the AJAX is done:
$(".jiradata").each(function (i, el) {
var $el = $(this);
$.get("/Project", { s: "open" }, function (data) { text; })
})
Although this does not work properly. Does anyone know what I can do?
Thanks!

Neo4jClient with keylines

Introduction:
I am a newbie to Neo4jClient so forgive me if i ask something that has been asked before. But i have been stuck on this forever.
What i Am trying to do:
I am trying to connect Neo4j with Keylines using .NET.
Cause:
First, i used Neo4j's REST with Jquery AJAX to do Return (*) which returned everything including data and URI's of self, start, end, all_Relationships etc.
data: Object
end: "http://localhost:7474/db/data/node/93"
extensions: Object
metadata: Object
properties: "http://localhost:7474/db/data/relationship/4019/properties"
property: "http://localhost:7474/db/data/relationship/4019/properties/{key}"
self: "http://localhost:7474/db/data/relationship/4019"
start: "http://localhost:7474/db/data/node/6"
type: "SENT"
Hence i was able to use self as a ID for keylines Node and start and end URI's as Keyline's Link id1 and id2.
function queryNeo4j(query) {
var request = $.ajax({
type: "POST",
url: "http://localhost:7474/db/data/cypher",
contentType: "application/json",
data: JSON.stringify({ "query": query, "params": {} }),
success: afterQueryNeo4j
});
};
function afterQueryNeo4j(json) {
console.log(json); // returned data from ajax call
makeNodes(json);
makeLinks(json);
//console.log(items);
createKeylines(items);
};
// populates global var itmes with nodes
function makeNodes(param) {
for (i = 0; i < param.data.length ; i++) {
for (j = 0 ; j < 2 ; j++) {
var node = {
type: 'node',
id: param.data[i][j].self,
dt: stringToDateConverter(String(param.data[i][1].data.yyyyMMdd)),
b: 'rgb(100,255,0)',
c: 'rgb(0,0,255)',
t: param.data[i][j].data.name,
subject: param.data[i][j].data.subject
};
items.push(node);
}
}
};
// populates global var itmes with nodes
function makeLinks(json) {
for (i = 0; i < json.data.length; i++) {
var link = {
type: 'link',
id: json.data[i][2].self,
id1: json.data[i][2].start,
id2: json.data[i][2].end,
t: json.data[i][2].metadata.type,
w: 2,
c: 'rgb(0,0,255)'
}
items.push(link);
}
}
Using this technique i was successfully able to plot keylines graph and timebar using only client side Javascript.
but problem Arose when i published this on IIS, it gave me Cross-Domain error which means i had to call Neo4j from server code (C#) and feed it to client side HTML/JS. That's when i found out about Neo4jClient.
Done so Far:
I am Successfully able to read data from Neo4j in my C# code using
public class Person
{
public string name { get; set; }
public int dob { get; set; }
}
var query = client.cypher
.match(("person:Person"))
.return(person => person.As<Person>());
var result = query.results;
foreach (var i in result)
{
Console.WriteLine("people name is:"+i.name);
}
Problem:
Now, i can read only data based on my Cypher Query but can't read other stuff like id, self, start, end URI's and relationships which i need for my UI.
Is there a way to get the Return (*) and all the other meta data using neo4jclient? Any code will help.
Should i stick to Client side ajax call by resolving cross reference errors using JSONP or CORS since it was much easier but i am afraid that it might give me some problems down the line as it is not the proper way to do it?
I can't seem to find any proper documentation for Neo4jClient.
There are so many options i see for return in Intellisense but dont't know how i can use them.
Any help is greatly appreciated.
Thanks in advance.
You can change your return to be:
.Return(person => person.As<Node<Person>>());
Which will return a wrapper around the Data containing all the node goodness, that should get you there.

Set id attribute after success of ajax call in Kineticjs

I'm using KineticJS in my MVC application.
In order to retrieve data from database, I'm making some ajax calls to web services in the API controller. There is an API that returns an id, which I want to assign to the current Kinetic.Group id attribute on success.
After drag and drop, a popup PopUpAddRoom(FloorId) appears and calls the AddRoom function.
my code:
function AddRoom(FloorId, RoomName, TypeID) {
$.ajax({
beforeSend: function (xhr) { // verify session },
url: "/api/someurl", //url for adding the room, that returns room's id
type: "Post",
dataType: 'json',
success: function (data) {
//data is the room id
var r = rightLayer.find("#" + data)[0];
console.log(r.getType()); // here I don't even get the type, I get "Object []"
var rec = r.find("Rect"); //rec is a rectangle inside the group
rec.setId(data);
rightLayer.draw();
}
});
}
The problem is that r = rightLayer.find("#" + data)[0]; is empty. It returns undefined.
When I call console.log(data); to see if data's content is empty, it returns the correct id !
I initialized the id like this: id: '' and want it to change as it gets the data from database. But this failed. Is there something wrong with this code?
EDIT :
After figuring out that id: '' is really dumb idea (with the help of markE), I tried initializing id to an empty variable ident which gets its value from a web service (this ws increments the id when a new instance is added successfully).
But the problem doesn't come from r = rightLayer.find("#" + data)[0];. It's the fact of assigning the id to a node (location of the instruction)
var ident;
var group = new Kinetic.Group({
...
id: ident
...
});
I added then this line: ident = data; after success of the ajax call, still the same error. It seems like this instruction isn't doing nothing. The value of ident isn't changing after that I call PopUpAddRoom function.
If you initialize the desired node's id to the empty string:
id:''
then this .find will never retrieve that desired node:
var r = rightLayer.find("#" + data);
Instead do something like this:
...
id:'replaceable1'
...
var r = rightLayer.find("#replaceable1")[0];
var rec = r.find("Rect")[0];
// change the id of both r and rec
r.setId(data);
rec.setId(data);
Note that .find always returns a collection (like a fancy array) so to get a reference to an element within that array you must do [n]. For example myLayer.find(something)[0].
According to KineticJS docs:
find(selector) --> return a Kinetic.Collection of nodes that match the selector
So if find returns a collection, in order for you to get the layer, I think something like:
var r = rightLayer.find("#" + data)[0];
should work, taking into account that id's in KineticJS shapes are unique.

Categories