I have an asp.net page.
Inside this page I have an img control/element.
I am calling an ashx page on my server.
This ashx page accepts a timestamp from the client and compares it to a timestamp stored on the server.
If the timestamps do not match then I return an image which has been converted to a byte array (in C#).
If the timestamps do not match then I return a string value of "-1".
So, this is a cut-down of my ashx page:
public void ProcessRequest (HttpContext context) {
context.Response.AddHeader("Access-Control-Allow-Origin", "*");
try
{
string clientTS = context.Request.QueryString["clientTS"];
if (clientTS == serverTS)
{
//new version available. refresh browser
context.Response.ContentType = "text/json";
string value = "-1";
context.Response.Write(value);
}
else
{
context.Response.ContentType = "image/jpg";
byte[] data = Shared.GetMobileNextFrame("par1", 0);
context.Response.BinaryWrite(data);
}
}
catch (Exception ex)
{
context.Response.ContentType = "text/json";
context.Response.Write("ERR");
}
}
And in my javascript code:
function GetImageStatus() {
finished = false;
var val = url + '/Mobile/isNewFrame.ashx?Alias=' + Alias + '&CamIndex=' + camIndex + '&Version=' + version + '&GuidLogOn=' + guidLogOn;
$.ajax({
url: val,
type: 'GET',
timeout: refreshWaitlimit,
data: lastTS,
success: function (response, status, xhr) {
var ct = xhr.getResponseHeader("content-type");
if (ct.indexOf('json') > -1) {
//no update
}
else {
try {
live1x4.src = 'data:image/bmp;base64,' + encode(response);
}
catch (err) {
alert(err);
}
}
},
error: function (jqXHR, textStatus, errorThrown) {
//handle error
}
});
}
function encode(data) {
var str = String.fromCharCode.apply(null, data);
return btoa(str).replace(/.{76}(?=.)/g, '$&\n');
}
But I get an error returned:
TypeError: Function.prototype.apply: Arguments list has wrong type
If I just apply:
live1x4.src = 'data:image/bmp;base64,' + btoa(response);
instead of:
live1x4.src = 'data:image/bmp;base64,' + encode(response);
I get this error:
InvalidCharacterError: btoa failed. the string to be encoded contains
characters outside of the Latin1 range.
I have tried using a canvas control with example code i have found on this site. I do not get an error but I also do not get an image.
I know the image is valid because my old code was point the image.src directly to the ashx handler (and i was not comparing timestamps).
I do not want to encode the byte array to base64 string on the server because that would inflate the download data.
I was wondering if I was using the wrong context.Response.ContentType but I could not see what else I could use.
What am i doing wrong?
When looking at the documentation at MDN you should pass 1 or more parameters to fromCharCode. You pass none in this line:
var str = String.fromCharCode.apply(null, data);
The syntax is:
String.fromCharCode(num1, ..., numN)
There is although the apply method as your said in comments, but you use it the wrong way. The first parameter shouldn't be null.
The syntax of that is (from Convert array of byte values to base64 encoded string and break long lines, Javascript (code golf)):
somefunction.apply(thisObj[, argsArray])
Just use
var str = String.fromCharCode.apply(data);
So use the thisObj parameter to pass the data.
Related
I am attempting to do this: Insert a http validation token into serialized JSON data. I already have working the ability to pass it normally in JSON and also to pass it in a header using a custom authorization extension. What I would like to do is, in the validator, if the request isn't in the header data or in the form data, deserialize the passed data and look in there. I can't actually find the data in the request though. When I read the passed data (84 bytes), i just ended up with a bunch of numbers.
Here's the validator for antiforgery:
namespace DispatchCrude.Extensions
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,
AllowMultiple = false, Inherited = true)]
public sealed class ValidateHeaderAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
var httpContext = filterContext.HttpContext;
var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName];
//try header first
var token = httpContext.Request.Headers["__RequestVerificationToken"];
//try normal method
if (token == null)
{
for(int cl = 0; cl < httpContext.Request.Form.Keys.Count; cl++)
{
if (httpContext.Request.Form.Keys[cl] == "__RequestVerificationToken")
{
token = httpContext.Request.Form[httpContext.Request.Form.Keys[cl]];
}
}
}
if (token == null)
{
//this section isn't finding the passed data
//get here if form stringified
System.IO.Stream str; String strmContents;
Int32 counter, strLen, strRead;
// Create a Stream object.
str = httpContext.Request.InputStream;
// Find number of bytes in stream.
strLen = Convert.ToInt32(str.Length);
// Create a byte array.
byte[] strArr = new byte[strLen];
// Read stream into byte array.
strRead = str.Read(strArr, 0, strLen);
// Convert byte array to a text string.
strmContents = "";
for (counter = 0; counter < strLen; counter++)
{
strmContents = strmContents + strArr[counter].ToString();
}
token = strmContents; //just so i can debug the returned token
}
//if both fail, attempt to check stringified data for matching token.
AntiForgery.Validate(cookie != null ? cookie.Value : null, token);
}
}
}
and here's some example code we are serializing the form data:
$.ajax({
type: "POST",
headers: appendVerificationToken(),
url: "/OrderSettlement/SessionApplyRatesCancel",
data: JSON.stringify({ key: data.key }),
contentType: "application/json",
success: function () {
/* decided to NOT close the window (just cancel and show the [CANCELLED] description)
// on success, close the owning popup
runFuncByNameSingleParameter(_owningPopup.Close);
*/
}
});
As I said, passing it in the header works, but I would like to be able to add it to the data instead and I can't find it in the request to parse it.
thanks to Ouroborus, I was able to come up with a solution.
This works:
string json;
var position = httpContext.Request.InputStream.Position;
using (var reader = new StreamReader(httpContext.Request.InputStream, Encoding.UTF8, false, 8192, true))
{
json = reader.ReadToEnd();
//you MUST reset the input stream to start or you will break post.
httpContext.Request.InputStream.Seek(position, SeekOrigin.Begin);
try
{
var jsonObj = Json.Decode(json); //attempt to parse into json object.
token = jsonObj["__RequestVerificationToken"];
}
catch
{
//eat the parse errors. That simply means it's not json.
}
}
I hope it helps someone else. Note that you must reset the stream back to zero, exactly how I'm doing it, or the controller function won't receive any passed data.
im writing a websocket client and i would like to receive messages as json strings. For this I need a login. And if the login isn't true i send a json string with nosuccess.
JSON String:
{"action":"login","args":["nosuccess"]}
On the client I'm using this to get the string:
WebSocket socket = new WebSocket("ws://localhost:2555/api");
socket.onmessage = function(evt) {
console.log(evt.data);
console.log(typeof(evt.data));
onMessage(evt);
}
function onMessage(evt) {
var data = JSON.parse(evt.data);
var action = data.action;
var args = data.args;
console.log(data);
console.log(typeof(data));
console.log(action);
console.log(args);
But the type of data is a string...
But why?
evt.data returns:
"{\"action\":\"login\",\"args\":[\"nosuccess\"]}"
data returns:
{"action":"login","args":["nosuccess"]}
The WebSocket server is a jetty Server which sends a string and a string array in json parsed in json with gson.toJson(class) Gson by Google. The Class is a class containing String action and String array args.
Complete source code of websocket.js:
var socket;
function openWebsocket(adress) {
socket = new WebSocket(adress);
socket.onopen = function(evt) {
console.log("Socket opened [" + adress + "]");
};
socket.onclose = function(evt) {
loadPage("login.html");
console.log("Socket closed [" + evt.code + "]");
}
socket.onmessage = function(evt) {
onMessage(evt);
}
socket.onerror = function(evt) {
console.log("Socket couldn't connect [" + evt.message + "]");
showMessage("fa-exclamation-circle", "Socket couldn't be established!", 1000);
}
}
function onMessage(evt) {
var data = JSON.parse(evt.data);
var action = data.action;
var args = data.args;
console.log(data);
console.log(typeof(data));
console.log(action);
console.log(args);
$(".card-container h3").html(data);
if(action == "login") {
if(args[0] == "success") {
loadPage("dashboard.htm");
currentpage = "dashboard.htm";
showMessage("fa-check", "Du wurdest erfolgreich eingeloggt", 2000);
} else if(args[0] == "nosuccess") {
loadPage("login.html");
currentpage = "login.html";
showMessage("fa-exclamation-circle", "Falscher Benutzername oder falsches Passwort", 2000);
} else if(args[0] == "unauthenticated") {
loadPage("login.html");
currentpage = "login.html";
showMessage("fa-exclamation-circle", "Login failure: not authenticated", 2000);
}
}
}
function sendMessage(json) {
$(".card-container h3").html(JSON.stringify(json));
console.log(JSON.stringify(json));
socket.send(JSON.stringify(json));
}
If I change this line:
var data = JSON.parse(evt.data);
to this:
var data = JSON.parse("{\"action\":\"login\",\"args\":[\"nosuccess\"]}");
Then it is a json object, but when I use evt.data then it is a string.
If I change the line to this:
var data = JSON.parse(JSON.parse(evt.data));
Then it works, but why, normally it should do it with only one JSON.parse, should it?
This seems to be fairly consistent with over-stringified strings. For example I loaded a text file using FileReader.readAsText that came with \n and \r which rendered in the console, so I did - (JSON.stringify(reader.result)).replace(/(?:\\[rn])+/g, '') first to see the symbols, then to get rid of them. Taking that and running JSON.parse() on it converts it to a non-escaped string, so running JSON.parse() again creates an object.
If you do not stringify your string, it will convert to an object and often it is not necessary but if you have no control over the obtained value, then running JSON.parse() twice will do the trick.
#DerpSuperleggera is correct. The issue was indeed an over-stringified string
let arr = "[\"~#iM\",[\"is_logged_out_token\",false,\"token_type\",\"Bearer\"]]"
So to parse it as an object, I just ran it through JSON.parse twice and that did the trick!
let obj = JSON.parse(JSON.parse(arr))
As others have stated in the comments it seems that this issue has been solved. If you are receiving a response from the server as a "stringified object" then you can turn it into a normal object with JSON.parse() like so:
var stringResponse = '{"action":"login","args":["nosuccess"]}';
var objResponse = JSON.parse(stringResponse);
console.log(objResponse.args);
You can also try out the above code here.
As for why the server is returning a string when you really wanted an object, that really comes down to your backend code, what library you are using, and the transport protocol. If you just want your front-end code to work, use JSON.parse. If you want to edit how the backend responds, please provide more information.
The checked response is correct in that it seems to be an issue of over-stringified strings. I came across it from using AWS Amplify AWSJSON type in my project. The solution that worked for me was to iterate multiple (twice) to get an object.
Wrote a simple JS function that when used to parse will return an object. There isn't really error checking, but a starting point.
export function jsonParser(blob) {
let parsed = JSON.parse(blob);
if (typeof parsed === 'string') parsed = jsonParser(parsed);
return parsed;
}
I am using SecuGen device and its library.
I wrote following code
function fnCapture() {
document.frmmain.objFP.Capture();
var result = document.frmmain.objFP.ErrorCode;
if (result == 0) {
//var strimg1 = objFP.ImageTextData;
var strmin = document.frmmain.objFP.MinTextData;
//document.frmmain.min.value = strmin;
document.frmdata.Thumb.value = strmin;
}
else
alert('Failed during captured - ' + result);
return;
}
And then I am passing document.frmdata.Thumb to server side to a webservice. But webservice provider are saying that "you are sending an invalid base64"
There is a property like
document.frmdata.Thumb.ContentType
But I am not sure how to send this in base64.
Any help will be apprecited
If the data is a string you could use the btoa() function on the window object:
console.log(document.frmdata.Thumb.value);
> "088BA76AFE122" Some raw string value from scanner
window.btoa(thumb);
> "MDg4QkE3NkFGRTEyMg==" Base-64 encoded string
while saving form details using backbone i m getting error as
POST http://localhost:8080/gamingengine/restful-services/badges 500 (Internal Server Error)
st.ajaxTransport.sendjquery.js:4
st.extend.ajaxjquery.js:4
Backbone.ajaxbackbone.js:1197
Backbone.syncbackbone.js:1178
_.extend.syncbackbone.js:284
_.extend.savebackbone.js:490
Backbone.Form.extend.saveBadgesbadges.js:219
st.event.dispatchjquery.js:3
st.event.add.y.handle
Uncaught SyntaxError: Unexpected token <
st.extend.parseJSONjquery.js:2
window.clearErrorscommon.js:386
st.event.dispatchjquery.js:3
st.event.add.y.handlejquery.js:3
st.event.triggerjquery.js:3
rjquery.js:4
st.ajaxTransport.send.r
my backbone code is as follows
this.model.save(this.getValue(), {
//beforeSend : setHeader, //added
iframe : true,
wait : true,
files : $file,
elem : this,
data : _.omit(this.getValue(), ['iconFile']),
silent : true,
success : function(model, response, options) {
alert("inside save..");
var error = false;
_.each(response, function(val, key) {
if (app.BadgesView.fields[key]
&& val instanceof Object
&& val.error) {
error = true;
app.BadgesView.fields[key]
.setError(val.message);
}
});
if (!error) {
app.BadgesView.model.set(model);
app.BadgesListCollection.add(model);
return;
}
return false;
},
error : function(model, response, options) {
console.log("error while save in badges.js : ");
}
});
and server side code is as follows which is using resteasy
#POST
#Consumes("multipart/form-data")
#Produces("text/html")
#Cache(noStore = true)
public final Response saveBadges(
#MultipartForm final BadgesForm badgesForm) throws IOException {
System.out.println("saveBadges called........");
final int no_of_coins = badgesForm.getNo_of_coins();
final String badge_name = badgesForm.getBadge_name();
final int score = badgesForm.getScore();
final int badge_id = badgesForm.getBadge_id();
final byte[] iconFile = badgesForm.getIconFile();
final Validator validatorNumeric = ValidationFactory
.getTextFieldNumericValidator();
validatorNumeric.validate("no_of_coins", no_of_coins,
threadlocalExceptions.get());
System.out.println("iconFile :" + iconFile);
if (iconFile.length >= GamingConstants.ONE) {
ValidationFactory.getImageContentValidator().validate("iconFile",
iconFile, threadlocalExceptions.get());
ValidationFactory.getImageSizeValidator().validate("iconFile",
iconFile, // added size // validator
threadlocalExceptions.get());
}
if (threadlocalExceptions.get().isEmpty()) {
try {
final Badges badges = new Badges();
badges.setNo_of_coins(no_of_coins);
badges.setBadge_name(badge_name);
badges.setScore(score);
badges.setBadge_id(badge_id);
final Coin coin = new Coin();
coin.setId(badgesForm.getCoin());
badges.setCoin(coin);
Badges.save(badges);
final Badges badgesObj = new Badges();
badgesObj.setBadge_id(badges.getBadge_id());
badgesObj.setCoin(coin);
badgesObj.setBadge_name(badges.getBadge_name());
badgesObj.setNo_of_coins(badges.getNo_of_coins());
badgesObj.setScore(badges.getScore());
if (iconFile.length >= GamingConstants.ONE) {
final String imgPath = "restful-services/badges/"
+ badges.getBadge_id() + "/image";
badgesObj.setIconPath(imgPath);
final String fileName = path + badges.getBadge_id()
+ ".png";
CommonUtils.writeIcon(iconFile, fileName);
} else {
badgesObj.setIconPath(defaultPath);
}
Badges.update(badgesForm.getBadge_id(), badgesObj);
final gamingengine.bind.Badges bindBadges = new gamingengine.bind.Badges();
bindBadges.setBadge_id(badgesObj.getBadge_id());
bindBadges.setCoin(badgesObj.getCoin());
bindBadges.setNo_of_coins(badgesObj.getNo_of_coins());
bindBadges.setBadge_name(badgesObj.getBadge_name());
bindBadges.setIconPath(badgesObj.getIconPath());
bindBadges.setScore(badgesObj.getScore());
final ObjectMapper mapper = new ObjectMapper();
final String jsonString = mapper.writeValueAsString(bindBadges);
return Response.ok().entity(jsonString).build();
} catch (DBException e) {
if (e.getMessage().startsWith(DBException.PARENT_NOT_EXISTS)) {
final String fieldName = e.getMessage()
.substring(e.getMessage().indexOf("-") + 1).trim()
.toLowerCase();
e.getValidationException()
.setMessage(
"The "
+ fieldName
+ " is already deleted.Please refresh the page ");
threadlocalExceptions.get().put(fieldName,
e.getValidationException());
}
}
}
final Map<String, ValidationException> exceptions = threadlocalExceptions.get();
threadlocalExceptions.remove();
final ObjectMapper mapper = new ObjectMapper();
final String exceptionJsonString = mapper
.writeValueAsString(exceptions);
return Response.ok().entity(exceptionJsonString).build();
}
while saving data of the form, backbone does not call the saveBadges() method of server side code
in chrome network it shows as
badges
/gamingengine/restful-services
POST
500
Internal Server Error
text/html
now i tried as
data:this.getvalue() in save() its sending all values to server except for iconPath
**iconPath : {
type : "FilePicker",
title : "Icon"
}**
and in save() of backbone
**var $file = $('input[name="iconPath"]', this.el);** this two lines are not sending iconPath, its empty any guesses
any help appreciated!!! thanks
The issue could be related to the content-type expected by your service, "multipart/form-data". Backbone by default does not provide an implementation to send a multipart request on the "save" method.
Here is a link with information about how you can send the multipart-request:
multipart form save as attributes in backbonejs
Also, message that you are receiving about the unexpected character ">" could be related to the "dataType" associated to the request, try to change it to "text" to avoid parsing to JSON, adding that you should be getting the correct error.
this.model.save(this.getValue(), {
//beforeSend : setHeader, //added
iframe : true,
wait : true,
files : $file,
dataType: "text",
elem : this,
data : _.omit(this.getValue(), ['iconFile']),
silent : true..
}
I will suggest to review your developer console as well in Chrome, Safari or Firefox to see how the request is been sent to the server, that could give you a better understanding how your request is been received by the server.
Also, try testing your service by external "Restful" tool, chrome provided the "Advance Restful Client" where you can test your service.
Hope this information helps to solve your issue or guide you in the right direction.
I need a cross domain web api method to return valid jsonp to some javascript from C#. I can't seem to make this magic happen. I've looked around the web and can't find a start to end example that fits my needs and works... Fiddler shows that I'm returning valid json data but when I hit a breakpoint in F12 dev tools or firebug the result is a failure message.
Here is what I've currently got:
C#
/// <summary>
/// POST: /Instance/RefreshItem
/// </summary>
/// <param name="instanceId"></param>
/// <returns>Json</returns>
[HttpPost]
public System.Web.Mvc.JsonResult RefreshItem(int instanceId, Guid customerId)
{
try
{
var clientConnection = Manager.ValidateInstance(customerId, instanceId);
clientConnection.RefreshItem();
var result = new MethodResult()
{
Success = true,
Value = instanceId,
Message = "Item successfully refreshed."
};
return new System.Web.Mvc.JsonResult() { Data = result };
}
catch (Exception ex)
{
Manager.LogException(_logger, ex, customerId, instanceId);
var result = new MethodResult()
{
Success = false,
Value = instanceId,
Message = ex.GetBaseException().Message
};
return new System.Web.Mvc.JsonResult() { Data = result };
}
}
JS
Example.RefreshItem = function ()
{
Example.SDK.JQuery.getSettings(
function (settings, userId, userLocaleId)
{
alert("Attempting to refresh item for instance " + settings.ConnectionId + "\r\nThis may take awhile.");
var url = settings.SystemUrl + "/Api/WebApiServices/ExampleAdmin/RefreshItem?customerId=" + settings.CustomerId + "&instanceId=" + settings.ConnectionId;
$.ajax({
url: url,
dataType: "jsonp",
jsonpCallback: 'RefreshItemCallback',
success: RefreshItemCallback
})
},
Example.SDK.JQuery.defaultErrorCallback
);
}
function RefreshItemCallback(data)
{
alert(data.d.Message);
}
I've also tried $.Post().Always() with the same results.
What am I doing wrong???
I think your problem is that you're instantiating a JsonResult instead of using the Json method.
Presumably the C# method you have is in a controller, so instead of
return new System.Web.Mvc.JsonResult() { Data = result };
do:
return Json(result);
This method probably sets some of the other properties of the JsonResult that, when not set, will not be properly received by the client.
See how Microsoft only shows you how to create a JsonResult via the Json method on MSDN
Note that the same is probably true with methods like View, Content, and File.
Fight all week unable to find an answer until you ask the question somewhere... Within 30 minutes of asking I found this: http://bob.ippoli.to/archives/2005/12/05/remote-json-jsonp/ which was exactly what I needed.
Thanks to all who posted.