最近發現活動室預定項目裏的上傳圖片有時候會有問題,週末找時間測試了一下,發現小圖片的上傳沒問題,大圖片上傳會有問題,並且異常信息還很奇怪,System.UriFormatException: Invalid URI: The Uri string is too long
看這個錯誤的信息還覺得是請求的 url 過長致使的,可是實際請求的 url 很短,詭異的異常信息git
爲了方便你們瞭解和測試這個bug,我在 Github 上提供了一個示例 https://github.com/WeihanLi/SamplesInPractice/blob/master/HttpClientTest/FormUrlEncodeContentTest.csgithub
HttpClient 示例代碼:async
public class FormUrlEncodeContentTest { private const string TestUrl = "https://cnblogs.com"; public static async Task FormUrlEncodedContentLengthTest() { using (var httpClient = new HttpClient(new NoProxyHttpClientHandler())) { using (var response = await httpClient.PostAsync(TestUrl, new FormUrlEncodedContent(new Dictionary<string, string>() { {"bigContent", new string('a', 65535)}, }))) { Console.WriteLine($"response status code:{response.StatusCode}"); } } } public static async Task ByteArrayContentLengthTest() { using (var httpClient = new HttpClient(new NoProxyHttpClientHandler())) { var postContent = $"bigContent={new string('a', 65535)}"; using (var response = await httpClient.PostAsync(TestUrl, new ByteArrayContent(postContent.GetBytes()))) { Console.WriteLine($"response status code:{response.StatusCode}"); } } } public static async Task StringContentLengthTest() { using (var httpClient = new HttpClient(new NoProxyHttpClientHandler())) { var postContent = $"bigContent={new string('a', 65535)}"; using (var response = await httpClient.PostAsync(TestUrl, new StringContent(postContent))) { Console.WriteLine($"response status code:{response.StatusCode}"); } } } }
測試代碼:post
InvokeHelper.OnInvokeException = Console.WriteLine; await InvokeHelper.TryInvokeAsync(FormUrlEncodeContentTest.FormUrlEncodedContentLengthTest); Console.WriteLine(); await InvokeHelper.TryInvokeAsync(FormUrlEncodeContentTest.StringContentLengthTest); Console.WriteLine(); await InvokeHelper.TryInvokeAsync(FormUrlEncodeContentTest.ByteArrayContentLengthTest); Console.WriteLine("Completed!");
輸出結果以下:測試
上傳圖片的時候會調用一個碼雲的一個 POST 接口來保存上傳的圖片,參數是經過 form-data 的方式傳遞的,在 POST 的時候報異常了,異常信息很詭異,具體信息和上面的是同樣的:url
這個異常信息看上去像是 url 過長致使的,可是實際的 url 很短只有幾百,並且從調用的堆棧上來看是 FormUrlEncodedContent
的 bug,而後根據異常堆棧信息去看了一下源碼,部分源碼以下:code
首先看 FormUrlEncodedContent
作了什麼:orm
而後再找上一層堆棧信息,Uri
是一個分部類(partial
),你若是直接在 Github 上 Find 的話會找到多個 Uri
相關的文件,最後在 UriExt
中找到了上面的 EscapeDataString
方法:blog
最後來看最上層的堆棧信息 UriHelper.EsacpeString
方法,找到異常拋出的地方接口
在 Uri 這個類中能夠找到上面定義的 c_MaxUriBufferSize
,它的值是 0xFFF0
轉成十進制就是 65520
找到問題所在以後,就能夠避免這個問題了,再遇到這個問題也就知道是怎麼回事了,上面的問題就是 post 的數據太大了,超過了這個限制,因此引起的異常
既然知道這個是 FormUrlEncodedContent
的 bug,那麼修復它就能夠經過避免使用它,能夠直接使用 ByteArray Content,或者不須要 Encode 處理直接用 StringContent 也是能夠的
後來在 Github 搜 issue 的時候發現也有不少人遇到了這個問題,這個問題會在 net5 中獲得修復,詳見 PR https://github.com/dotnet/corefx/pull/41686
文中一些源碼的連接在文章最後的 Reference
的部分能夠找到