使用Javascript(或Angular)组合multipart/form-data,每个部分具有不同的Content-Type

61

问题提出得不正确,请看下面的更新

我需要将我的AngularJS项目与现有的RESTful API集成。这些API会消费POST请求,其中一个请求既上传文件又提交表单数据。但是,其中一个表单输入需要在请求中为Content-Type: Application/json

在网上搜索后,我只能使用Content-Type: multipart/form-data进行POST,其中每个部分都没有特定的MIME类型。那么,如何在Javascript中为每个部分组合具有不同MIMEmultipart/form-data

POST /api/v1/inventory
Host: localhost:8000
Origin: http://localhost:9000
Content-Type: multipart/form-data; boundary=------border

------border
Content-Disposition: form-data; name="owner"

john doe
------border
Content-Disposition: form-data; name="image"; filename="mybook.png"
Content-Type: image/png


------border
Content-Disposition: form-data; name="items"
Content-Type: application/json

{"name": "Book", "quantity": "12"}
------border--

相关参考文献:

  1. https://developer.mozilla.org/en-US/docs/Web/Guide/Using_FormData_Objects
  2. REST - HTTP Post Multipart with JSON
  3. http://code.activestate.com/recipes/578846-composing-a-postable-http-request-with-multipartfo/
  4. application/x-www-form-urlencoded或multipart/form-data?
  5. https://dev59.com/e2ox5IYBdhLWcg3wnlpI#9082243

更新

抱歉提出错误的问题。原始问题是我可以看到服务器调用逻辑类似于:

func POST(req):
     owner = req.owner // This is string
     image = req.image // This is file object
     itemQuantity = req.items.quantity // Items is an object with attribute quantity
     itemName = req.items.name // Items is an object with attribute name
我也成功地找出了如何提交这样的帖子请求。我将在下面发布我的答案。
再次抱歉问错问题。

该API目前已与移动应用程序一起使用。如果移动应用程序可以生成此类请求,我相信Web浏览器也应该能够组合这些请求。 - Yeo
参考5。展示我们应该能够组成这种请求的规范。 - Yeo
我不确定是否需要从头开始创建自定义XMLHttpRequest吗?https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest - Yeo
这个问题是否应该被关闭,因为它是一个错误的问题。 - Yeo
3个回答

111
根据FormData的文档,您可以使用Blob构造函数附加具有特定内容类型的字段:
var formData = new FormData();

formData.append('items', new Blob([JSON.stringify({
    name: "Book",
    quantity: "12"
})], {
    type: "application/json"
}));

经过仔细观察,结果发现会发送以下部分:

Content-Disposition: form-data; name="items"; filename="blob"
Content-Type: text/json

如果不想自己构建整个请求,唯一的安全选择就是传递一个字符串值:

formData.append('items', '{"name": "Book", "quantity": "12"}');

不幸的是,这并没有设置Content-Type头信息。


2
尽管它设置了 Content-Type: 'application/json',但它仍然被包装在一个 Blob 下面。因此,API 拒绝了我的请求。https://gist.github.com/9gix/a79a70f9b56ed252c00e#file-multipart-form-data-L6-L11 - Yeo
1
我之前的评论结果是基于Blob的正确语法。 - Yeo
1
@Yeo 你也试过使用 formData.append('items', JSON.stringify(...)) 吗? - Ja͢ck
1
因为我正在使用Angular,所以这就是我用来转换成JSON的方法 angular.toJson(),我相信它做的事情是一样的。https://docs.angularjs.org/api/ng/function/angular.toJson - Yeo
2
Yeap 可以使用 blob : var blob = new Blob([JSON.stringify(requestVo)], { type: "application/json" }) formData.append('data',blob); - cabaji99
显示剩余5条评论

9
错误1:我错误地认为items必须是JSON格式的,以便我们可以调用其属性。
解决方案:提交包含文件和对象格式的多部分请求非常简单。
form = new FormData();
form.append('items[name]', 'Book');
form.append('items[quantity]', 12);
form.append('image', imageFile);
form.append('owner', 'John Doe');

因此,请求头和请求体将如下所示:
POST /api/v1/inventory
Host: localhost:8000
Origin: http://localhost:9000
Content-Type: multipart/form-data; boundary=------border

------border
Content-Disposition: form-data; name="owner"

john doe
------border
Content-Disposition: form-data; name="image"; filename="mybook.png"
Content-Type: image/png


------border
Content-Disposition: form-data; name="items[name]"

Book
------border
Content-Disposition: form-data; name="items[quantity]"

12
------border--

1
啊,你救了我的一天。我也以为 JSON 必须像常规的 POST 数据一样只能是一个对象!谢谢。 - iamserious

7

在我的代码中,我上传文件和一些 JSON 数据,但是无论我怎么尝试都不能使它正常工作,直到我将 Content-Type 头部设置为未定义。

public uploadFile(code: string, file):angular.IHttpPromise<any>{
    var data = `{"query":"mutation FIRMSCORECARD_CALCULATE($code:String!){ FirmScorecardMutation{ BatchCalculate(Code:$code) }}","variables":{"code":"${code}"},"operationName":"FIRMSCORECARD_CALCULATE"}`;
    var formData = new FormData();
    formData.append('operations', data);
    formData.append('file', file, file.name);

    let config = {
        headers: {
            'Accept': 'application/json',
            'Content-Type': undefined
        }
    };
    let response = this.$http.post(this.graphqlUrl, formData, config);
    return response;
}

这是我唯一找到的地方,可以告诉你如何在JavaScript中设置GraphQL请求中的字符串变量。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接