搜尋此網誌

2024年8月30日 星期五

使用 Javascript 來 post form

緣起:


    前幾天在處理專案 (WebForm),有個下戴 excel 的功能需要調整,它是用 window.location 來指定 ashx,用 get 帶參數,然後 ashx 再依參數來回傳 excel 檔。

    這個功能碰上的問題是,get 帶的參數有時會太長,超過限制


    由於功能很複雜,我想要動最少量的程式碼來修正這個錯誤,最簡單能想到的就是 get 改成用 post。


情境:


    我這邊新增一個叫 WebApplication1 的 WebForm 來示範,專案裡有 birdshiutest.aspx 跟 DownloadTxt.ashx。

    birdshiutest.aspx,cs 檔沒有寫額外的程式,所以就不展示出來了

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="birdshiutest.aspx.cs" Inherits="WebApplication1.birdshiutest" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>
    <script type="text/javascript">
        function DownloadTxt() {
            window.location.href = '/DownloadTxt.ashx?param1=hello&param2=test&param3=yes';
        }
    </script>
    <form id="form1" runat="server">
        <div>
            <button onclick="DownloadTxt(); return false;">下載txt</button>
        </div>
    </form>
</body>
</html>


    這是 DownloadTxt.ashx

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace WebApplication1
{
    /// <summary>
    /// DownloadTxt 的摘要描述
    /// </summary>
    public class DownloadTxt : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            string sParam1 = context.Request.QueryString.Get("param1");
            string sParam2 = context.Request.QueryString.Get("param2");
            string sParam3 = context.Request.QueryString.Get("param3");

            StringBuilder sbResult = new StringBuilder();
            sbResult.AppendLine($"參數 1 : {sParam1}");
            sbResult.AppendLine($"參數 2 : {sParam2}");
            sbResult.AppendLine($"參數 3 : {sParam3}");

            context.Response.Clear();
            context.Response.Buffer = false;
            context.Response.ContentEncoding = Encoding.UTF8;
            context.Response.Charset = "UTF-8";
            context.Response.ContentType = "text/plain";
            context.Response.AppendHeader("Content-Disposition", $"attachment;filename=download.txt");
            context.Response.Write(sbResult.ToString());
            context.Response.End();
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

    把 birdshiutest.aspx 跑起來,進到頁面後,點擊 "下載txt" 的按鈕,就能載 txt



改成用 Post:


    首先,要修改 DownloadTxt.ashx,主要是修 17~19 行,把 context.Request.QueryString.Get 改成 context.Request.Form.Get。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace WebApplication1
{
    /// <summary>
    /// DownloadTxt 的摘要描述
    /// </summary>
    public class DownloadTxt : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            string sParam1 = context.Request.Form.Get("param1");
            string sParam2 = context.Request.Form.Get("param2");
            string sParam3 = context.Request.Form.Get("param3");

            StringBuilder sbResult = new StringBuilder();
            sbResult.AppendLine($"參數 1 : {sParam1}");
            sbResult.AppendLine($"參數 2 : {sParam2}");
            sbResult.AppendLine($"參數 3 : {sParam3}");

            context.Response.Clear();
            context.Response.Buffer = false;
            context.Response.ContentEncoding = Encoding.UTF8;
            context.Response.Charset = "UTF-8";
            context.Response.ContentType = "text/plain";
            context.Response.AppendHeader("Content-Disposition", $"attachment;filename=download.txt");
            context.Response.Write(sbResult.ToString());
            context.Response.End();
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

    再來是 birdshiutest.aspx 頁面,這個要怎麼改呢 ? window.location.href 只能傳網址參數而已,用 ajax 嗎 ? 如果是用 ajax 呼叫取得回傳值,那又要怎麼開啟下載的頁面 ? 感覺不太好做,所以我後來就再看看有什麼其它的方法,最後是找到這個。它是動態產生一個 form 的 dom 物件,然後把要傳的值打包成 type="hidden" 的 input,放到 form 裡,然後把 from 掛到網頁 body 的後面,最後呼叫 form 的 submit function 來傳送表單。

    所以,我們 birdshiutest.aspx 的程式碼可以改成

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="birdshiutest.aspx.cs" Inherits="WebApplication1.birdshiutest" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>
    <script type="text/javascript">
        function postForm(path, params, method = 'post') {

            const form = document.createElement('form');
            form.method = method;
            form.action = path;

            for (const key in params) {
                if (params.hasOwnProperty(key)) {
                    const hiddenField = document.createElement('input');
                    hiddenField.type = 'hidden';
                    hiddenField.name = key;
                    hiddenField.value = params[key];

                    form.appendChild(hiddenField);
                }
            }

            document.body.appendChild(form);
            form.submit();
        }

        function DownloadTxt() {
            postForm('/DownloadTxt.ashx', {
                param1: 'hello',
                param2: 'test',
                param3: 'yes'
            });
        }
    </script>
    <form id="form1" runat="server">
        <div>
            <button onclick="DownloadTxt(); return false;">下載txt</button>
        </div>
    </form>
</body>
</html>

    一樣打開 birdshiutest.aspx 頁面,然後點擊按鈕,能成功下載 txt 檔


    開啟網頁開發人員工具來看,可以看到,確實有個動態產生的 form dom


    每次點 button,body 就都會加上一個 form,感覺不是很健康,所以可以再修改一下那個 postForm 的 function,改成 submit 完後過個 5 秒,就呼叫 remove 方法來把表格移除。



沒有留言:

張貼留言