I can't use twitter api with Nim - twitter

I want to use twitter api with Nim.
But, I can't solve error.
{"errors":[{"code":85,"message":"The list failed validation: A list's name can't be blank."}]}
I success authentication.
Which I make a mistake using twitter API or
using Nim library oauth1,
sending Post method body?
import tables, oauth1, strutils, httpclient, json
proc parseResponseBody(body: string): Table[string, string] =
let responses = body.split("&")
result = initTable[string, string]()
for res in responses:
let r = res.split("=")
result[r[0]] = r[1]
proc getRequestToken(consumerKey, consumerKeySecret: string): Table[string, string] =
let response = getOAuth1RequestToken(
"https://api.twitter.com/oauth/request_token",
consumerKey,
consumerKeySecret,
isIncludeVersionToHeader = true)
if response.status == "200 OK":
return parseResponseBody(response.body)
else:
assert(false, response.body)
proc getAccessToken(consumerKey, consumerKeySecret, requestToken, requestTokenSecret, verifier: string): Table[string, string] =
let response = getOAuth1AccessToken(
"https://api.twitter.com/oauth/access_token",
consumerKey,
consumerKeySecret,
requestToken,
requestTokenSecret,
verifier,
isIncludeVersionToHeader = true)
if response.status == "200 OK":
return parseResponseBody(response.body)
else:
assert(false, response.body)
let
consumerKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
consumerKeySecret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
let requestToken = getRequestToken(consumerKey, consumerKeySecret)
echo getAuthorizeUrl("https://api.twitter.com/oauth/authorize", requestToken["oauth_token"])
let verifier = readLine(stdin)
let accessTokens = getAccessToken(
consumerKey,
consumerKeySecret,
requestToken["oauth_token"],
requestToken["oauth_token_secret"],
verifier)
let param = %*{"name": "chage","mode": "private","description": "description"}
let response = oauth1Request(
"https://api.twitter.com/1.1/lists/create.json",
consumerKey,
consumerKeySecret,
accessTokens["oauth_token"],
accessTokens["oauth_token_secret"],
httpMethod = HttpPost,
body = $param
)
echo response.body

Looking at the documentation for the Twitter API it seems like it takes it's input in the form of query parameters and not a JSON body. This means that you need to not create a param JSON object but rather a parameter string. This can be done by simple concatenation, but make sure to escape URI characters with something like: https://nim-lang.org/docs/uri.html#encodeUrl,string

Related

The '#' char gets converted to %23 causing the GET Request to fail

I am a new Swift developer using Swift 3 developing an iOS app. I need to make a URL Request to get some data from the web. That URL contains a # character.
I use URLComponents with URLQueryItems to build the request URL. During this process the # char gets converted to %23 which I think is valid utf8 encoding. Unfortunately, this causes the GET to fail.
To test I pasted the URL into my browser and changed %23 back to # and it worked just fine.
How can I fix this so it does not change # to URL. I did find a post from a couple years ago but it was using old framework items so it does not apply to me.
Below is the playground I made to illustrate and test this.
// ------------------------------------------------------------------
//: Playground - TestBuildURLWithParameters
//
// I am using this playground to build the proper
// URL for the GET request to get the detailed
// rtesults for a specific athlete where the "Key"
// is their Bib Nbr. If the GET cant find the specific
// Atlete with that URL it redirects you to the list
// of athlete results (which is no go to me in this case)
//
// Currently, there is a big "gotcha". In building the URL
// using component and query items, the foundation classes
// replace the '#' sign in the URL with the %23 which represents
// the pound sign. Unfortunately, the causes the GET to fail
// and redirects to athlete list which is not helpful
// I am still trying to figure out how to FORCE it to keep
// the '#" character in the URL so it will work
//
// ------------------------------------------------------------------
import Foundation
import UIKit
let baseURLString = "http://www.ironman.com/triathlon/events/americas/ironman-70.3/augusta/results.aspx"
let rd = "20150927"
let race = "augusta70.3"
let bibID = "93"
var detail = "1#axzz4FGGcjBOn"
print("Detail w/o unicocde: \(detail)")
detail = "1\u{0023}axzz4FGGcjBOn"
print("Detail with unicocde: \(detail)")
var components = URLComponents(string: baseURLString)!
var queryItems: [URLQueryItem] = [] // All Items after the "?"
let baseParams =
[
"rd": rd,
"race": race,
"bidID": bibID, // Note: HTML mispelled bib with bid so "bidID" is the URL search
"detail": detail
]
for (key, value) in baseParams
{
let item = URLQueryItem(name: key, value: value)
queryItems.append(item)
}
components.queryItems = queryItems // what does this look like
print("components: \(components)") // see it
It is not a good way to include fragment part of URL into query items.
Try this:
import Foundation
let baseURLString = "http://www.ironman.com/triathlon/events/americas/ironman-70.3/augusta/results.aspx"
let rd = "20150927"
let race = "augusta70.3"
let bibID = "93"
var detail = "1"
//# split the last query item and the fragment
let fragment = "axzz4FGGcjBOn"
var components = URLComponents(string: baseURLString)!
var queryItems: [URLQueryItem] = []
let baseParams =
[
"rd": rd,
"race": race,
"bidID": bibID,
"detail": detail
]
for (key, value) in baseParams
{
let item = URLQueryItem(name: key, value: value)
queryItems.append(item)
}
components.queryItems = queryItems
components.fragment = fragment
print("components: \(components)")
If you need you can choose the character that will receive the encoding.
In the charactersIn: you put all characters you want to encode.
Then you use the .inverted so all the others characters will go normal like this:
let customAllowedSet = NSCharacterSet(charactersIn:"=\"%/<>?#\\^`{|}").inverted
let encondedString = originalString.addingPercentEncoding(withAllowedCharacters: customAllowedSet)
print("enconded string: \(encondedString)")
Encode your parameters and then add it to URL, this will encode # before hitting API and you'll get desired result.
To encode parameters, you can use below code.
var parameterString = "your parameter string"
var encodedString = parameterString .addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
print(encodedString !)
I did get an answer via email from a friend that works for now. Basically, I added the query items manually to the URL using the URL extension below so it didn't monkey with the '#' char:
extension URL {
func appendingNonEscapedQueryItems(_ queryItems: [URLQueryItem]) -> URL {
let url = self
var urlString = url.absoluteString
for queryItem in queryItems {
let queryName = queryItem.name
guard let queryValue = queryItem.value else {
continue
}
let query = queryName + "=" + queryValue
if queryItem == queryItems.first {
urlString = urlString + "?" + query
}
else
{
urlString = urlString + "&" + query
}
}
return URL(string: urlString)!
}
}
...
let requestURL = components.url!.appendingNonEscapedQueryItems(queryItems)
print("URL \(requestURL)")

