How to get a MS Translator access token from Swift

2019-09-13 06:16发布

问题:

I am trying to work with a MS Translator API from Swift 3 (right now playing in playgrounds, but the target platform is iOS). However, I got stuck when I was trying to get an access token for OAuth2. I have following code (I tried to port the code from example at Obtaining an access token):

let clientId = "id".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let clientSecret = "secret".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let scope = "http://api.microsofttranslator.com".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!

let translatorAccessURI = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
let requestDetails = "grant_type=client_credentials&client_id=\(clientId)&client_secret=\(clientSecret)&scope=\(scope)"

let postData = requestDetails.data(using: .ascii)!
let postLength = postData.count

var request = URLRequest(url: URL(string: translatorAccessURI)!)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("\(postLength)", forHTTPHeaderField: "Content-Length")

request.httpBody = postData

URLSession.shared.dataTask(with: webRequest) { (returnedData, response, error) in
    let data = String(data: returnedData!, encoding: .ascii)
    print(data)
    print("**************")
    print(response)
    print("**************")
    print(error)
}.resume()

Of course, I used a valid clientId and a valid clientSecret.

Now the callback prints following information. First, the returnedData contain a message that the request was invalid, along with a following message:

"ACS90004: The request is not properly formatted."

Second, the response comes with a 400 code (which fits the fact that the request is not properly formatted).

Third, the error is nil.

Now I was testing the call using Postman, and when I used the same URI, and put the requestDetails string as a raw body message (I added the Content-Type header manually), I got the same response. However, when I changed the body type in Postman UI to application/x-www-form-urlencoded and typed in the request details as key value pairs through its UI, the call succeeded. Now it seems that I am doing something wrong with the message formatting, or maybe even something bad with the Swift URLRequest/URLSession API, however, I cannot get a hold on to what. Can somebody help me out, please? Thanks.

回答1:

OK, so after some more desperate googling and experimenting I have found my error. For the future generations:

The problem resided in encoding the parameters in the body of the PUT http request. Instead of:

let scope = "http://api.microsofttranslator.com"
             .addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!

I have to use the following:

let scope = "http://api.microsofttranslator.com"
             .addingPercentEncoding(withAllowedCharacters: 
                         CharacterSet(charactersIn: ";/?:@&=$+{}<>,").inverted)!

Seems that the API (or the HTTP protocol, I am not an expert in this) have problems with / and : characters in the request body. I have to give credit to Studiosus' answer on Polyglot issue report.