application / x-www-form-urlencoded 또는 multipart / form-data?


1335

HTTP에는 데이터를 POST하는 두 가지 방법이 있습니다 : application/x-www-form-urlencodedmultipart/form-data. 대부분의 브라우저는 파일을 multipart/form-data사용하는 경우에만 파일을 업로드 할 수 있음을 이해합니다 . API 컨텍스트에서 인코딩 유형 중 하나를 사용할 때 추가 브라우저가 필요합니까 (브라우저가 필요 없음)? 예를 들면 다음과 같습니다.

  • 데이터 크기
  • 비 ASCII 문자 존재
  • (인코딩되지 않은) 이진 데이터에 존재
  • 파일 이름과 같은 추가 데이터를 전송할 필요성

나는 기본적으로 지금까지 다른 내용 유형의 사용에 관한 공식적인 웹 지침을 찾지 못했습니다.


74
이들은 HTML 양식이 사용하는 두 가지 MIME 유형이라는 점을 언급해야합니다. HTTP 자체에는 그러한 제한이 없습니다 ... HTTP를 통해 원하는 MIME 유형을 사용할 수 있습니다.
tybro0103

답변:


2013

TL; DR

요약; 전송할 이진 (영숫자가 아닌) 데이터 (또는 크기가 큰 페이로드)가있는 경우을 사용하십시오 multipart/form-data. 그렇지 않으면을 사용하십시오 application/x-www-form-urlencoded.


언급 한 MIME 유형 Content-Type은 사용자 에이전트 (브라우저)가 지원해야하는 HTTP POST 요청에 대한 두 개의 헤더입니다. 이 두 가지 유형의 요청의 목적은 이름 / 값 쌍 목록을 서버로 보내는 것입니다. 전송되는 데이터의 유형과 양에 따라 방법 중 하나가 다른 방법보다 효율적입니다. 왜 그런지 이해하려면, 각각이 무엇을하고 있는지 살펴 봐야합니다.

의 경우 application/x-www-form-urlencoded서버로 전송 된 HTTP 메시지 본문은 본질적으로 하나의 거대한 쿼리 문자열입니다. 이름 / 값 쌍은 앰퍼샌드 ( &)로 구분되고 이름은 값과 등호 ( =)로 구분됩니다. 이에 대한 예는 다음과 같습니다. 

MyVariableOne=ValueOne&MyVariableTwo=ValueTwo

사양 에 따르면 :