Swift REST POST losing quotes?

Somehow I've ended up completely stuck on what should be an easy REST call. I have a restful service (in node) that is expecting an array of strings. Every time I call with what appears to be a valid parameter, I get an error from the server that looks like it was trying to parse the string as an integer, which makes me think it is losing the quotes around the string somewhere.
Here's the original Swift (3) code (with two more variants at the end). "id" and "parmvalue" are passed to this function.
let url = URL(string: "\(root)path/\(id)/myendpoint")
var request = URLRequest(url:url!)
request.httpMethod = "POST"
request.addValue("application/json",forHTTPHeaderField: "Content-Type")
let params = [ "parm" : [ parmvalue ] ]
do {
let jsonParams = try JSONSerialization.data(withJSONObject: params)
request.httpBody = jsonParams
let task = URLSession.shared.dataTask(with: request, completionHandler: {(data, response, error) -> Void in
...etc
Here's the server code:
app.post('/path/:id/myendpoint', globals.authentication, function(req, res){
var param = JSON.parse(req.param('parm', "[]"));
var Id = req.param('id', -1);
...etc
Passing in a parmvalue = "898e1ac8-4892-4e6e-89cb-4b2ea9306f75.jpg", I get a 500 response from the server, and the data passed to the completionHandler for the dataTask looks like:
SyntaxError: Unexpected token e
and a stack trace pointing right at that "var param" line. The unexpected token that is returned almost always corresponds to the first non-numeric value in the guid.
Here was variant 1 (just hardcode the json, it isn't that complicated)
let url = URL(string: "\(root)path/\(id)/myendpoint")
var request = URLRequest(url:url!)
request.httpMethod = "POST"
request.addValue("application/json",forHTTPHeaderField: "Content-Type")
let testparamsString = "{ \"photos\" : [ \"\(filename)\" ] }"
let testparams = testparamsString.data(using: .utf8)
request.httpBody = testparams
...etc
And here was variant 2 (if json doesn't work, try query param format)
let url = URL(string: "\(root)path/\(id)/myendpoint")
var request = URLRequest(url:url!)
request.httpMethod = "POST"
let queryparams = "parm[]=\(filename.stringByAddingPercentEncodingForFormUrlencoded()!)"
request.httpBody = queryparams.data(using: .utf8)
(note that all of the code fragments have been somewhat anonymized wrt variable names and values, and all three variations in Swift generate the same response from the server, so if there's something wonky in a particular fragment, it's probably my anonymizing by hand)
Just for grins, here's part of a C# app that also calls this endpoint (using the RestSharp library) and works - so I pretty much trust that the server code is correct:
var request = new RestRequest($"/path/{id}/myendpoint", Method.POST);
var parmArray = new JArray();
parmArray.Add(parmvalue);
var jsonParms = JsonConvert.SerializeObject(parmArray, Formatting.None);
request.AddParameter("parm", jsonParms);
Variant 1 was close, but it needed an extra (escaped) backslash to keep those quotes in there. Here's what I ended up with:
let url = URL(string: "\(root)path/\(id)/myendpoint")
var request = URLRequest(url:url!)
request.httpMethod = "POST"
request.addValue("application/json",forHTTPHeaderField: "Content-Type")
let paramsString = "{ \"photos\" : \"[\\\"\(filename)\\\"]\" }"
request.httpBody = paramsString.data(using: .utf8)

How to send litter '&' as url parameter - swift

I have a code to update name of department in the database .. I use this encoding code :
let myurlstring="http:example/updateDepartment.php?deptName="+"\(deptName)"+"&id="+"\(deptID)"
let escapedString = myurlstring.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed)
let myUrl = URL(string:escapedString!)!
It works very well , but when deptName string contains this litter & it's not working.
example1 : send request with deptName = "CIT and Network" it will work .
example2 : send request with deptName = "CIT & Network" will be in the database as only "CIT" any litter after & will be skipped.
any help ?
Use URLComponents + URLQueryItem instead. It can be used to encode the query part from structured input:
var comp = URLComponents(string: "http://example.com")!
comp.path = "/some path to/update.php"
comp.queryItems = [
URLQueryItem(name: "deptName", value: "CIT & Network"),
URLQueryItem(name: "id", value: "123456"),
]
let url = comp.url!
print(url)
// http://example.com/some%20path%20to/update.php?deptName=CIT%20%26%20Network&id=123456

sending a list of images to Django REST framework - iOS Client

Im trying to send a list of images from iOS application to a Django REST backend. This is the iOS Request using AlamoFire.
iOS Code:
let URL = "myURL"
var imagesDictonaryList = [[String : AnyObject]]()
var images = [UIImage]()
for _ in 1...3 {
images.append(UIImage(named: "profileImagePlaceholder")!)
}
//let imagesData = imagesToBase64(images)
for index in 0..<3 {
var myDictionary = [String:AnyObject]()
myDictionary["name"] = "\(index)"
myDictionary["image"] = images[index]
imagesDictonaryList.append(myDictionary)
}
print(imagesDictonaryList)
let parameters = [
"title": "service 1 title",
"description": "service 1 description",
"price": "11",
"images": imagesDictonaryList
]
Alamofire.request(.POST, URL, parameters: parameters as? [String : AnyObject]).responseJSON { response in
print(response.request) // original URL request
print(response.response) // URL response
print(response.data) // server data
print(response.result) // result of response serialization
if let dataString = String(data: response.data!, encoding: NSUTF8StringEncoding) {
print(dataString)
}
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
Server Code:
views.py
class PredefinedServiceList(APIView):
"""
List all Predefined Services, or create a new Predefined Service
"""
permission_classes = (permissions.AllowAny,)
def post(self, request):
serializer = PredefinedServiceSerializer(data=request.POST)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py
class ServiceImageSerializer(serializers.ModelSerializer):
def create(self, validated_data):
b64_text = validated_data.get('image', None)
image_data = b64decode(b64_text)
image_content = ContentFile(b"%s" % image_data, 'whatup.jpg')
validated_data['image'] = image_content
instance = ServiceImage.objects.create()
instance.save()
return instance
class Meta:
model = ServiceImage
fields = ('image', 'name')
class PredefinedServiceSerializer(serializers.ModelSerializer):
images = ServiceImageSerializer(many=True)
class Meta:
model = PredefinedService
fields = ('title', 'description', 'price', 'images')
def create(self, validated_data):
images_data = validated_data.pop('images')
service = PredefinedService.objects.create(**validated_data)
for image in images_data:
new_image, created = ServiceImage.objects.get_or_create(name=image.get('name', None))
new_image.image = image.get('image', None)
new_image.save()
service.images.add(new_image)
return service
models.py
class ServiceImage(models.Model):
image = models.ImageField(upload_to="predefined", null=True)
name = models.CharField(max_length=9001, null=True)
class PredefinedService(models.Model):
title = models.CharField(max_length=100, null=True, blank=True, default="untitled")
description = models.TextField(default="No description available.")
price = models.FloatField(default=0.000)
images = models.ForeignKey(ServiceImage, null=True, blank=True)
The problem is that the images are not in the request.data in the code but all other information sent are there.
the images are just an empty list after the iOS Client request. and the HTML i get is "index out of range" when hard coding list[0] to see the first picture received.
what might the problem be in this code? how to solve it? and is there any better way to implement this?
Files can be accessed via
request.FILES
so you would have to change
serializer = PredefinedServiceSerializer(data=request.POST)
to
serializer = PredefinedServiceSerializer(data=request.POST, files=request.FILES)
But if you're using DRF higher than 3.0, then according to the docs, the correct way to do this is using data attribute
serializer = PredefinedServiceSerializer(data=request.data)

Simple Post Request with Alamofire

It's my first post here, I'm trying to do this with Alamofire:
Swift code:
let name = "Taza"
let description = "50cl"
let parameters = ["name": name, "description": description]
Alamofire.request(.POST, "http://xxxxx.es/JSONpresenter.php?op=5", parameters: parameters, encoding: .JSON);
PHP code:
$op=$_GET['op'];
else if($op == 5)
{
// Get user id
$name = isset($_POST['name']) ? mysql_real_escape_string($_POST['name']) : “”;
$description = isset($_POST['description']) ? mysql_real_escape_string($_POST['description']) : “”;
add($name,$description);
}
But only get a register with "" in all cells. What am I doing wrong?
You need to use the URLEncoding instead of JSONEncoding for this particular case

Resources