I am learning how to post data to a web server from Unity3d as a first step towards creating a leaderboard for my game. Right now I have a very simple script in Unity that posts a name and score to my server. The PostScoreURL maps to a create action in Rails (when sent as a POST). It is very simple but works on a basic level.
Unity has a WWW class that I am using to POST to the server. This class has an error property but it is always coming up as 500 Internal Server Error; it has this error if the user is successfully created or if the user is not created due to a validation problem. I would like to add to my Rails app so that I can send more meaningful error messages back to Unity so that the user will know what is going on. For example, if they try to create a user that already exists, they should be notified that they need to use a different name.
Here is my code:
The Unity script:
string PostScoreURL = "http://localhost:3000/users";
string name = "Melanie";
int score = 8732;
WWW www;
bool requestSent = false;
void OnGUI()
{
if (GUI.Button(new Rect(0, 0, 100, 100),"Post Score"))
{
WWWForm form = new WWWForm();
form.AddField("user[name]", name);
form.AddField("user[score]", score);
www = new WWW(PostScoreURL,form);
requestSent = true;
}
if(requestSent && www.isDone)
{
print (www.error);
requestSent = false;
}
}
From the Rails app:
user.rb:
class User < ActiveRecord::Base
attr_accessible :name, :score
validates_presence_of :name, :score
validates_uniqueness_of :name
end
Here is my create action:
def create
#user = User.new(params[:user])
#user.save
end
I would like to send back relevant error messages to Unity. For example: Missing Username, Missing Score, Username taken.
You shouldn't consider your business rule errors as connection errors, that is, those errors should be treated in the server side, returning to the client a "successful" response with an internal error code, defined by you.
I don't know RoR, it probably let you define error codes in the response, but in my case (that don't use RoR) we created a response class with error codes that is serialized and returned to the client. After that, WWW class calls a parser that will verify the error codes. Also, WWW watches for connection errors on WWW.error.
Related
I wonder if anyone can help, and if the issue is specific to Last.fm (perhaps not the greatest of APIs).
I've built an album search feature into my app that takes two parameters - album and artist. Now, this returns an album just fine if there's a result, but in the instances that there isn't a result - if just type gibberish into the fields - Rails breaks with a 404 error when trying to run URI.open(url).read.
What I don't quite understand (and I am fairly new at this), is that when I run the same API call url in my search engine, with the gibberish, I do get a JSON response:
// https://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=xxxxxxx&artist=akjsdhkasd&album=hdkuahd&format=json
{
"message": "Album not found",
"error": 6
}
So, I don't understand why I'm getting a 404 error when it runs in my code.
Is there any way that I can rescue this, so that I can just render a 'no result', rather than crashing the entire site?
Not sure my code adds much to the picture, but this is where I run the URI:
def get_album(artist, album)
album = ERB::Util.url_encode(album)
artist = ERB::Util.url_encode(artist)
url = "https://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=xxxx&artist=#{artist}&album=#{album}&format=json"
serialized = URI.open(url).read
JSON.parse(serialized, object_class: OpenStruct).album
end
Thanks for any pointers.
From what I understood, you are using open-uri to reach this service. If you want to rescue an exception in this process you can try something like this:
def get_album(artist, album)
album = ERB::Util.url_encode(album)
artist = ERB::Util.url_encode(artist)
url = "https://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=xxxx&artist=#{artist}&album=#{album}&format=json"
serialized = URI.open(url).read
JSON.parse(serialized, object_class: OpenStruct).album
rescue OpenURI::HTTPError
"Error when trying to fetch album information"
end
*I'm returning just a string but you can implement an appropriate return that fits your purpose.
I'm not sure if it's possible to rescue specific 404 - Not Found errors using this strategy. But, you can take a look into 'net/http' or other HTTP Client Libraries (httparty, typhoeus, etc..) to try different approaches if you want..
I am testing Account Kit (Basic Web version - phone number verification) on a Django (Python) based web app. One thing I try is logging with multiple accounts on localhost, and trying to link a different number to each one successively. If a number has already successfully attached to a previous account, I show an "already taken" error prompt. Standard stuff.
I've been noticing that I sporadically get the "already taken" error prompt on unused numbers as well. Investigating deeper, I found that although I had input and verified (via SMS) a new number, the account kit ID and mobile number returned to me was the previous pair.
I can't tell why this is happening. Can someone help me in debugging this? In case it matters, my authorization flow uses the app secret.
Following are some relevant snippets. First, the Account Kit Manager class I've written:
from myproj.account_kit_settings import FAID, AKAS
class AccountKitManager(object):
obj = None
def __init__(self, app_id, app_secret):
self.app_secret = app_secret
self.app_access_token = 'AA|{0}|{1}'.format(app_id, app_secret)
def get_user_cred(self, auth_code):
if not self.obj:
self.set_user_cred(auth_code)
return self.obj
def set_user_cred(self, auth_code, url=None):
if not url:
url = 'https://graph.accountkit.com/v1.2/access_token?grant_type=authorization_code&code={0}&access_token={1}&appsecret_proof={2}'.\
format(auth_code,self.app_access_token,self.get_appsecret_proof(self.app_access_token))
data = self.retrieve_data(url)
data = self.evaluate_data(data)
string_obj = self.retrieve_user_cred(data["access_token"])
self.obj = self.evaluate_data(string_obj)
def retrieve_user_cred(self, user_access_token, url=None):
if not url:
url = 'https://graph.accountkit.com/v1.2/me/?access_token={0}&appsecret_proof={1}'.\
format(user_access_token,self.get_appsecret_proof(user_access_token))
return self.retrieve_data(url)
def retrieve_data(self, url):
return requests.get(url).text
def evaluate_data(self, data):
return ast.literal_eval(data)
def get_appsecret_proof(self, access_token):
h = hmac.new(self.app_secret.encode('utf-8'),msg=access_token.encode('utf-8'),digestmod=hashlib.sha256)
return h.hexdigest()
Next, here's how I use it:
mobile_data = AccountKitManager(FAID, AKAS)
def account_kit_handshake(csrf, state, status, auth_code):
if csrf == state and status=='PARTIALLY_AUTHENTICATED':
user_data = mobile_data.get_user_cred(auth_code)
if FAID == user_data["application"]["id"]:
return user_data["id"], user_data["phone"]
else:
# app id mismatch
return None, None
else:
# csrf mismatch, or could not authenticate
return None, None
def get_requirements(request):
status = request.GET.get('status', None)
auth_code = request.GET.get('code', None)
state = request.GET.get('state', None)
return account_kit_handshake(request.session["csrf"], state, status, auth_code)
def verify_consumer_number(request,*args,**kwargs):
AK_ID, MN_data = get_requirements(request)
request.session.pop("csrf",None)
if AK_ID and MN_data:
if someone_elses_number(MN_data['national_number'], request.user.id):
return render(request,"used_number.html",{})
else:
save_consumer_credentials.delay(AK_ID, MN_data, request.user.id)
return redirect("classified_listing")
else:
return render(request,"unverified_number.html",{})
UPDATE: Seems the user access token isn't always being returned. This could be a problem with variable scope.
The problem emanated from the scope of the AccountKitManager class instance. It was being set globally (i.e. see mobile_data variable in my code). Making this variable local solved the problem.
Maybe it is a good example for server push system. There are many users in the system, and users can talk with each other. It can be accomplished like this: one user sends message(through websocket) to the server, then the server forward the message to the other user. The key is to find the binding between the ws(websocket object) and the user. The example code like below:
EM.run {
EM::WebSocket.run(:host => "0.0.0.0", :port => 8080, :debug => false) do |ws|
ws.onopen { |handshake|
# extract the user id from handshake and store the binding between user and ws
}
ws.onmessage { |msg|
# extract the text and receiver id from msg
# extract the ws_receiver from the binding
ws_receiver.send(text)
}
end
}
I want to figure out following issues:
The ws object can be serialized so it can be stored into disk or database? Otherwise I can only store the binding into memory.
What the differences between em-websocket and websocket-rails?
Which gem do you recommend for websocket?
You're approaching a use case that websockets are pretty good for, so you're on the right track.
You could serialize the ws object with Marshal, but think of websocket objects as being a bit like http request objects in that they are abstractions for a type of communication. You are probably best off marshaling/storing the data.
em-websocket is a lower(ish) lever websocket library built more or less directly on web-machine. websocket-rails is a higher level abstraction on websockets, with a lot of nice tools built in and pretty ok docs. It is built on top of faye-websocket-rails which is itself built on web machine. *Note, action cable which is the new websocket library for Rails 5 is built on faye.
I've use websocket-rails in the past and rather like it. It will take care of a lot for you. However, if you can use Rails 5 and Action Cable, do that, its the future.
The following is in addition to Chase Gilliam's succinct answer which included references to em-websocket, websocket-rails (which hadn't been maintained in a long while), faye-websocket-rails and ActionCable.
I would recommend the Plezi framework. It works both as an independent application framework as well as a Rails Websocket enhancement.
I would consider the following points as well:
do you need the message to persist between connections (i.e. if the other user if offline, should the message wait in a "message box"? for how long should the message wait?)...?
Do you wish to preserve message history?
These points would help yo decide if to use a persistent storage (i.e. a database) for the messages or not.
i.e., to use Plezi with Rails, create an init_plezi.rb in your application's config/initializers folder. use (as an example) the following code:
class ChatDemo
# use JSON events instead of raw websockets
#auto_dispatch = true
protected #protected functions are hidden from regular Http requests
def auth msg
#user = User.auth_token(msg['token'])
return close unless #user
# creates a websocket "mailbox" that will remain open for 9 hours.
register_as #user.id, lifetime: 60*60*9, max_connections: 5
end
def chat msg, received = false
unless #user # require authentication first
close
return false
end
if received
# this is only true when we sent the message
# using the `broadcast` or `notify` methods
write msg # writes to the client websocket
end
msg['from'] = #user.id
msg['time'] = Plezi.time # an existing time object
unless msg['to'] && registered?(msg['to'])
# send an error message event
return {event: :err, data: 'No recipient or recipient invalid'}.to_json
end
# everything was good, let's send the message and inform
# this will invoke the `chat` event on the other websocket
# notice the `true` is setting the `received` flag.
notify msg['to'], :chat, msg, true
# returning a String will send it to the client
# when using the auto-dispatch feature
{event: 'message_sent', msg: msg}.to_json
end
end
# remember our route for websocket connections.
route '/ws_chat', ChatDemo
# a route to the Javascript client (optional)
route '/ws/client.js', :client
Plezi sets up it's own server (Iodine, a Ruby server), so remember to remove from your application any references to puma, thin or any other custom server.
On the client side you might want to use the Javascript helper provided by Plezi (it's optional)... add:
<script src='/es/client.js' />
<script>
TOKEN = <%= #user.token %>;
c = new PleziClient(PleziClient.origin + "/ws_chat") // the client helper
c.log_events = true // debug
c.chat = function(event) {
// do what you need to print a received message to the screen
// `event` is the JSON data. i.e.: event.event == 'chat'
}
c.error = function(event) {
// do what you need to print a received message to the screen
alert(event.data);
}
c.message_sent = function(event) {
// invoked after the message was sent
}
// authenticate once connection is established
c.onopen = function(event) {
c.emit({event: 'auth', token: TOKEN});
}
// // to send a chat message:
// c.emit{event: 'chat', to: 8, data: "my chat message"}
</script>
I didn't test the actual message code because it's just a skeleton and also it requires a Rails app with a User model and a token that I didn't want to edit just to answer a question (no offense).
I am Developing grails application with multiple services and Quartz Jobs. Within Grails Quartz Jobs, I inject some services which make requests to server and perform some operation based upon the result, returned from server.
Now, Sometimes that server goes down due to some reasons and the service which communicates with that server gets connectionException. As all this is happening at back end and user doesn't know about it. I want to show message to user (No matter in which GSP page currently user is when server went down) at the top of the GSP whenever my service encounters that server is Down.
And that message will be disappeared when my service started communicating server (when server is up). As far I know, FLASH can be used here but that persists within single request but I want to show this message until server becomes accessible.
What are the different options for me to achieve this in Grails ? What will be best option ?
Thanks in Advance :)
Create a status service that keeps a volatile stats property, set it to reflect the status when it changes and use a tag library to read the status and include it in your layout/GSPs.
Here is a very quick example of that
First the Service:
// MyStatusService
package com.example
class MyStatusService {
boolean isServerDown = false
...
}
Then within your code:
// From within your code, setting the status
def myStatusService // assumes you can inject it
...
myStatusService.isServerDown = true // or false depending on your code
...
A tag library:
// MyStatus TagLibrary
package com.example
class MyStatusTagLib {
def myStatusService
static namespace = "myStatus"
def checkStatus = { attrs ->
if (myStatusService.isServerDown) {
out << "Server is down message here."
}
}
}
Then finally in your GSP or even your layout:
<myStatus:checkStatus />
Hope that helps.
I had used the symfony admin generator to create an web application for athletes management. One of the last client's requirement was to add a feature to notice the user and send an e-mail to the administrators when an athlete with the same number is inserted on the database. Until now, the column number of the Athlete table had a unique constraint but the client desires that the athlete can by inserted anyway.
To accomplish that, I was trying to extend the the edit / new actions in order to implement the client requirements.
Here is the code:
public function executeEdit(sfWebRequest $request)
{
$user = $this->getUser();
if(! $user->hasCredential('admin'))
{
$clube_id = $user->getAttribute('id');
$atleta_id = $request->getParameter('id');
$atleta = Doctrine::getTable('Atleta')->find($atleta_id);
if($clube_id != $atleta->clube_id)
$this->forward404();
}
if($request->get('post'))
{
// check if the inserted athlete BI already exists; if so, display a message to the user and send an email to the system admins
$atleta_id = $request->getParameter('id');
$atletaBIExiste = Doctrine::getTable('Atleta')->findDuplicateAthleteBI($atleta_id);
if($atletaBIExiste)
{
// display a notice message to the user
$this->getUser()->setFlash('error', 'Athlete already exists');
// send an email to the system administrator
}
}
return parent::executeEdit($request);
}
Here is my problem: when I execute the edit action, I only want to check for a duplicate athlete number when the HTTP is POST but it seems that never is. I had already sent some exceptions to the output to verify which type is HTTP Request and it seems it is always GET.
The problem you will be having is that when you hit save on the Edit page the information isn't posted to the edit action, it is posted to an action called update.
Have a look at the actions.class.php file in the cache and you will see it.