UnityWebRequest.Put doesn't send JSON string - javascript

I'm looking to make a PUT or POST request to an API I've deployed to Vercel built with NEXT JS. It does other things besides being an API but that's not important...
So, I have a Unity Project where I would like to log the time that my player completed the level they're on.
Originally I wanted to use Firebase but discovered that I can't as the Windows Unity builds don't support it so I ended up just making an API that send the Data to MongoDB and does some other stuff, again not important.
So getting to the guts of the problem:
I have this IEnumerator to send the request:
IEnumerator logPlayerTime()
{
string url = "http://some-vercel-cloud-function/api/new";
var requestBody = new PlayerRequestBody();
requestBody.firstName = "John";
requestBody.lastName = "Doe";
requestBody.email = "email#email.com";
requestBody.marketing = false;
requestBody.time = 200000; // this actually come from somewhere else but for now this is fine
string json = JsonUtility.ToJson(requestBody);
UnityWebRequest request = UnityWebRequest.Put(url, json);
request.SetRequestHeader("Content-Type", "application/json");
request.SetRequestHeader("Accept", "*/*");
request.SetRequestHeader("Accept-Encoding", "gzip, deflate, br");
request.SetRequestHeader("Connection", "keep-alive");
request.useHttpContinue = false;
yield return request.SendWebRequest();
if (request.result != UnityWebRequest.Result.Success)
{
Debug.Log(request.error);
}
else
{
Debug.Log("Time Logged! " + json);
}
}
According to the Docs I should have two arguments, the first being my URL and the second Being my Data.
As for the JSON I want to send I made this struct
[Serializable]
private struct PlayerRequestBody
{
public string firstName;
public string lastName;
public string email;
public bool marketing;
public float time;
}
Then, I'm using JsonUtiltiy.ToJson to turn my json variable into a JSON string, which seems to work.
All of this said, no matter what I change in the request, even typing the JSON string out manually escaped. It just sends a request body of an empty JSON object like this:
{}
So if we take a look at what the API receives the logs look like this:
[PUT] /api/new 01:44:25:13 {}
I'm sure it's something small but I just don't have enough experience with Unity or C# to find the issue (and I want to go to bed).
As for how this gets called, In one of my Scripts that manages the game I have a handler method that is subscribed to GameState changes, so when my GameState === GameState.Victory then I would run the handler. So the code for that, with bits removed for readability looks like this:
// ...
private void Awake()
{
GameManager.OnGameStateChanged += GameManager_OnGameStateChanged;
}
// ...
private void GameManager_OnGameStateChanged(GameState state)
{
if (state == GameState.Victory)
{
handleVictory();
}
}
That handleVictory method just runs StartCoroutine on the IEnumerator at the moment
Any ideas? Thanks in advance

So for anyone who ever somehow runs into this, the answer is... Next is weird.
I made another API using Express JS and boom instant data from the Player.
I don't have time to look into it but there must be something about Unitys UnityWebRequest that Next doesn't agree with. Long and short I'm gonna pull the API out of next and then move over my components to React and host it on a server less function :)

Related

API request shows correct response in Chrome DevTools but is actually null in Angular app

