Ошибка сервера при публикации данных MIME Multipart напрямую с помощью Javascript

Я пытаюсь создать данные составной формы непосредственно в Javascript, чтобы отправить свои данные на сервер. Я знаю, что есть плагины для форм Ajax, но я действительно думаю, что они не будут соответствовать моим потребностям, поскольку я буду создавать двоичные данные в браузере и отправлять их, как если бы это был файл submit (сервер, на который я буду публиковать, требует этого таким образом).

Моя проблема сейчас в том, что простейший пример построения текстовых данных Multipart MIME завершается сбоем на стороне сервера с ошибкой:

500 Internal Server Error: Invalid boundary in multipart form

Я попытался сократить код до минимума: в этом main.html (это имя, на которое он будет ссылаться позже в коде сервера) есть обе формы html для отправки текста способом html без Ajax. а также функцию Javascript, которая пытается воспроизвести это с помощью XmlHttprequest:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Posting MIME Multipart directly in Javascript</title>

<script>
function sendMimeMultipart(url, data) {

    boundary = '---------------------------1504702169761927311267328916'
    xhr = new XMLHttpRequest();

    xhr.open("POST", url);

    //Build the MIME POST request.
    var body = "--" + boundary + "\r\n";
    body += 'Content-Disposition: form-data; name="contents"\r\n\r\n';
    body += data+"\r\n";
    body += "--" + boundary + "--"+"\r\n";

    var fileSize = body.length
    xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary);
    xhr.setRequestHeader("Content-Length", fileSize);
    xhr.send(body);
    return true;
}

function sendData() {
    sendMimeMultipart('http://localhost:8080/myhandler', "Hello World!");   
}
</script>
</head>

<body onload='sendData()'> 

<form action = "myhandler" method = "post" enctype = "multipart/form-data">
    <input type = "text" name = "contents">
    <input type = "submit">
</form>

</body>
</html>

Это объект запроса, который поступает на сервер при использовании формы:

Request: POST /myhandler
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3
Connection: keep-alive
Content-Length: 187
Content-Type: multipart/form-data;
boundary=---------------------------18171295601131570933197493099
Host: localhost:8080
Keep-Alive: 115
Referer: http://localhost:8080/
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; es-ES; rv:1.9.2.20) 
Gecko/20110803  Firefox/3.6.20

-----------------------------18171295601131570933197493099
Content-Disposition: form-data; name="contents"

Hello World!
-----------------------------18171295601131570933197493099--

А это объект Request, поступающий на сервер при использовании функции Javascript (sendMimeMultipart):

Request: POST /myhandler
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 185
Content-Type: multipart/form-data; charset=UTF-8, 
boundary=---------------------------1504702169761927311267328916
Host: localhost:8080
Keep-Alive: 115
Pragma: no-cache
Referer: http://localhost:8080/
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; es-ES; rv:1.9.2.20) 
Gecko/20110803 Firefox/3.6.20

-----------------------------1504702169761927311267328916
Content-Disposition: form-data; name="contents"

Hello World!
-----------------------------1504702169761927311267328916--

Разница в 2 байта в Content-Length связана с тем, что браузер генерирует границы случайным образом, иногда они длиннее, а иногда короче. В данном случае это на один символ длиннее, что объясняет разницу в два байта в двух граничных вхождениях.

Я не думаю, что сервер имеет к этому какое-то отношение, автобус на всякий случай, если я опубликую код на стороне сервера. Это фрагмент Appengine, предназначенный только для локального использования; вызов «localhost:8080/myhandler» извлекает значение «содержимого», отправленное браузером, и сохраняет его в глобальной переменной. После этого вызов «localhost:8080/show» отображает ранее полученный текст. Как я уже упоминал ранее, если мы отправляем данные с помощью формы, текстовое содержимое сохраняется правильно, и обработчик «show» отображает его. Однако, если мы используем Javascript, строка кода:

        contents = self.request.get("contents")

В MyHandler (код ниже) выдает ошибку.

Вот код сервера:

import cgi
import datetime
import logging
import os

from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.api import images
from google.appengine.ext.webapp import template
from os import environ

contents=''

class mein(webapp.RequestHandler):
    def get(self):
        template_values = {}
        path = os.path.join(os.path.dirname(__file__), 'templates/main.html')
        self.response.out.write(template.render(path, template_values))

class MyHandler(webapp.RequestHandler):
    def post(self):
        global contents
        contents = self.request.get("contents")

class Show(webapp.RequestHandler):
    def get(self):
        global contents
        self.response.headers['Content-Type'] = "text/plain"
        self.response.out.write(contents)

application = webapp.WSGIApplication([
  ('/', mein),
  ('/myhandler', MyHandler),
  ('/show', Show)
], debug=True)

def main():
  run_wsgi_app(application)

if __name__ == '__main__':
  main()

Любая идея, почему это должно потерпеть неудачу? Я пробовал миллион разных вещей, но, похоже, я не могу заставить это работать или понять причину, почему это не работает!.

Заранее большое спасибо за ваши идеи и помощь.
Всего наилучшего:

-Хавьер


person Javier Reyes    schedule 19.08.2011    source источник
comment
Вы пытались удалить параметр charset из Content-Type? В данном контексте это не имеет смысла.   -  person Nick Johnson    schedule 22.08.2011
comment
Я не знаю, как избавиться от него. Это Javascript, который включает его. :-( И если вы видите поле принятой кодировки, похоже, что utf-8 не должно быть проблемой. Этот тест предназначен только для текстового содержимого, а не для двоичного.   -  person Javier Reyes    schedule 22.08.2011
comment
Однако само составное сообщение на самом деле не является текстом в том смысле, в котором оно используется, и каждая из отдельных частей имеет свой собственный тип содержимого. Тем не менее, кажется маловероятным, что это проблема.   -  person Nick Johnson    schedule 23.08.2011
comment
Обратите внимание, однако, что заголовок Accept-Charset указывает, что клиент примет в ответе — он не имеет отношения к тому, что примет сервер.   -  person Nick Johnson    schedule 23.08.2011
comment
Это происходит в dev, prod или в обоих? Как вы определяете содержимое отправляемых запросов?   -  person Nick Johnson    schedule 23.08.2011


Ответы (1)


Я столкнулся с тем же сообщением об ошибке при попытке создать загрузку файла http вручную. Я заставил его работать, заменив запятую (,) точкой с запятой (;) в заголовках Content-Type. В вашем случае, заменив:

xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary);

с:

xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary="+boundary);

Кажется, это связано с бэкэндом Python, потому что у меня была такая же проблема с Django (Python), и когда я отлаживал его на тестовом сервере PHP, работали и запятая, и точка с запятой.

Наконец, пример в RFC1867 ДЕЙСТВИТЕЛЬНО использует запятую, поэтому в конце я не уверен, что действительно правильный способ сделать это, но точка с запятой решила это для меня.

person Arahman    schedule 09.07.2012