Ошибка XMLHttpRequest
У меня есть приложение Google App Engine, которое отлично работает на производстве, когда все работает на одном хосте, и в основном работает, когда веб-приложение работает на отдельном хосте. Все запросы к / с сервера (GET
, POST
, PUT
, DELETE
) ведут себя как ожидалось. Это указывает мне на то, что у меня все CORS настроено правильно во всей системе (я сражался в этой битве несколько недель назад и все получилось).
Единственная часть, которую я не могу сделать - это загрузка файлов. я использую django
, djangoappengine
, django-cors-headers
, а также filetransfers
и в результате все, что я не могу загрузить файлы при запуске с удаленного сервера, но все остальное работает правильно. В консоли JavaScript в Chrome я вижу следующую ошибку:
XMLHttpRequest cannot load http://localhost:8080/_ah/upload/ahl...<truncated>.
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:9000' is therefore not allowed
access. The response had HTTP status code 405.
Это явно ошибка CORS, поэтому я примерно знаю, что должно произойти. За исключением того, что я не могу понять, как внести необходимые изменения в мою конфигурацию, чтобы преодолеть это.
Вот моя общая установка:
dev_appserver.py
обслуживающий API через порт 8080grunt serve
обслуживание клиентского приложения на порту 9000- Настройки CORS:
- развитие:
CORS_ORIGIN_ALLOW_ALL = True
- производство:
CORS_ORIGIN_WHITELIST = [ '(app.domain.com for my app)' ]
- развитие:
Я полагаю, что в процессе производства исправление будет заключаться в настройке CORS на моем ведре, но я не уверен. Однако я не знаю, как настроить для этого локальный сервер разработки, чтобы я мог проверить общий поток данных перед развертыванием.
Вот JavaScript, который в конечном итоге не работает (приложение использует AngularJS
):
var form = angular.element('#media-form');
var data = new FormData(form);
// have the API return a URL using prepare_upload in
// filetransfers module to upload to:
var uploadActionUrl = https://api.domain.com/upload_url/';
$http.get(uploadActionUrl)
.then(function(response) {
// I get here with no problem
$http.post(response.data.action, formData)
.then(function(response) {
console.log('got:', response);
}, function(error) {
console.log('upload error:', error); // <- this is where I end up
});
}, function(error) {
console.log('get upload URL error:', error);
});
Опять же, код, очень похожий на этот, работает правильно при запуске с того же хоста (поэтому сам API работает правильно), и (что важно) все методы HTTP работают на всех конечных точках, кроме загрузки моего файла, поэтому сам CORS настроен правильно для взаимодействие с App Engine. Это только часть загрузки файла, которая не работает.
Мне приходит в голову, что, возможно, исправление включает сборку моей формы для загрузки с использованием JSON вместо FormData
, но я никогда не нашел способ сделать это в прошлом.
--- ОБНОВЛЕНО, ЧТОБЫ ДОБАВИТЬ ---
В качестве пояснения: конечная точка, которая вызывает эту ошибку, не находится непосредственно в моем приложении, а находится по URL-адресу, обрабатываемому отдельной службой Google. Код, который дает мне URL-адрес:
from google.appengine.ext.blobstore import create_upload_url
def prepare_upload(request, url, **kwargs):
return create_upload_url(
url,
gs_bucket_name = settings.GOOGLE_CLOUD_STORAGE_BUCKET
), {}
URL, который я получаю, имеет вид /_ah/upload/<one-time key>
и все, что происходит по этому URL-адресу (кажется) вне моего контроля, включая добавление заголовков.
2 ответа
Один из способов - установить заголовки http для определенного URL в вашем app.yaml
Так, например:
handlers:
- url: /_ah/upload....
...
http_headers:
Access-Control-Allow-Origin: http://localhost:9000
Ваши обработчики http должны иметь метод OPTIONS для отправки заголовков cors в браузер.
Например; Chrome всегда отправляет запрос OPTIONS на один и тот же URL-адрес, прежде чем запросы PUT для проверки заголовков CORS. Если браузер не может получить ответ на запрос OPTIONS, cors завершится ошибкой.
Проверьте этот пример движка приложения webapp2.
class BaseRestHandler(webapp2.RequestHandler):
def options(self, *args, **kwargs):
self.response.headers['Access-Control-Allow-Origin'] = '*'
self.response.headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept'
self.response.headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE'