Sending/Receiving byte[] With AJAX and ASP.NET MVC - c#

Overview: Here is an summed up description of what I'm doing - I have a C# application that's running constantly that contains a HTTPListener and waits for a request. Then there is a MVC Web Application where on one of the pages is a button that fires some JS that does an ajax post to the address that the HTTP Listener is listening on, when that button is clicked, the listener catches this request then the C# application performs some other job (not relevant to the problem) and then produces a byte array. This byte array is then sent back as part of the response to the original request, and so the success function of the ajax post that made the request is triggered with the byte array in the success function's data parameter. Now another ajax post is made, but this time to a MVC controller so that the byte array can be saved in my db:
Here is some code.
First, Here is the ajax post that makes the request to the HTTP Listener, the data passed along with the post is just some metadata that I use (for now ignore the code in the success function):
$.ajax({
type: "POST",
url: 'http://127.0.0.1:8089/',
data: '{ "Action":"Enroll", "RefUser":"6" }',
crossDomain: true,
success: function (data) {
//Code that posts array to server to be saved in DB
}); //end of ajax
And here is the HTTP Listener's callback method that catches the request and sends the response:
var context = listener.EndGetContext(listenerresult);
Thread.Sleep(1000);
var data_text = new StreamReader(context.Request.InputStream,context.Request.ContentEncoding).ReadToEnd();
//functions used to decode json encoded data.
JavaScriptSerializer js = new JavaScriptSerializer();
RequestConfiguration RequestConfig = (RequestConfiguration)js.Deserialize(data_text, typeof(RequestConfiguration));
byte[] templateDataArray = null;
//Do some work and assign value to the byte array, 'templateDataArray'
//I append a status code and a message to the array of bytes and send everthing back as a response
//The values are split using the '<>' characters.
byte[] msgDataArray = System.Text.Encoding.UTF8.GetBytes("1<>Success" + "<>");
byte[] responseArray = new byte[msgDataArray.Length + templateDataArray.Length];
msgDataArray.CopyTo(responseArray, 0);
templateDataArray.CopyTo(responseArray,msgDataArray.Length);
var response = context.Response;
response.ContentLength64 = responseArray.Length;
response.Headers.Add("Access-Control-Allow-Origin", "*");
response.Headers.Add("Access-Control-Allow-Methods", "POST, GET");
response.StatusCode = 200;
response.StatusDescription = "OK";
response.OutputStream.Write(responseArray, 0, responseArray.Length);
response.OutputStream.Close();
Looking now at the success function from the original ajax post:
...
success: function (data) {
//Code that posts array to server to be saved in DB
var returnArray = data.split("<>");
if (returnArray[0] === "1") {
alert(returnArray[1]);
$("#Loader").addClass('hide');
$.ajax({
type: "POST",
url: '#Url.Action("EnrollFingerprintToDB")',
dataType: 'json',
data: { enrollData: NewID + '<>' + returnArray[2] },
success: function (data) {
alert(data.msg);
}
});//end of inner ajax
} //end of if
} //end of success
...
When I do a sample run, here is what I get back in the success function, lets look at the data after it has been split (the var 'returnArray'):
Note that we now post returnArray[2] to the MVC controller, which contains the byte array (encoded as a string right now). Here is the mvc controller that catches that post:
[HttpPost]
public ActionResult EnrollFingerprintToDB(string enrollData)
{
string[] sDataParts = enrollData.Split(new[] { "<>" }, StringSplitOptions.None);
var bytes = System.Text.Encoding.UTF8.GetBytes(sDataParts[1]);
if (FingerprintBLL.InsertFingerprintTemplate(int.Parse(sDataParts[0]),bytes))
{
return Json(new { success = true, msg = "Template successfully saved" });
}
else
{
return Json(new { success = true, msg = "Template could not be saved" });
}
}
What concerns me and what my question is
Why is the array that I send off from the listener different from the one I finally read in the MVC controller? The reason I say that is, Here is the byte array sent from the Listener:
And here is the array the is received at the MVC controller:
As you can see the MVC Controller Action takes in a string that is split into two and the second part is converted to a byte array, and that second part is the byte array in string form received from the listener (take another look at that second ajax post and you'll see it).
I Believe that I'm taking the incorrect approach to send and receive byte data.

Related

Parse JSON from C# to AngularJS

I have an array of file names:
[HttpPost]
public JsonResult GetJSONFilesList()
{
string[] filesArray = Directory.GetFiles("/UploadedFiles/");
for (int i = 0; i < filesArray.Length; i++)
{
filesArray[i] = Path.GetFileName(filesArray[i]);
}
return Json(filesArray);
}
I need this in AngularJS as a list of objects so I can ng-repeat it out and apply filters. I'm unable to figure out how to get the JSON from the MVC controller to AngularJS.
I've tried the following to make it visible to the view for angular to grab, but I don't know how to make the ng-init see the function to return the list. It erros on "SerializeObject(GetJSONFilesList())" saying it doesn't exist in current context.
<div ng-controller="MyController" data-ng-init="init(#Newtonsoft.Json.JsonConvert.SerializeObject(GetJSONFilesList()),
#Newtonsoft.Json.JsonConvert.SerializeObject(Model.Done))" ng-cloak>
</div>
EDIT:
I've tried using http.get.
Test one:
alert('page load');
$scope.hello = 'hello';
$http.get('http://rest-service.guides.spring.io/greeting').
then(function (response) {
$scope.greeting = response.data;
alert($scope.greeting);
});
alert($scope.hello);
The alert in the http.get never fires, the other alerts do however.
Test two:
$http({
url: '/Home/testHello',
method: 'GET'
}).success(function (data, status, headers, config) {
$scope.hello = data;
alert('hi');
});
[HttpPost]
public string testHello()
{
return "hello world";
}
This causes the angular to break and nothing in the .js works.
Test three
alert('page load');
$scope.hello = 'hello';
$scope.GetJSONFilesList = function () {
$http.get('/Home/testHello')
.success(function (result) {
$scope.availableFiles = result;
alert('success');
})
.error(function (data) {
console.log(data);
alert('error');
});
alert('hi');
};
alert($scope.hello);
[HttpPost]
public string testHello()
{
return "hello world";
}
Alerts nothing from within it, other alerts work.
Fixed:
After some googling, I've found that using .success and .error are deprecated and that .then should be used. So by using .then this resulted in the C# being hit via debug.
Then after using console.log on the returned value found that to have anything be returned I needed to return the value from C# using "return Json(myValue, JsonRequestBehavior.AllowGet); "
And by viewing the object in the console in Chrome by using console.log, I could see my values were in the data part of the returned object.
It was stored in data as an array (as I was passing an array).
I could then get the data out of there by assigning the returned value.data to a scope and could call that in the view {{result[1]}} etc.
return Json(filesArray, JsonRequestBehavior.AllowGet);
$scope.fileList;
$http.get("/Home/GetFileList").then(function (result) {
console.log(result)
$scope.fileList = result.data;
})
Imagine that you divide your front end in three layers (MVC or MVVM) whatever you want.
When you need info from server, the best practice is to separate the logic that makes the request and the logic that manipulates the data.
More info about how to make the request you can find it reading about REST APIS in Consuming a RESTful Web Service with AngularJS.
Normally one of the layers requires the use of services and you can have your controllers and your services (the place where you get the raw data from the server and you make the request. For that you need to use the $http service from angularjs.
$http: The $http service is a core AngularJS service that facilitates communication with the remote HTTP servers via the browser's XMLHttpRequest object or via JSONP.
So basically it shows you how to make get, post and put requests. One example from the documentation is :
// Simple GET request example:
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Pay attention to the url because there is the place where you let your request knwow which method is going to be hit on the server to take the action. If your request is succesful, then you can use the parameter called response. From there, you can do whatever you want. If you decide to make that request part from your controller, you can assign it directly to a variable on your scope. Pay attention if you need to serialize the data. Something like
$scope.myResponseName = response.name ;
The first documentation link from above shows this example which does exactly what I tell you.
angular.module('demo', [])
.controller('Hello', function($scope, $http) {
$http.get('http://rest-service.guides.spring.io/greeting').
then(function(response) {
$scope.greeting = response.data;
});
});
After all the mentioned above, pay attention to what you want to display. Are you going to display the elements of an object array? The use on your HTML the ng-repeat directive. Are you going to display just a variable (No array nor object) then you use need to use an angular expression {{ }}
In summary:
By making an HTTP request, hit the correct method on server.
Make sure you are sending the JSON correctly and that the data is correct.
Retrieve the data on your response.
Assign the data to a variable on your scope and serialize the data if needed.
Display the data correctly depending if it is within an array, if it´s an object or if its just a variable.
I hope the explanation makes sense and check the documentation if you need more info.
You can build your viewmodel so that it contains the data you'd like to serialize and then pass it to angularJS in your view as follows:
<div ng-controller="MyController" data-ng-init="init(#JsonConvert.SerializeObject(myArrayData),
#Newtonsoft.Json.JsonConvert.SerializeObject(Model.Done))" ng-cloak>
and then in your angular controller have a function to receive the data as follows:
$scope.init = function (myArrayData) {
//do something with data
};
The above assumes you're trying to pass data from mvc to angularjs on page load. If you're trying to hit a controller and get data back to angularjs upon some event such as a button click, then you can write an angularjs function similar to the following (this will be an asynchronous request):
app.controller('MyController', function ($scope, $http, $window) {
$scope.ButtonClick = function () {
var post = $http({
method: "POST",
url: "/SomeController/SomeAjaxMethod",
dataType: 'json',
data: { path: $scope.Path},
headers: { "Content-Type": "application/json" }
});
post.success(function (data, status) {
//do something with your data
});
post.error(function (data, status) {
$window.alert(data.Message);
});
}
}
and your controller action would look something like:
[HttpPost]
public JsonResult SomeAjaxMethod(string path)
{
string[] filesArray = Directory.GetFiles(path);
for (int i = 0; i < filesArray.Length; i++)
{
filesArray[i] = Path.GetFileName(filesArray[i]);
}
return Json(filesArray);
}
other answers say to use .success in the angular function, .success and .error are deprecated, instead .then should be used.
Working result:
MVC:
public JsonResult GetFileList()
{
//form array here
return Json(myArray, JsonRequestBehavior.AllowGet);
}
The function needs to be of type JsonResult, and the returned value of Json using JsonRequestBehavior.AllowGet.
AngularJS:
$scope.fileList;
$http.get("/Home/GetFileList").then(function (result) {
console.log(result)
$scope.fileList = result.data;
})
This is in my AJS controller, using .then instead of .success. If you use console.log the result returned from the mvc controller and view it in the browser inspect you'll see the object with lots of other info and the values you want are in the .data section of the object.
So to access the values you need to do result.data. In my case this gives me and array. I assign this to a scope. Then in my view I can access the values by doing {{fileList[1]}} etc. This can also be used in ng-repeat e.g:
<div ng-repeat="file in fileList">
{{fileList[$index]}}
</div>
Each value in the array in the repeat can be accessed using $index which is the number of the repeat starting at 0.

POST AJAX request via HTTPHandler to another HTTPHandler

I have a button on a website which sends an AJAX request to a .NET HTTPHandler. In the ProcessRequest method of this handler, it then took the query string parameters, and created a new WebRequest to another URL and appended the parameters onto it.
However, the AJAX request has been changed to use POST rather than GET (amongst other things).
This is what I need to do:
public void ProcessRequest(HttpContext context)
{
// Take POST data from request and create a new request
// with it to another URL
}
What makes it more complicated is that the POST data contains multiple arrays all called 'points' which hold latitude, longitude and altitude. So I can't use context.Request["points"] as that doesn't return an array of my points arrays. And I've tried using context.Request.Form.GetValues("points") but that doesn't seem to work, presumably because the data wasn't submitted as a form.
Edit:
AJAX request is:
$.ajax({
url: self.stationsAllFile,
data: data,
type: 'POST',
success: function (response) {
// store stations as kml string
self.stationsCurrent = response;
self.showStations(self.stationsCurrent, false);
self.listStations(self.stationsCurrent, finishedLoading);
var stations = $.parseXML(response),
$stations = $(stations),
$placemarks = $stations.find('Placemark');
$placemarks.each(function () {
var $placemark = $(this),
latLng = $placemark.find('coordinates').text(),
latLngSplit = latLng.split(','),
lng = latLngSplit[0],
lat = latLngSplit[1];
excludePoints.push(lng + ',' + lat + ',0.000000');
});
// do we need to go again?
if(endPoint < route.length) {
self.processRoute(route, endPoint + 1, excludePoints);
}
},
error: function () {
self.showError(self.language.errors.unknownError);
},
dataType: 'text'
});
Example POST data (pasted from Fiddler):
points%5B%5D=-2.2349300000000003%2C53.48073%2C0.000000&points%5B%5D=-2.26805%2C53.559020000000004%2C0.000000&type=route

Server result to webpage

I am trying to simply write out some data to my webpage as a result of a callback. Everything works up until the point where I need to output the result of the callback.
Client-side:
function toServer(data) {
var dataPackage = data + "~";
jQuery('form').each(function () {
document.getElementById('payload').value = JSON.stringify({ sendData: dataPackage });
$.ajax({
type: "POST",
async: true,
url: window.location.href.toString(),
data: jQuery(this).serialize(),
success: function (result) {
//this does not work because it just puts an entire source code copy of my site in there instead...
//document.getElementById('searchResults').value = result
console.log("callback compelete");
},
error: function(error) {
console.log("callback Error");
}
});
});
}
Server-Side: (on page load)
//holds actions from page
string payload = HttpContext.Current.Request.Form["payload"] ?? String.Empty;
// See if there were hidden requests (callbacks)
if (!String.IsNullOrEmpty(payload))
{
string temp_AggregationId = CurrentMode.Aggregation;
string[] temp_AggregationList = temp_AggregationId.Split(' ');
Perform_Aggregation_Search(temp_AggregationList, true, Tracer);
}
else
{
HttpContext.Current.Session["SearchResultsJSON"] = "";
}
The rest of the server-side code works properly and just handles the parsing of the incoming and performs a search of the db and then parses the search results into a JSON obj.
Currently, the only way the json obj gets written to the page is if I call it without the callback (just call it on page load). Also, in firebug, it looks like the entire page source is posting back as the 'result' of the callback. I do see my json result within the posted back 'result' but it also contains the entire page HTML.
Moreover, I can't seem to get the result to post to the page which is the whole point. Actually, I could get the result to post to the page by simply uncommenting that bit in the client side code but it posts a copy of my site and not the actual result I thought I created...
What am I missing? How do you explicitly state in the C# code what is returned to the JS callback as 'result'?
You get the entire page because you're making a request to an ASP.NET page. In fact, you're requesting the vary same page you're viewing. The server is returning what it would return if you were submitting a form.
To get JSON data, you need to create a web method to handle your request. Read this article, it will help you. It shows you how to return simple text, but you can return JSON too. Information on this MSDN article.
Finally, to make sure jQuery is parsing the server response as JSON, change your request and indicate it explicitly:
function toServer(data) {
var dataPackage = data + "~";
jQuery('form').each(function () {
document.getElementById('payload').value = JSON.stringify({ sendData: dataPackage });
$.ajax({
type: "POST",
async: true,
url: window.location.href.toString(),
data: jQuery(this).serialize(),
dataType: 'json',
success: function (result) {
//this does not work because it just puts an entire source code copy of my site in there instead...
//document.getElementById('searchResults').value = result
console.log("callback compelete");
},
error: function(error) {
console.log("callback Error");
}
});
});
}

MVC4 AJAX post JSON

this is my function, where i post json only
function test() {
var imgFile = document.getElementById('image');
// var imgData = JSON.stringify(getBase64Image(imgElem));
//var imgData = Convert.FormBase64String(imgElem);
$.ajax({
type: 'POST',
dataType: 'json',
url: "http://localhost:59102/Contacts/AddContact",
data: "json=" + "{\"token\":\"8mVm/nS1OfpU+nlQLbJjqXJ7kJI=VyLGI2GEKkGgtDt0babrAw==\"}",
success: function (returnPayload) {
console && console.log("request succeeded");
},
error: function (xhr, ajaxOptions, thrownError) {
console && console.log("request failed");
},
processData: false,
async: false
});
and i dont know how to add to my data, image, i need to post json and image
this is my controller
[HttpPost]
[AllowAnonymous]
public JsonResult AddContact(string json, HttpPostedFileBase file)
{}
You can't upload files via AJAX (by design) unless you use a plugin that utilises other 'technology' such as flash, or iframes - this is a security measure as JavaScript reading local files on your machine would not be the best idea
There's an option here: http://jquery.malsup.com/form/
...otherwise I suggest looking up one of the many other alternatives!
after you getting the data to a base 64 , your json should be an object
from your controller your json should be something like this
{"json":"something here that is a string","file":"some file"}
Also on the client side you should have a n object on which you invoke JSON.stringify()
var ob = {json:imageDataAsBase64,file:fileDataAsBinary}
although I dont see the reason to send both.
if what you need is just transafering an image that you just need to get the image as base64 and post it as a json

GET 405 method not allowed

This is my jquery code to call web api
var request = {
RequestId: "123",
DeviceId: "ACU-B2-01-R1",
AccessType: "Unlock",
LoginId: "tester",
Password: "tester"
};
$.ajax({
url: 'http://localhost:55208/api/accesspanel',
type: 'POST',
data: JSON.stringify(request),
dataType: 'jsonp',
contentType: "application/json;charset=utf-8",
success: function (data) {
alert("success");
alert(JSON.stringify(data));
},
error: function (x, y, z) {
alert(x + '\n' + y + '\n' + z);
}
});
When I run this code, nothing happens. Neither the success nor error block gets fired. After checking in the debug console of chrome, this is the error record:
GET http://localhost:55208/api/accesspanel?callback=jQuery18203847100134007633_…22,%22LoginId%22:%22tester%22,%22Password%22:%22tester%22}&_=1364916423737 405 (Method Not Allowed)
send jquery.min.js:2
p.extend.ajax jquery.min.js:2
(anonymous function)
I am, however, able to call my web api method successfully using C# code, which looks like this:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:55208/");
var request = new DeviceAccessRequest
{
RequestId = Guid.NewGuid().ToString(),
DeviceId = "ACU/B2/01/R1",
AccessType ="Unlock",
LoginId = "tester",
Password = "tester" ,
};
var response = client.PostAsJsonAsync("api/accesspanel", request).Result;
if (response.IsSuccessStatusCode)
{
var deviceAccessResponse = response.Content.ReadAsAsync<DeviceAccessResponse>().Result;
}
}
And this is my web api method:
[HttpPost]
public HttpResponseMessage PostDeviceControl(DeviceAccessRequest deviceAccessRequest)
{
var deviceAccessResponse = new DeviceAccessResponse(deviceAccessRequest.RequestId)
{
Status = "OK"
};
var response = Request.CreateResponse<DeviceAccessResponse>(HttpStatusCode.OK, deviceAccessResponse);
return response;
}
The reason you are seeing a GET request in your console is because you specified dataType: 'jsonp' which works only with GET requests (the reason for that is because jQuery translates this to a <script> tag pointing to the url you specified). If you want to be doing cross domain AJAX with other verbs than GET you cannot use JSONP. If you need to use other verbs (such as POST in your case) you have 2 options:
CORS. Take a look at the following video which illustrates how you could enable CORS on your Web API. Obviously for this to work your client side browser need tio support it
Server side bridge on your domain. The idea here is to have some server side script on the domain hosting your javascript code that will send the HTTP request to the API and return the response to the client. Then you will send a regular AJAX POST request to your own domain

Categories