[예약 및] 영숫자가 아닌 문자는 문자의 ASCII 코드를 나타내는 퍼센트 부호 및 2 개의 16 진수로`% HH '로 대체됩니다.

그것은 우리의 값 중 하나에 존재하는 영숫자가 아닌 각 바이트에 대해 그것을 표현하기 위해 3 바이트가 필요하다는 것을 의미합니다. 큰 이진 파일의 경우 페이로드가 3 배가되는 것이 비효율적입니다.

사용자들은 multipart/form-data(다른 응답에 의해 기술 된 바와 같이)에 온다. 이름 / 값 쌍을 전송하는이 방법은, 각각의 쌍은 MIME 메시지의 "일부"로서 표현된다. 부품은 특정 문자열 경계로 구분됩니다 (이 경계 문자열이 "값"페이로드에서 발생하지 않도록 구체적으로 선택). 각 부분에는와 같은 자체 MIME 헤더 세트가 Content-Type있으며 특히 Content-Disposition각 부분에 "이름"을 부여 할 수 있습니다. 각 이름 / 값 쌍의 값 부분은 MIME 메시지의 각 부분의 페이로드입니다. MIME 사양은 값 페이로드를 나타낼 때 더 많은 옵션을 제공합니다. 대역폭을 절약하기 위해 이진 데이터의보다 효율적인 인코딩을 선택할 수 있습니다 (예 : 기본 64 또는 원시 이진).

multipart/form-data항상 사용하지 않습니까? 대부분의 웹 양식과 같은 짧은 영숫자 값의 경우 모든 MIME 헤더를 추가하는 오버 헤드가보다 효율적인 이진 인코딩으로 인한 비용 절감보다 훨씬 클 것입니다.


84
x-www-form-urlencoded는 길이 제한이 있습니까, 아니면 무제한입니까?
Pacerier

34
@Pacerier 제한은 POST 요청을받는 서버에 의해 적용됩니다. 자세한 내용은이 스레드를 참조하십시오 : stackoverflow.com/questions/2364840/…
Matt Bridges

5
@ZiggyTheHamster JSON과 BSON은 각각 다른 유형의 데이터에 더 효율적입니다. Base64는 두 직렬화 방법 모두 gzip보다 열등합니다. Base64는 전혀 이점을 제공하지 않으며 HTTP는 바이너리 pyload를 지원합니다.
Tiberiu-Ionuț Stan

16
또한 양식에 이름이 지정된 파일 업로드가 포함 된 경우 urlencoded에는 파일 이름을 배치 할 수있는 방법이 없으므로 (form-data에서 content-disposition의 이름 매개 변수 임) 양식 데이터 만 선택할 수 있습니다.
귀도 반 로섬

4
@EML은 나의 괄호 "(이 경계 문자열이"값 "페이로드에서 발생하지 않도록 구체적으로 선택)"를 참조하십시오.
Matt Bridges

151

여기에서 첫 번째 파라를 읽으십시오!

나는 이것이 3 년이 너무 늦다는 것을 알고 있지만 Matt의 (허용 된) 대답은 불완전하며 결국 당신을 곤경에 빠지게 할 것입니다. 여기서 핵심은을 사용하도록 선택한 경우 서버가 결국 수신하는 파일 데이터에 multipart/form-data경계가 표시 되지 않아야 한다는 것입니다.

application/x-www-form-urlencoded경계가 없기 때문에 문제가되지 않습니다 . x-www-form-urlencoded또한 임의의 바이트 하나를 3 7BIT바이트 로 간단하게 전환하여 이진 데이터를 항상 처리 할 수 ​​있습니다 . 비효율적이지만 작동합니다 (이진 데이터뿐만 아니라 파일 이름을 보낼 수 없다는 의견은 정확하지 않습니다. 다른 키 / 값 쌍으로 보내십시오).

문제 multipart/form-data는 파일 데이터에 경계 구분 기호가 없어야한다는 것입니다 ( RFC 2388 참조 ; 섹션 5.2에는이 문제를 피하는 적절한 집계 MIME 유형이 없기 때문에 다소 절름발이가 포함되어 있습니다).

그래서, 첫눈에, multipart/form-data에없는 값 전혀입니다 어떤 파일 업로드 바이너리 또는 그렇지 않으면. 당신이 제대로 경계를 선택하지 않은 경우, 당신은 것입니다 서버가 잘못된 장소에서 경계를 발견 할 것이다, 당신의 파일이 잘립니다, 또는 POST - 결국 일반 텍스트 또는 원시 바이너리를 전송하고 있는지, 문제가 실패합니다.

핵심은 선택한 경계 문자가 인코딩 된 출력에 나타나지 않도록 인코딩 및 경계를 선택하는 것입니다. 간단한 해결책 중 하나는 사용 base64하지 않는 것입니다 (원시 바이너리는 사용 하지 마십시오 ). 에서는 베이스 64 (3), 임의의 바이트가 상기 출력 된 문자 세트는 4 개의 7 비트 문자로 부호화된다 [A-Za-z0-9+/=](즉, 영숫자, '+', '/'또는 '='). =는 특별한 경우이며 인코딩 된 출력의 끝에 단일 =또는 double 로만 나타날 수 있습니다 ==. 이제 base64출력에 나타나지 않는 7 비트 ASCII 문자열로 경계를 선택하십시오 . 인터넷에서 볼 수있는 많은 선택이이 테스트에 실패합니다. MDN 양식 문서예를 들어, 이진 데이터를 전송할 때 "blob"를 경계로 사용하십시오. 그러나 "! blob!"와 같은 것 base64출력에 나타나지 않습니다 .


52
multipart / form-data에 대한 고려는 데이터에 경계가 표시되지 않도록하는 것이지만, 충분히 긴 경계를 선택하면 달성하기가 매우 간단합니다. 이 작업을 수행하기 위해 base64 인코딩을 사용하지 마십시오. UUID는 무작위로 생성 된 경계와 같은 길이가 충분해야 stackoverflow.com/questions/1705008/... .
Joshcodes

20
@EML, 전혀 이해가되지 않습니다. 분명히 경계는 http 클라이언트 (브라우저)에 의해 자동으로 선택되며 클라이언트는 업로드 된 파일의 내용과 충돌하는 경계를 사용하지 않을만큼 똑똑합니다. 그것은 단순한 하위 문자열 일치 index === -1입니다.
Pacerier

13
@Pacerier : (A) "브라우저와 관련이없는 API 컨텍스트"라는 질문을 읽으십시오. (B) 브라우저는 어쨌든 당신을 위해 요청을 구성하지 않습니다. 수동으로 직접 수행하십시오. 브라우저에는 마법이 없습니다.
EML

12
@BeniBela, 그는 아마 '()+-./:=그때 사용하는 것이 좋습니다 . 그러나 하위 문자열 검사를 통한 무작위 생성은 여전히가는 길이며 한 줄로 수행 할 수 있습니다 while(true){r = rand(); if(data.indexOf(r) === -1){doStuff();break;}}. EML의 제안 (하위 문자열 일치를 피하기 위해 base64로 변환)은 불필요하게 성능이 저하된다는 것은 말할 것도없이 명백합니다. 한 줄 알고리즘이 똑같이 간단하고 단순하기 때문에 모든 문제는 없습니다. HTTP 본문 은 모든 8 비트 옥텟을 허용 하므로 Base64는 이러한 방식으로 사용되지 않습니다 .
Pacerier

31
이 답변은 토론에 아무것도 추가 할뿐만 아니라 잘못된 조언을 제공합니다. 먼저, 분리 된 부분으로 임의의 데이터를 전송할 때마다 선택된 경계가 페이로드에 존재할 수 있습니다. 이것이 일어나지 않도록하는 유일한 방법은 우리가 만드는 각 경계에 대한 전체 페이로드를 검사하는 것입니다. 완전히 비현실적입니다. 우리 는 충돌 의 무한한 확률을 받아들이고 "--- boundary- <UUID here> -boundary ---"와 같은 합리적인 경계를 제시합니다. 둘째, 항상 Base64를 사용하면 아무런 이유없이 대역폭을 낭비하고 버퍼를 채 웁니다.
vagelis

92

HTTP가 multipart 또는 x-www-form-urlencoded에서 POST로 제한되지 않는다고 생각합니다. 콘텐츠 형식 헤더 는 HTTP POST 방법에 직교 (당신은 어느 정장 당신 MIME 타입을 채울 수 있습니다). 일반적인 HTML 표현 기반 웹앱의 경우도 마찬가지입니다 (예 : json 페이로드는 ajax 요청에 대한 페이로드 전송에 매우 인기가있었습니다).

HTTP를 통한 Restful API와 관련하여 내가 가장 많이 접한 컨텐츠 유형은 application / xml 및 application / json입니다.

응용 프로그램 / xml :

  • data-size : XML은 매우 장황하지만 압축을 사용하고 쓰기 액세스 사례 (예 : POST 또는 PUT을 통해)가 읽기 액세스보다 훨씬 드물다고 생각할 때 일반적으로 문제가되지 않습니다 (대부분의 경우 모든 트래픽의 <3 % 임) ). 쓰기 성능을 최적화해야하는 경우는 거의 없습니다.
  • ASCII가 아닌 문자의 존재 : XML에서 인코딩으로 utf-8을 사용할 수 있습니다
  • 이진 데이터의 존재 : base64 인코딩을 사용해야합니다
  • 파일 이름 데이터 :이 필드를 XML로 캡슐화 할 수 있습니다

응용 프로그램 / json

  • 데이터 크기 : XML보다 작고 텍스트는 작지만 압축 할 수 있음
  • ASCII가 아닌 문자 : json은 UTF-8입니다.
  • 이진 데이터 : base64 ( json-binary-question 참조 )
  • 파일 이름 데이터 : json 내부의 자체 필드 섹션으로 캡슐화

자체 리소스로서의 이진 데이터

바이너리 데이터를 자체 자산 / 자원으로 나타내려고합니다. 다른 전화를 추가하지만 더 나은 연결을 해제합니다. 이미지 예 :

POST /images
Content-type: multipart/mixed; boundary="xxxx" 
... multipart data

201 Created
Location: http://imageserver.org/../foo.jpg  

이후의 리소스에서는 이진 리소스를 링크로 간단히 인라인 할 수 있습니다.

<main-resource>
 ...
 <link href="http://imageserver.org/../foo.jpg"/>
</main-resource>

흥미 롭군 그러나 언제 application / x-www-form-urlencoded를 사용하고 언제 multipart / form-data를 사용합니까?
최대

3
application / x-www-form-urlencoded는 요청의 기본 MIME 유형입니다 ( w3.org/TR/html401/interact/forms.html#h-17.13.4 참조 ). 나는 "정상적인"웹폼에 사용합니다. API의 경우 application / xml | json을 사용합니다. multipart / form-data는 첨부 파일을 생각할 때 중요한 종입니다 (응답 본문 내부에 여러 데이터 섹션이 정의 된 경계 문자열로 연결되어 있음).
마누엘 알다 나

4
나는 OP가 아마도 HTML 양식이 사용하는 두 가지 유형에 대해 묻는 것 같지만 이것이 지적되어 기쁘다.
tybro0103

30

마누엘이 말한 것에 동의합니다. 사실, 그의 의견은이 URL을 참조합니다 ...

http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4

... 상태는 다음과 같습니다.

"application / x-www-form-urlencoded"컨텐츠 유형은 많은 양의 이진 데이터 또는 ASCII가 아닌 문자를 포함하는 텍스트를 전송하는 데 비효율적입니다. 컨텐츠 유형 "multipart / form-data"는 파일, 비 ASCII 데이터 및 2 진 데이터를 포함하는 양식을 제출하는 데 사용해야합니다.

그러나 나를 위해 도구 / 프레임 워크 지원이 필요했습니다.

  • API 사용자가 어떤 도구와 프레임 워크로 앱을 구축 할 것으로 예상하십니까?
  • 사용할 수있는 프레임 워크 나 구성 요소가 있습니까?

사용자에 대한 명확한 아이디어와 사용자가 API를 사용하는 방법을 알면 결정에 도움이됩니다. API 사용자를 위해 파일 업로드를 어렵게하면 파일이 사라 지므로 지원하는 데 많은 시간을 소비하게됩니다.

그 다음으로 API 작성에 필요한 도구 지원과 하나의 업로드 메커니즘을 다른 메커니즘보다 쉽게 ​​수용 할 수 있습니다.


1
안녕하세요, 웹 서버에 무언가를 게시 할 때마다 웹 서버가 데이터를 디코딩해야한다는 것을 알리기 위해 콘텐츠 유형이 무엇인지 언급해야합니까? http 요청을 직접 작성하더라도 콘텐츠 유형 권리를 언급해야합니까?
GMsoF

2
@GMsoF, 선택 사항입니다. stackoverflow.com/a/16693884/632951을 참조하십시오 . 일반적인 오버 헤드를 피하기 위해 특정 서버에 대한 특정 요청을 작성할 때 컨텐츠 유형을 사용하지 않는 것이 좋습니다.
Pacerier

2

HTML5 캔버스 이미지 데이터 업로드에 대한 내 힌트는 다음과 같습니다.

인쇄 소용 프로젝트를 진행 중이며 HTML5 canvas요소 에서 가져온 이미지를 서버에 업로드하여 문제가 발생했습니다 . 나는 적어도 한 시간 동안 고군분투하고 있었고 서버에 이미지를 올바르게 저장하지 못했습니다.

내가 설정 한 후에 contentType내 jQuery를 아약스 호출의 옵션 application/x-www-form-urlencoded모두를 올바른 방법과 base64로 인코딩 된 데이터가 올바르게 해석하고 성공적으로 이미지로 저장된 갔다.


어쩌면 누군가를 도울 수 있습니다!


4
변경하기 전에 어떤 컨텐츠 유형을 보냈습니까? 서버가 전송 한 컨텐츠 유형을 지원하지 않기 때문에이 문제점이 발생했을 수 있습니다.
catorda

1

Content-Type = x-www-urlencoded-form을 사용해야하는 경우 FormDataCollection을 매개 변수로 사용하지 마십시오. asp.net에서 Core 2+ FormDataCollection에는 Formatters에 필요한 기본 생성자가 없습니다. 대신 IFormCollection을 사용하십시오.

 public IActionResult Search([FromForm]IFormCollection type)
    {
        return Ok();
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.