使用 Aurelia 的 Fetch Client 时 Azure Translator API(认知服务)出现 CORS 错误

CORS errors on Azure Translator API (Cognitive Services) when using Aurelia's Fetch Client

我尝试使用来自 Windows Azure 的非常基本的 API 调用来翻译一些文本。他们给出了 quickstart example code.

我试过这段代码,效果很好。文本 Hello world 被翻译成德语和意大利语。

我删除了我的个人订阅密钥。

示例如下:

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));
});

看起来这段代码是 node 的服务器端库。现在我需要将此代码集成到我的 [aurelia][2] 应用程序中。于是想到用aurelia-fetch-client代替request的方法。我使用 Aurelia CLI。

这是我所做的:

在package.json中添加:

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

在aurelia.json中添加:

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

运行 npm install 在我的控制台中。

创建了一个测试页:

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;
    });

}

诀窍是能够获取传递给示例代码request的选项,并将其调整到aurelia-fetch-client。我没有成功。

不幸的是,我总是收到以下错误:

访问“https://api.cognitive.microsofttranslator.com/' from origin 'http://localhost:9000”已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:预检请求不允许重定向。

有什么建议吗?

TL;DR - 就像您一样,我很难从浏览器中的文档中获取说明。但是,附加 Subscription-Key 作为查询字符串参数似乎确实有效。

示例,请阅读评论:

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());
  }
}

请注意,对于此示例,您不需要 requestuuid 库。您只需要:

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

我还注意到您正在使用 TypeScript,因此将示例更改为使用 @autoinject 来使用它。

更长的故事

如果您遇到很多 401 - 有一个 older MSDN blogpost 绝对值得一读。一些亮点:

  • different API endpoints 取决于您的 Azure 服务的地理位置。
  • 基于地理区域的单个服务(翻译器、视觉等)与通用的、更广泛的认知服务(a.k.a。认知 多服务订阅)。
  • 如果您使用的是单独的服务,API 密钥因服务而异。
  • 有 3 种不同的验证方式;但是,我只能让其中之一在浏览器中工作。

这也写的差不多in the official docs

话虽这么说,文档中的示例并不是很好。最起码,我是这么想的。首先,你要确保你确切地知道你应该做什么。这是一个卷曲示例:

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!'\''
}]'

虽然这听起来很简单,但我无法让它在浏览器中正常工作,我不断收到似乎是 401 的 CORS 问题。原因似乎是它没有吞下 'Ocp-Apim-Subscription-Key'。我还尝试用 subscriptionKey 交易授权不记名令牌 (example),这在浏览器中也不起作用。这些示例在 curl 或 Postman 中确实有效。

最后,回到使用 SubscriptionKey 作为查询字符串有所帮助。至少,对我来说。

希望对您有所帮助!