My app is supposed to display Code Analysis logs. Those logs are in large(20k+ lines) html files. I'm reading them in ASP.NET Core app and pass them as string to API. C# read them nicely and passes them on, Chrome shows the correct response in DevTools, and yet Angular doesn't seem to notice this and logs null to the console aswell as when I try to display them.
I'm relatively new to Angular so I might be ommiting something obvious here. Here's my code - that's the Get method that reads and passes the file:
[HttpGet, Route("analysis")]
public string Analysis(string projectName, string definitionName)
{
string path = Path.Combine(#"\\glbuild2\CodeAnalysisLogs", projectName, definitionName, "CodeAnalysis.html");
if (System.IO.File.Exists(path))
{
string temp = System.IO.File.ReadAllText(path);
return temp;
}
else return "Error";
}
And here's the method I use to make an API request(please note it does work, I use the same method with different uri but to the same server and it works there):
this.http.get('http://localhost:54639/api/logs/analysis?projectName=' + this.currentDefinition.project.name + '&definitionName=' + this.currentDefinition.name).subscribe(data => {
this.AnalysisLogs = data;
console.log(data);
})
Now the line logging to console always logs null, and the response in DevTools is the one I'm expecting: a string going

On a get request, how can I start program, and then using another get request stop it?

I'm running a website using java and tomcat. At a high level what I'm trying to do is route a start button to a java method which makes an endless amount of calls to some other resource. When I want to stop that program I would push the stop button.
I have figured out the start part, but cannot stop it because I don't think the state of the get request page gets saved. I have a few global variables in that class which I think keep getting reset everytime I make a get request. How could I prevent that from happening?
#Path("/myresource")
public class MyResource {
continuousMethod sample = new continuousMethod()
boolean collecting = false;
/**
* Method handling HTTP GET requests. The returned object will be sent
* to the client as "text/plain" media type.
*
* #return String that will be returned as a text/plain response.
*/
#GET
#Produces(MediaType.TEXT_PLAIN)
#Path("/collect")
public String startCollection() throws URISyntaxException {
System.out.println("Made it here");
sample.start();
collecting = true;
return "Collecting";
}
#GET
#Produces(MediaType.TEXT_PLAIN)
#Path("/stopcollect")
public String stopCollection() {
sample.close();
collecting = false;
return "Finished collecting!";
//return "Not collecting";
}
}
Make use of sessions instead of global variables.

Is it possible to send binary data with STOMP over WebSockets using Spring-WebSockets?

I am able to send and receive JSON with STOMP over WebSockets following spring documentation. However performance is poor at large high rates, so I wish to profile the use of binary messages.
Spring-WebSockets 4.0
JavaScript client running in Chrome 35
stomp.js 1.7.1
Sending
I send messages using SimpMessageTemplate with the necessary broker relay - see spring documentation
#Controller
public class DemoBinaryController {
#Autowired
private SimpMessagingtemplate template
#Scheduled(fixedDelay = 5000)
public void demo() throws Exception {
GenericMessage<byte[]> message = new GenericMessage<byte[]>(new byte[]{65,66,67});
template.send("/app/binarydemo", message);
}
}
Receiving
A JavaScript client receives data using stomp.js using the standard mechanism.
var subscription = client.subscribe("/app/binarydemo", new function(message) {
console.log("RX message", typeof message.body, message.body.length);
});
Messages are received, but as Strings, with console output as follows. I'm expecting some raw type, like ArrayBuffer
RX message string 3
RX message string 3
Things I've tried
I realise the T in STOMP stands for Text, however the Spring documentation implies binary messages are possible at least with plain WebSockets, also the stomp specification states
STOMP is text based but also allows for the transmission of binary
messages.
Debugging the sending code, and it appears to remain as byte [] for as far as I can see
Debugging the stomp.js library whilst receiving. The message appears to be a String when received in the underlying ws.onmessage callback (line 243 in stomp-1.7.1.js)
Lots of searching - this seems a rare topic with little information
Looking at the stomp.js source code. The only reference to binary is ws.binaryType = "arraybuffer".
Update: I've done more debugging on the server side. It seems that org.springframework.web.socket.TextMessage is always used within org.springframework.web.socket.messaging.StompSubProtocolHandler rather than org.springframework.web.socket.BinaryMessage. I've raised a feature request for this SPR-12301
message = MessageBuilder.withPayload(message.getPayload()).setHeaders(headers).build();
byte[] bytes = this.stompEncoder.encode((Message<byte[]>) message);
synchronized(session) {
session.sendMessage(new TextMessage(new String(bytes, UTF8_CHARSET)));
}
My question
Is this approach possible with this mix of technologies?
Am I missing some crucial step?
Can anyone point me to a working example
It seems that org.springframework.web.socket.TextMessage is always used within org.springframework.web.socket.messaging.StompSubProtocolHandler rather than org.springframework.web.socket.BinaryMessage. I've raised a feature request for this SPR-12301 which has been accepted.
message = MessageBuilder.withPayload(message.getPayload()).setHeaders(headers).build();
byte[] bytes = this.stompEncoder.encode((Message<byte[]>) message);
synchronized(session) {
session.sendMessage(new TextMessage(new String(bytes, UTF8_CHARSET)));
}
Update
SPR-12301 was delivered in 4.1.2 but only adds support for receiving binary messages
Raised SPR-12475 for sending of binary messages
Try to configure you Server just with ByteArrayMessageConverter:
#Configuration
#EnableWebSocketMessageBroker
public class MyWebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
messageConverters.add(new ByteArrayMessageConverter());
return false;
}
}
UPDATE
Ah! I see that. Thanks:
public TextMessage(byte[] payload) {
super(new String(payload, UTF_8));
this.bytes = payload;
}
From other side from STOMP spec:
The body of a STOMP message must be a String. If you want to send and receive JSON objects, ...
According to the ArrayBuffer:
Getting an array buffer from existing data
From a Base64 string
From a local file
So, I think you don't have a chioce rather than provide you some custom MessageConverter, which converts your byte[] to Base64 String and send it.
On the JavaScript side you should decode that string to the ArrayBuffer somehow.

