2
votes

I try to use a very basic API call from Windows Azure to translate some texts. They gives a quickstart example code.

I try this code and it works pretty well. The text Hello world is translated into deutch and italian.

I removed my personal subscription key.

Here is the sample:

const request = require('request');
const uuidv4 = require('uuid/v4');

const subscriptionKey = '........';

let options = {
    method: 'POST',
    baseUrl: 'https://api.cognitive.microsofttranslator.com/',
    url: 'translate',
    qs: {
      'api-version': '3.0',
      'to': ['de', 'it']
    },
    headers: {
      'Ocp-Apim-Subscription-Key': subscriptionKey,
      'Content-type': 'application/json',
      'X-ClientTraceId': uuidv4().toString()
    },
    body: [{
          'text': 'Hello World!'
    }],
    json: true,
};

request(options, function(err, res, body){
    console.log(JSON.stringify(body, null, 4));
});

It looks like this code is a server side library for node. Now I need to integrate this code into my [aurelia][2] application. So I think of using the aurelia-fetch-client to replace the request method. I use the Aurelia CLI.

Here is what I did:

Added in package.json:

"dependencies": {
    ....
    "@types/uuidv4": "^2.0.0",
    ...
    "uuidv4": "^4.0.0",
}

Added in aurelia.json:

"dependencies": [
      ...
      "uuidv4"
]

Run npm install inside my console.

Created a test page:

import { HttpClient, json } from 'aurelia-fetch-client';
import { autoinject } from 'aurelia-framework';
import * as uuidv4 from 'uuidv4';
import secret from '../secret';

@autoinject
export class Translator {
    constructor(httpClient: HttpClient) {
        this.httpClient = httpClient;
    }
    private httpClient: HttpClient;
    private translate(from, to, html) {

        debugger;

        var init: RequestInit =
        {
            method: 'POST',
            //mode: 'no-cors',
            headers: {
                'Ocp-Apim-Subscription-Key': secret.translatorKey,
                'Content-type': 'application/json',
              //'Content-Type': 'application/x-www-form-urlencoded',
                'X-ClientTraceId': uuidv4().toString()
            },
            credentials: 'same-origin',
            body: $.param({
                'api-version': '3.0',
                'from': 'en',
                'to': 'fr',
                'text': '<b>Hello World!</b>' })
          //body: json({ 'text': '<b>Hello World!</b>' })
        };

   this.httpClient.fetch(`https://api.cognitive.microsofttranslator.com/`, init)
    .then((result) => {   
        debugger;
    })
    .catch((error) => {
        debugger;
    });

}

The trick is to be able to get the options passed to the request of the sample code and adjust it to the aurelia-fetch-client. I did not succeed.

Unfortunately I always get the error below:

Access to fetch at 'https://api.cognitive.microsofttranslator.com/' from origin 'http://localhost:9000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

Any suggestions ?

1
See the answer at stackoverflow.com/a/42172732/441757. The server response from the COR preflight OPTIONS request that the browser makes to https://api.cognitive.microsofttranslator.com/ is apparently a 3xx redirect. Check the Location response header in the response. You can use the Network pane in browser devtools to view it. See what URL the response is trying to redirect you to.sideshowbarker
There are also q&a's like this and that related to the same topic. These are for the older webservices. @sideshowbarker may be right with the link to the preflight discussion, however this is not something that can be fixed client side. The answer about using 'Subscription-Key' as querystring, how awful that me be, may help though.Juliën

1 Answers

2
votes

TL;DR - Just like you, I'm having a hard time getting the instructions from the documentation working in the browser. However, appending the Subscription-Key as querystring parameter does seem to work.

Example, and please read the comments:

import { HttpClient } from 'aurelia-fetch-client';
import { autoinject } from 'aurelia-framework';

@autoinject
export class App {

  constructor(private http: HttpClient) {
  }

  private async attached(): Promise<void> {

    // Important: use either key1 or key2, not the guid from your subscription
    const subscriptionKey = 'YOUR-KEY-HERE';

    // Important: the endpoint is different per GEO-REGION
    const baseUrl = 'https://api-eur.cognitive.microsofttranslator.com';

    const body = [{
      'text': 'Hello World!'
    }];

    // Note: I couldn't get 'Ocp-Apim-Subscription-Key' working in the browser (only through Postman, curl)
    // Also, trading in the subscriptionKey for a Bearer token did not work for me (in the browser)
    // Therefor, we append it to the url later on.
    // Also notice, X-ClientTraceId' is NOT neccessary and the docs don't explain why it's needed
    const headers = new Headers();
    headers.append('Content-Type', 'application/json');

    const response = await this.http.fetch(`${baseUrl}/translate?api-version=3.0&to=nl&Subscription-Key=${subscriptionKey}`, {
      method: 'POST',
      headers: headers,
      body: JSON.stringify(body)
    });

    if (response.ok) console.log(await response.json());
  }
}

Note that for this example, you don't need request and uuid libraries. You just need:

$ npm install --save aurelia-fetch-client whatwg-fetch

I also noticed you're using TypeScript, so changed the example to use that by using @autoinject.

The longer story

If you're getting a lot of 401's - there is an older MSDN blogpost which is still definitely worth a read. Some highlights:

  • There are different API endpoints depending on the geolocation of your Azure service.
  • There is a difference in endpoints between the individual services (Translator, Vision etc.) which are geo-region based - and the generic, broader Cognitive Services (a.k.a. the Cognitive multi-service subscription).
  • If you're using individual services, API keys are different per service.
  • There are 3 different ways of authenticating; however, I can only get one of them to work in the browser.

This is also more or less written in the official docs.

That being said, the example in the docs aren't really any good. At least, in my opinion. First, you want to make sure you know exactly what you're supposed to do. Here is a curl example:

curl -X POST \
  'https://api-eur.cognitive.microsofttranslator.com/translate?api-version=3.0&to=nl' \
  -H 'Content-Type: application/json' \
  -H 'Ocp-Apim-Subscription-Key: <YOUR-KEY-HERE>' \
  -d '[{
    '\''text'\'': '\''Hello World!'\''
}]'

While this sounds easy, I cannot get this to work properly in the browser where I keep getting what appears to be CORS issues with 401's. The cause seems to be it doesn't swallow the 'Ocp-Apim-Subscription-Key'. I've also tried trading in the subscriptionKey for an Authorization Bearer token (example) which also didn't work in the browser. These examples do work in curl, or in Postman though.

Finally, just falling back to using the SubscriptionKey as querystring helped. At least, for me.

Hope this helps!