[創意料理] ASP.NET MVC 在網址不變的情況下,自訂 HTTP 400(Bad Request)狀態碼的回應內容。

先前有寫過一篇個人常用的 ASP.NET MVC 自訂 HTTP 回應碼畫面的套路,一切看起來都很好,但是當使用者輸入一些資料,驗證不通過的時候,想要送 400(Bad Request)順便在 Body 中塞入一些訊息回傳給使用者,不做點調整是做不到的。

冤枉路一:existingResponse="Auto"、TrySkipIisCustomErrors

因為原先自訂回應碼畫面都是強制由 IIS 自訂錯誤網頁提供,所以當回傳 400 時就不能走這條路,因此嘗試將 existingResponse 設成 Auto,並且將 Response 的 TrySkipIisCustomErrors 屬性值設成 true

看起來運作正常,回傳 400 時能在 Body 塞入訊息,其他自訂回應碼的畫面也好好的。

直到我輸入了一個不存在的網址,看到的不是我自訂的畫面。

原來 ASP.NET MVC UrlRoutingModule 在 Match 不到網址回傳 404 的時候,是會跳過 IIS 自訂錯誤網頁的,這條路被封死了。

冤枉路二:<customErrors ... redirectMode="ResponseRewrite">

既然這樣,那我改走 customErrors 這條路,也將自訂回應碼畫面弄一組到 customErrors 上,然後將 redirectMode 設成 ResponseRewrite。

但是這條路也被封死了,因為 redirectMode="ResponseRewrite" 的情況下,自訂回應碼畫面的 redirect 值必須是檔案的型態。

路是人走出來的

無意中發現,IIS 自訂錯誤網頁觸發時,會將原來的 Request 內容原封不動地轉發給自訂錯誤網頁,那這樣是不是就能將訊息塞入 Request 裡面,讓它隨著轉發機制一路送到自訂錯誤網頁手上,再由自訂錯誤網頁把訊息塞入 Body 裡面回傳給使用者? 我們來試試看。

先在 IIS 錯誤網頁再自訂一條給 400 用的設定

然後約定好當 400 被觸發的時候,訊息可以從 Headers["400"] 裡面存取得到。

這邊要注意 Headers 的總大小限制,預設是 65535 bytes,所以如果當訊息塞進去超過限制大小的時候,可能就要尋求外部的儲存資源,像是將訊息放在 HttpContext.Cache,然後只要塞 Cache Key 進 Headers 就好了。

最後只要在需要的時候,指定回應碼為 400,將訊息塞進 Headers["400"] 裡面,就大功告成了。

當然不只有純文字訊息,JSON 也可以,也可以傳遞一個檔案路徑,回傳 FileResult,但看我們的需求去變化。

相關資源

C# 指南
ASP.NET 教學
ASP.NET MVC 指引
Azure SQL Database 教學
SQL Server 教學
Xamarin.Forms 教學