Having trouble grasping how to securely sign JWT with Private Key

I'm looking at this example here which refers to the javascript functionality of JWT
I am trying to use javasrcipt to sign a piece of data. However, it says I have to use a Private RSA Key and it doesn't allow you to use a public key.
My goal was once a form is submitted via PHP, call this javascript function and encrypt the data.
Pardon my ignorance, but how can you use a private RSA key in javascript and keep it private at the same time?
It appears that you have to give it a private key somehow and wouldn't that private key be visible to a user using simple developer tools in the web browser?
function _genJWS() {
var sHead = '{"alg":"RS256"}';
var sPayload = '{"data":"HI","exp":1300819380}';
var sPemPrvKey = document.form1.pemprvkey1.value;
var jws = new KJUR.jws.JWS();
var sResult = null;
try {
sResult = jws.generateJWSByP1PrvKey(sHead, sPayload, sPemPrvKey);
document.form1.jwsgenerated1.value = sResult;
} catch (ex) {
alert("Error: " + ex);
}
}
What your are looking for is not JWS (signed), but JWE (encrypted).
If you want to send secured data to a server using JWE, you must :
get the public key of the server
encrypt your data using this public key and produce a JWE
send your JWE to the server.
As far as I know, there is no javascript library able to produce JWE (I may be wrong, but I found nothing).

Youtube Data API v. 3 - Display channel author name of private / deleted video

This is one of them Youtube internal decisions that is highly counter-productive. Correct me if I'm wrong, but they currently don't want to expose the channel name of private / deleted videos. They even call it a 'bug' if it happens ( http://code.google.com/p/gdata-issues/issues/detail?id=5893 ). So what happens if I solely request video IDs that were public beforehand but then later became private or deleted? I will get, via usage of Youube Data API v3, a "response is undefined" error message in console. WHat happens when that message happens? My code breaks!
This is the code I currently use:
function DisplayThemVideos(yeah) {
var yeah = $("#ThoseMissingIDs").text();
var vidrequestOptions = {
id: yeah,
part: 'snippet',
fields: 'items(id),items(snippet(channelId)),items(snippet(channelTitle)),
items(snippet(title)),items(snippet(thumbnails(default)))'
};
var vidrequest = gapi.client.youtube.videos.list(vidrequestOptions);
vidrequest.execute(function(response) {
var videoIdItems = response.result.items;
if (videoIdItems) { // If results
displayResults(videoIdItems);
} else { // if NO results
alert('Sawwy, YouLose, thx to Youtube!'); // This alert never fires!
}
});
}
Now the undefined "response" is actually an empty request sent back to my app from Youtube server. Youtube answers back with an empty "item" tag which obviously doesn't help much for the displayResults(videoIdItems) function which gets fired without any item to display! They should at least let the channel name and channel ID filter through so a User could click on a link in order to access the remaining public videos of that channel (wouldn't that be productive Jeff P?).
SO my dilemma is to get the else section working like so:
displayResults(videoIdItems);
} else { // if NO results or if results return EMPTY or response "undefined"
alert('Sorry pal but these IDs ___________ are currently missing.
Click the Channel link to access the public videos of that channel.');
}
The else part works as it should for similar API calls as demonstrated in Youtube Data API v.3 sample code, but I guess it currently isn't able to handle empty requests.
So what am I to do? WIll I have to use an ajax call with success fail error handling? Like I said beforehand, the API returns an empty request so the response is legit but the response content comes empty for private/deleted videos, hence, "undefined" with code breaking along the wway.
Any hints leading to a working solution would help! Thx for guidance.
I can only partly answer my question. Easiest is to use Catch of Try - Catch method which detects everything except syntax errors, helpful in detecting an empty response (undefined) and activating your function that can access the Youtube player for displaying current availability status of the missing ID:
...
var vidrequest = gapi.client.youtube.videos.list(vidrequestOptions);
vidrequest.execute(function(response) {
try {
var videoIdItems = response.result.items;
if (videoIdItems) { // If results
displayResults(videoIdItems);
return false;
}
} catch (e) {
// put code here that handles errors such as empty or undefined response
// which otherwise breaks your code
} finally {
// optionally, put code here that will always trigger
}
});
Hope this helps others.

Categories