I am attempting to implement an online leaderboard in a game app for iOS, using Django to process POST requests from the iDevice and store the scores. I have figured out how to get Django to serialize the objects to XML, and my iPhone can read and display the scores. However, I can't for the life of me get my iPhone to POST XML to my Django server.
Below is the function I am using to post the scores...
iOS (Objective-C) Controller:
- (void) submitHighScore {
NSLog(@"Submitting high score...");
NSString *urlString = HIGH_SCORES_URL;
NSURL *url = [NSURL URLWithString: urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url];
[request setHTTPMethod: @"POST"];
[request setValue: @"text/xml" forHTTPHeaderField: @"Content-Type"];
NSMutableData *highScoreData = [NSMutableData data];
[highScoreData appendData: [[NSString stringWithFormat: @"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"] dataUsingEncoding: NSUTF8StringEncoding]];
[highScoreData appendData: [[NSString stringWithFormat: @"<player_name>%@</player_name", @"test"] dataUsingEncoding: NSUTF8StringEncoding]];
[highScoreData appendData: [[NSString stringWithFormat: @"<score>%d</score>", 0] dataUsingEncoding: NSUTF8StringEncoding]];
[highScoreData appendData: [[NSString stringWithFormat: @"</xml>开发者_如何学编程"] dataUsingEncoding: NSUTF8StringEncoding]];
[request setHTTPBody: highScoreData];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: YES];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest: request
delegate: self];
if (!connection) {
NSLog(@"Request to send high scores appears to be invalid.");
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: NO];
}
}
The above method succeeds in sending the request, and interprets it correctly as CONTENT_TYPE: text/xml
, but the Django view that processes the request can't seem to make any sense of it, interpreting it almost as if it was merely plain text. Below is my Django view...
Django (Python) view:
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.core import serializers
from django.core.exceptions import ValidationError
from django.views.decorators.csrf import csrf_exempt
from modologger.taptap.models import HighScore
@csrf_exempt
def leaderboard( request, xml = False, template_name = 'apps/taptap/leaderboard.html' ):
"""Returns leaderboard."""
if xml == True: # xml is set as True or False in the URLConf, based on the URL requested
if request.method == 'POST':
postdata = request.POST.copy()
print postdata
# here, postdata is evaluated as:
# <QueryDict: {u'<?xml version': [u'"1.0" encoding="UTF-8" ?><player_name>test</player_name<score>0</score></xml>']}>
for deserialized_object in serializers.deserialize('xml', postdata): # this fails, returning a 500 error
try:
deserialized_object.object.full_clean()
except ValidationError, e:
return HttpResponseBadRequest
deserialized_object.save()
else:
high_score_data = serializers.serialize( 'xml', HighScore.objects.all() )
return HttpResponse( high_score_data, mimetype = 'text/xml' )
else:
high_scores = HighScore.objects.all()
return render_to_response( template_name, locals(), context_instance = RequestContext( request ) )
To be honest, I'm not sure whether the problem lies in the Objective-C or in the Django code. Is the Objective-C not sending the XML in the right format? Or is the Django server not processing that XML correctly?
Any insight would be much appreciated. Thanks in advance.
Update:
I got it to work, by editing the iOS Controller to set the HTTPBody of the request like so:
NSMutableData *highScoreData = [NSMutableData data];
[highScoreData appendData: [[NSString stringWithFormat: @"player_name=%@;", @"test"] dataUsingEncoding: NSUTF8StringEncoding]];
[highScoreData appendData: [[NSString stringWithFormat: @"score=%d", 0] dataUsingEncoding: NSUTF8StringEncoding]];
[request setHTTPBody: highScoreData];
For some reason putting a semicolon in there got Django to recognize it, assign the values to a new instance of a HighScore class, and save it. The logging on the test server indicates request.POST
is <QueryDict: {u'score': [u'9'], u'player_name': [u'test']}>
.
Still not quite sure what to make of all this.
As per Radu's suggestion, I took a look at highScoreData with NSLog, right after appending it to request.HTTPBody, and the result is <706c6179 65725f6e 616d653d 74657374 3b73636f 72653d39>
.
I'm a huge Obj-C noob, so again, any help is appreciated! Thanks again.
Since you control both sides, I'd drop the complexity of xml encoding the data and use RestKit or some other framework that makes it easy to communicate with Django.
精彩评论