UWInfo Blog
發表新文章
[Join] | [忘記密碼] | [Login]
搜尋

搜尋意見
文章分類-#Author#
[所有文章分類]
所有文章分類
  • ASP.NET (48)
  • ASP.NET2.0 (15)
  • ASP.NET4.0 (34)
  • JavaScript (49)
  • jQuery (26)
  • FireFox (4)
  • UW系統設定 (3)
  • SQL (39)
  • SQL 2008 (25)
  • mirror (4)
  • SVN (4)
  • IE (9)
  • IIS (20)
  • IIS6 (1)
  • 閒聊 (7)
  • W3C (6)
  • 作業系統 (9)
  • C# (24)
  • CSS (12)
  • FileServer (1)
  • HTML 5 (11)
  • CKEditor (3)
  • UW.dll (13)
  • Visual Studio (16)
  • Browser (8)
  • SEO (1)
  • Google Apps (3)
  • 網站輔助系統 (4)
  • DNS (5)
  • SMTP (4)
  • 網管 (11)
  • 社群API (3)
  • SSL (4)
  • App_Inventor (1)
  • URLRewrite (2)
  • 開發工具 (6)
  • JSON (1)
  • Excel2007 (1)
  • 試題 (3)
  • LINQ (1)
  • bootstrap (0)
  • Vue (3)
  • IIS7 (3)
  • foodpanda (2)
  • 編碼 (2)
  • 資安 (3)
  • Sourcetree (1)
  • MAUI (1)
  • CMD (1)
  • my sql (1)
最新回應
  • Newtonsoft.Json.JsonConvert.DeserializeObject 失敗的情況
    test...more
  • dotnet ef dbcontext scaffold
    ...more
  • [ASP.NET] 利用 aspnet_regiis 加密 web.config
    ...more
  • IIS ARR (reverse proxy) 服務安裝
    ...more
  • [錯誤訊息] 請加入 ScriptResourceMapping 命名的 jquery (區分大小寫)
    ...more
  • 用 Javascript 跨網頁讀取 cookie (Cookie cross page, path of cookie)
    ...more
  • 線上客服 - MSN
    本人信箱被盜用以致資料外洩,是否可以請貴平台予以協助刪除該信箱之使用謝謝囉...more
  • 插入文字到游標或選取處
    aaaaa...more
  • IIS 配合 AD (Active Directory) 認証, 使用 .Net 6.0
    太感謝你了~~~你救了我被windows 認證卡了好幾天QQ...more
  • PostgreSQL 的 monitor trigger
    FOR EACH ROW 可能要改為 FOR EACH STATEMENT ...more
標籤
  • .
  • aspnet
  • html
  • server
  • ad
  • a
  • CK,
  • viewstate
  • sp_
  • 1
  • git
  • fb
  • 5151
  • 防火牆
  • 26
  • 992
  • reserve AP
  • 必學
  • 12
  • 28
  • CS
  • 0
  • if
  • Config
  • 筆記
  • 134 ORDER
  • 172
  • ef
  • 9818
  • 字串
  • c
  • dotnet
  • en21211211
  • -6716
  • User
  • -3089
  • code
  • mod ORDER
  • json
  • web.config
  • ie
  • http 錯èª
  • 102
  • -7160
  • FORT
  • jquery
  • EN
  • 140,
  • sysconfig
  • Captcha
頁數 1 / 9 下一頁
搜尋 ORM 結果:
AWS SES 建立 SNS 通知
在 powershell 執行以下的指令


# List of SES identities
$identities = @(
    "wztech.com.tw",
    "s3.com.tw",
    "jdcard.com.tw",
    "mskcable.com",
    "uwinfo.com.tw",
    "bike.idv.tw",
    "richwave.com.tw",
    "ctcn.com.tw",
    "jcard.com.tw",
    "bike@bike.idv.tw",
    "ee@ier.org.tw"
)

foreach ($identity in $identities) {
    # Convert identity to a valid topic name by replacing '@' and '.' with '_'
    $safeIdentity = $identity -replace "@", "_" -replace "\.", "_"
    $topicName = "SES_NOTIFY_$safeIdentity"
    $endpoint = "https://working.uwinfo.com.tw/aws/api/sns/receive?topic=$topicName"

    # Validate topic name format
    if ($topicName -notmatch '^[a-zA-Z0-9_\-\$]+$') {
        Write-Host "❌ Invalid topic name: $topicName"
        continue
    }

    # 1. Create SNS topic
    $topicArn = aws sns create-topic `
        --name $topicName `
        --query 'TopicArn' `
        --output text

    Write-Host "✔ Created topic: $topicArn"

    # 2. Subscribe webhook
    aws sns subscribe `
        --topic-arn $topicArn `
        --protocol https `
        --notification-endpoint $endpoint

    Write-Host "✔ Subscribed webhook: $endpoint"

    # 3. Link SES notifications
    foreach ($type in @("Delivery", "Bounce", "Complaint")) {
        aws ses set-identity-notification-topic `
            --identity $identity `
            --notification-type $type `
            --sns-topic $topicArn
        Write-Host "✔ $type linked to $topicName"
    }

    Write-Host "✅ Setup complete for $identity\n"
}

Write-Host "🎉 All identities processed."



收到的 log 會放在這裡: E:\WebBackup\195\ASP.NET Project\working\Data\Log\sns

 
 
More...
Bike, 2025/4/16 上午 10:40:39
AWS SES 建立 SNS 通知
在 powershell 執行以下的指令

# List of SES identities
$identities = @(
    "wztech.com.tw",
    "s3.com.tw",
    "jdcard.com.tw",
    "mskcable.com",
    "uwinfo.com.tw",
    "bike.idv.tw",
    "richwave.com.tw",
    "ctcn.com.tw",
    "jcard.com.tw",
    "bike@bike.idv.tw",
    "ee@ier.org.tw"
)

foreach ($identity in $identities) {
    # Convert identity to a valid topic name by replacing '@' and '.' with '_'
    $safeIdentity = $identity -replace "@", "_" -replace "\.", "_"
    $topicName = "SES_NOTIFY_$safeIdentity"
    $endpoint = "https://working.uwinfo.com.tw/aws/api/sns/receive?topic=$topicName"

    # Validate topic name format
    if ($topicName -notmatch '^[a-zA-Z0-9_\-\$]+$') {
        Write-Host "❌ Invalid topic name: $topicName"
        continue
    }

    # 1. Create SNS topic
    $topicArn = aws sns create-topic `
        --name $topicName `
        --query 'TopicArn' `
        --output text

    Write-Host "✔ Created topic: $topicArn"

    # 2. Subscribe webhook
    aws sns subscribe `
        --topic-arn $topicArn `
        --protocol https `
        --notification-endpoint $endpoint

    Write-Host "✔ Subscribed webhook: $endpoint"

    # 3. Link SES notifications
    foreach ($type in @("Delivery", "Bounce", "Complaint")) {
        aws ses set-identity-notification-topic `
            --identity $identity `
            --notification-type $type `
            --sns-topic $topicArn
        Write-Host "✔ $type linked to $topicName"
    }

    Write-Host "✅ Setup complete for $identity\n"
}

Write-Host "🎉 All identities processed."

--

收到的 log 會放在這裡: E:\WebBackup\195\ASP.NET Project\working\Data\Log\sns

 
 
More...
Bike, 2025/4/16 上午 10:40:31
IIS ARR (reverse proxy) 服務安裝
IIS 如果要使用 reverse proxy server 服務,其實網路上已經有很多文章可以參考
這篇文章只是記錄一下安裝上要注意的事

過去安裝 IIS 套件 可以透過 Web Platform Installer 搜尋下載
但現在 IIS 的 Web Platform Installer 已經不讓人搜尋下載可安裝的套件
所以要直接去微軟網站找相關套件 可以用 IIS ARR 搜尋
https://www.iis.net/downloads/microsoft/application-request-routing
下載 requestRouter_amd64.msi 安裝這個 (3.0版 2021 年以後就沒有更新了)
安裝前,IIS也要預先安裝 URLRewrite 2 套件

安裝很簡單,msi 安裝後,IIS重啟就可以看到
IIS 的主機設定,可以看到 "Application Reuest Routing Cache"  --> 點進後右邊有 Server Proxy Settings
proxy 的設定有一些地方要注意一下,避免未來採到雷

首先 當然先開啟 Enable proxy,下面針對一些要注意的屬性說明一下

1. Time-out : 預設120秒,如果你後端的站台有一些操作可能超過兩分鐘(例如處理報表),這個就調長一點
2. Reverse rewrite host in response header: 這個勾勾預設是開的,他的好意是同站台redirect(302) 到其他網頁,可以覆蓋
    host 讓 client端能跑到正常的網址。但如果你是 redirect 到其他站台,建議把它關掉,不然後端網站如果下
    redirect (302) 到別的站台,他會主動把 redirect網址 host 改為本站 (被雷過,所以要特別記下來)
3. Include TCP port from client IP:  這是一個 X-Forwarded-For 設定,預設是打開,這樣後端主機抓 client 來源 IP就會類似
    "112.121.100.100:443" ,但後端網站在抓 client端IP通常不會管 port number,因此就會造成比對 IP 發生錯誤
    所以建議還是把它關閉
4. Enable disk cache: 預設是勾勾打開,如果後端是靜態網站,例如圖片server,這個打開沒有問題,但如果後端網站是動態網站
    那還是關掉

 
More...
darren, 2025/1/10 上午 11:01:38
Restful 的 API 範例
Restful 的 API 範例,比較特別的是取得單一筆資料時,不是用一般常見的 {id} 而是用 get?id=xxx 的方式,以避免 XXS 的功擊。(不要把原網頁中的參數拼入 API 網址,要改用 Query String 的方式傳給 API)

using Ds;
using Ds.Gv;
using iText.Kernel.Geom;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NPOI.SS.Formula.Functions;
using NPOI.SS.Util;
using Su;
using System.Linq.Expressions;

namespace CallCampaign.Api
{
    /// <summary>
    /// 行銷活動
    /// </summary>
    [Route("api/call-campaign")]
    [ApiController]
    [SetAuthorizationFilter(Sh.AuthCode.不設限)]
    public class ReserveCampaignController : Controller
    {
        /// <summary>
        /// 取得行銷活動列表
        /// </summary>
        /// <param name="reserveCampaignName"></param>
        /// <param name="currentPage"></param>
        /// <param name="pageSize"></param>
        /// <param name="orderByName"></param>
        /// <param name="sort"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        [HttpGet("")]
        public async Task<object> ListAsync([FromQuery] string reserveCampaignName = "", [FromQuery] int? currentPage = 1, [FromQuery] int? pageSize = 20, [FromQuery] string orderByName = "OrderNo", [FromQuery] string sort = "asc")
        {
            if (pageSize > 500)
            {
                pageSize = 500;
            }

            if (!(sort == "asc" || sort == "desc"))
            {
                throw new CustomException(System.Net.HttpStatusCode.BadRequest, "sort只能是asc或desc");
            }

            var temp = new V_ReserveCampaign().GetType().GetProperty(orderByName);
            if (temp == null)
            {
                throw new CustomException(System.Net.HttpStatusCode.BadRequest, "不存在欄位");
            }

            Expression<Func<V_ReserveCampaign, bool>> q = p => p.Is_Deleted == "N"
                    && (string.IsNullOrEmpty(reserveCampaignName) || (p.ReserveCampaignName != null && p.ReserveCampaignName.Contains(reserveCampaignName)))
                    ;

            if (orderByName.ToLower().Trim() != "id")
            {
                orderByName += " " + sort + ", id desc";
            }
            else
            {
                orderByName += " " + sort;
            }

            var ct = NewContext.GvContext;
            var list = await ct.GetPageListAsync(q, columns: "Id, ReserveCampaignName, OrderNo, StartAt, EndAt, ModifierName, ModifyDate, CreatorName, CreateDate", page: currentPage ?? 1, pageSize: pageSize ?? 20, orderByName);
            //var list = await ct.GetPageListAsync(q, page: currentPage ?? 1, pageSize: pageSize ?? 20, orderByName + " " + sort);
            return list;
        }

        /// <summary>
        /// 取得行銷活動
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        [HttpGet("get")]
        public async Task<dynamic> GetAsync([FromQuery] int Id)
        {
            var res = await Ds.NewContext.GvContext.ReserveCampaigns.Where(r => r.Id == Id)
                .FirstOrDefaultAsync();

            if (res == null)
            {
                throw new CustomException(System.Net.HttpStatusCode.BadRequest, "查無資料 " + Id.ToString());
            }
            return res;
        }
                
        /// <summary>
        /// 建立行銷活動
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        [HttpPost("")]
        public async Task<object> CreateAsync(Dtos.CreateReserveCampaign dto)
        {
            var ct = NewContext.GvContext;
            var res = await Models.ReserveCampaignHelper.CreateReserveCampaignAsync(ct, dto);
            return res;
        }

        /// <summary>
        /// 編輯行銷活動
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        [HttpPatch("")]
        public async Task<object> UpdateAsync(Dtos.UpdateReserveCampaign dto)
        {
            var ct = NewContext.GvContext;
            var res = await Models.ReserveCampaignHelper.UpdateReserveCampaignAsync(ct, dto);
            return res;
        }

        /// <summary>
        /// 刪除行銷活動
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        [HttpDelete("")]
        public async Task<object> DeleteAsync([FromQuery] int id)
        {
            var res = await Ds.NewContext.GvContext.MarkDeleteAsync<Ds.Gv.ReserveCampaign>(id, Sh.ModifyInfo);
            return res;
        }
    }
}



再增加一個同步範例(只例出 action)


        /// <summary>
        /// 取得列表
        /// </summary>
        /// <param name="name"></param>
        /// <param name="currentPage"></param>
        /// <param name="pageSize"></param>
        /// <param name="orderByName"></param>
        /// <param name="sort"></param>
        /// <returns></returns>
        [HttpGet("")]
        public object List([FromQuery] string name = "", [FromQuery] int? currentPage = 1, [FromQuery] int? pageSize = 20, [FromQuery] string orderByName = "OrderNo", [FromQuery] string sort = "asc")
        {
            return "";
        }

        /// <summary>
        /// 取得明細資料
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        [HttpGet("get")]
        public object Get([FromQuery] int id)
        {
            return "";
        }

        /// <summary>
        /// 建立
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        [HttpPost("")]
        public object Create(Dtos.PhysicalCheckUpType dto)
        {
            return "";
        }

        /// <summary>
        /// 編輯
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        [HttpPatch("")]
        public object Update(Dtos.PhysicalCheckUpType dto)
        {
            return "";
        }

        /// <summary>
        /// 刪除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        [HttpDelete("")]
        public object Delete([FromQuery] int id)
        {
            return 1;
        }
More...
Bike, 2023/12/13 上午 08:54:28
平行序處理多筆資料
由於匯入91訂單,如果是一筆一筆抓,會耗費比較久的時間
下列寫法是,開兩條 thread 平行處理,可以節省約一半時間

public ConcurrentBag<V2SalesOrderGetDataList> ConcurencyGetApp91OrderDetails(List<string> TMCodes, int shopId, ref ConcurrentBag<string> orderErrMsgs)
        {
            #region 平行查詢
            ConcurrentBag<V2SalesOrderGetDataList> result = new ConcurrentBag<V2SalesOrderGetDataList>();
            ConcurrentBag<string> errs = new ConcurrentBag<string>();
            ConcurrentQueue<string> TMCodeQueues = new ConcurrentQueue<string>();
            TMCodes.ForEach(x => TMCodeQueues.Enqueue(x));

            Action searchOrderDetail = () =>
            {
                if (!TMCodeQueues.IsEmpty)
                {
                    string TMCode = string.Empty;

                    while (TMCodeQueues.TryDequeue(out TMCode))
                    {
                        V2SalesOrderGetReqModel reqModel = new V2SalesOrderGetReqModel()
                        {
                            ShopId = shopId,
                            TGCode = null,
                            TMCode = TMCode,
                            TSCode = null,
                        };

                        try
                        {
                            V2SalesOrderGet91API req = new V2SalesOrderGet91API();
                            var resp = req.Execute(reqModel, shopId);

                            if (resp.Status == "Success")
                            {
                                foreach (var l in resp.Data.List)
                                {
                                    result.Add(l);
                                }
                            }
                            else
                            {
                                //主單編號 , 錯誤原因
                                errs.Add(TMCode + " , " + resp.ErrorMessage);
                            }
                        }
                        catch (Exception ex)
                        {
                            errs.Add(TMCode + " , " + ex.Message);
                        }
                    }
                }
            };

            //指派Thread
            Parallel.Invoke(searchOrderDetail, searchOrderDetail);
            #endregion

            //查詢失敗
            orderErrMsgs = errs;

            return result;
        }
More...
darren, 2023/6/19 下午 04:47:30
Entity Framework 用起來和 Daper 越來越像了
Entity Framework 提供了 ExecuteSqlRawAsync 和 FromSqlRaw  之後,可以和 Dapper 非常類似的用法。

我們在用 Dapper 時。最常用的就是 sql command 加上一個物件做為參數,就可以執行 CRUD 的動作。

其實用 Entity Framework 的 ExecuteSqlRawAsync 和 FromSqlRaw 也可以逹到幾乎一樣的效果。

ExecuteSqlRawAsync 和 FromSqlRaw 接受的參數是 object array (其實是 Microsoft.Data.SqlClient.SqlParameter 的 array)

所以我們先做一個 Object to Microsoft.Data.SqlClient.SqlParameter Array 的擴充, 可參考: https://gist.github.com/aliozgur/75182b2e9b0a58b83443

不過很奇怪的是,原作者提供的擴充轉出來的會是 System.Data.SqlClient.SqlParameter  Array 無法直接使用於 ExecuteSqlRawAsync 和 FromSqlRaw,所以要稍微改一下,把 using System.Data.SqlClient; 改為 using Microsoft.Data.SqlClient; 即可:



另外,我們再自行 對 DbContext 做一個擴充如下:

        /// <summary>
        /// 會把物件 Parameter 的各 Property 帶入 SQL 中.
        /// </summary>
        /// <param name="dbct"></param>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static async Task<int> ExecuteParameterSqlAsync(this DbContext dbct, string sql, object parameters)
        {
            return await dbct.Database.ExecuteSqlRawAsync(sql, parameters.ToSqlParamsArray());
        }



最後的結果可以做到以下的效果: (res3 和 res4 是配合 FormattableString 的範例 )

 
範例程式碼:
        public async Task<object> EfTest()
        {
            var dbct = Ds.NewContext.GvContext;
            var insertSql = @"Insert into ProfileEvent(JobId,[Name],GvShoplineId,LiShoplineId,Phone,Email,LineId,GaClientId)
            Values(@JobId, @Name, null , null , null , @Email, @GaClientId , @GaClientId )";

            //執行 SQL 的原生寫法
            await dbct.Database.ExecuteSqlRawAsync(insertSql, new object[]
            {
                new SqlParameter("@JobId", 10),
                new SqlParameter("@Email", "XX'TT"),
                new SqlParameter("@Name", "AA'BB"),
                new SqlParameter("@GaClientId", "GaClientId"),
            });

            //執行 SQL 的擴充寫法
            await dbct.ExecuteParameterSqlAsync(insertSql, new
            {
                JobId = 10,
                Email = "XX'TT",
                Name = "AA'BB",
                GaClientId = "GaClientId"
            });

            //關於查詢
            string name = "%'B%";
            //原生寫法
            var res1 = await dbct.ProfileEvents.FromSqlRaw("Select top 100 * from ProfileEvent where Name like @name",
                new object[]
                    {
                        new SqlParameter("@name", "%'B%")
                    })
                .ToListAsync();

            //擴充, object to SqlParameter array
            var res2 = await dbct.ProfileEvents.FromSqlRaw("Select top 100 * from ProfileEvent where Name like @name",
                new { name }.ToSqlParamsArray())
                .ToListAsync();

            var res3 = await dbct.ProfileEvents.FromSql($"Select top 100 * from ProfileEvent where Name like {name}")
                .ToListAsync();

            var res4 = await dbct.ProfileEvents.FromSqlInterpolated($"Select top 100 * from ProfileEvent where Name like {name}")
                .ToListAsync();

            return new { res1, res2, res3, res4 };
        }

 
More...
Bike, 2023/5/6 下午 05:37:19
關於 Entity Framework Extensions 的 UpdateFromQueryAsync
這個指令還是會把所有的資料 Select 出來,再更新

原指令:

UPDATE Job Set En_Status = 200 Where En_Status = 100 and LastTouchAt < '2023-05-06 12:34:56'
其中  '2023-05-06 12:34:56' 是 DateTime.Now.AddMinutes(-2) 的結果(Web Server 端的時間扣 2 分鐘)

但,若是改使用 UpdateFromQueryAsync 如下:
var c = await Ds.NewContext.GvContext.Jobs.Where(j => j.En_Status == Cst.Job.Status.Running && j.LastTouchAt < DateTime.Now.AddMinutes(-2))
                .UpdateFromQueryAsync(j => new Ds.Gv.Job { En_Status = Cst.Job.Status.ReStarting });

產生的 SQL 如下:
UPDATE A 
SET A.[En_Status] = @zzz_BatchUpdate_0
FROM [Job] AS A
INNER JOIN ( SELECT [j].[Id], [j].[CancelledAt], [j].[CancelledBy], [j].[En_Status], [j].[EndAt], [j].[Exception], [j].[Filename], [j].[InformationJson], [j].[InitAt], [j].[Is_CheckOnly], [j].[LastTouchAt], [j].[LastTouchMessage], [j].[LoopStartAt], [j].[Name], [j].[ScheduleId], [j].[TotalTouch], [j].[TouchCount]
FROM [Job] AS [j]
WHERE [j].[En_Status] = 100 AND [j].[LastTouchAt] < DATEADD(minute, CAST(-2.0E0 AS int), GETDATE())
           ) AS B ON A.[Id] = B.[Id]

有兩個要注意的地方:
1. 它會先 Select 全欄位,再做更新

2. 它的時間是 DB Server 的現在時間。不是 Web Server 端的時間。


順便記錄一下。若是要執行 Update xx Ser cc = cc + 1 Where ...

EF 可寫為:
var c = await Ds.NewContext.GvContext.Jobs.Where(j => j.En_Status == Cst.Job.Status.Running && j.LastTouchAt < DateTime.Now.AddMinutes(-2))
                .UpdateFromQueryAsync(j => new Ds.Gv.Job { TotalTouch = j.TotalTouch + 1 });

轉換的 SQL 為:
UPDATE A 
SET A.[TotalTouch] = B.[TotalTouch] + 1
FROM [Job] AS A
INNER JOIN ( SELECT [j].[Id], [j].[CancelledAt], [j].[CancelledBy], [j].[En_Status], [j].[EndAt], [j].[Exception], [j].[Filename], [j].[InformationJson], [j].[InitAt], [j].[Is_CheckOnly], [j].[LastTouchAt], [j].[LastTouchMessage], [j].[LoopStartAt], [j].[Name], [j].[ScheduleId], [j].[TotalTouch], [j].[TouchCount]
FROM [Job] AS [j]
WHERE [j].[En_Status] = 100 AND [j].[LastTouchAt] < DATEADD(minute, CAST(-2.0E0 AS int), GETDATE())
           ) AS B ON A.[Id] = B.[Id]
More...
Bike, 2023/4/29 下午 08:44:31
sessionStorage 跨分頁無法讀取
問題:
在開出新分頁後,無法讀取 sessionStorage 的資料


可能原因:
Chrome 某個版本,Stop cloning sessionStorage for windows opened with noopener
a标签_blank默认 rel="noopener" ,所以a标签需要加入rel=“opener” 而才能像window.open("同源页面")这种方式新开的页面会复制之前的sessionStorage


解決方法:
開新分頁前,加入 rel=“opener”參數即可。
例:
<a href="http://..." target="_blank" rel="opener">Link</a>

$(".hlkPrint").click(function () {
    $("form").setPostDataToStorage();

    $("form").attr("rel", "opener");
    $("form").attr("target", "_blank");
    $("form").attr("action", "xxxxx.aspx");
    $("form").submit();
});





參考:
面试官:你确定多窗口之间sessionStorage不能共享状态吗???
More...
Reiko, 2022/12/1 下午 05:57:23
.Net 6 的 Captcha 實做
產生 FileStreamResult 物件的 function 如下: (目前置於 SU 之中,以便轉移)

        static List<Brush> CaptchaBrushes = null;
        public static FileStreamResult CreateCaptcha(string captcha)
        {
            if (CaptchaBrushes == null)
            {
                CaptchaBrushes = new List<Brush>();
                CaptchaBrushes.Add(Brushes.White);
                CaptchaBrushes.Add(Brushes.Gold);
                CaptchaBrushes.Add(Brushes.LightSkyBlue);
                CaptchaBrushes.Add(Brushes.LimeGreen);
                CaptchaBrushes.Add(Brushes.AliceBlue);
                CaptchaBrushes.Add(Brushes.AntiqueWhite);
                CaptchaBrushes.Add(Brushes.BurlyWood);
                CaptchaBrushes.Add(Brushes.Silver);
            }

            int width = 90;
            int height = 45;

            //https://stackoverflow.com/questions/61365732/cannot-access-a-closed-stream-when-returning-filestreamresult-from-c-sharp-netc
            //Using statements close and unload the variable from memory set in the using statement which is why you are getting an error trying to access a closed memory stream.You don't need to use a using statement if you are just going to return the result at the end.

            //這個 memory stream 不用關閉或 dispose
            var ms = new MemoryStream();

            // 釋放所有在 GDI+ 所佔用的記憶體空間 ( 非常重要!! )
            using (Bitmap _bmp = new Bitmap(width, height))
            using (Graphics _graphics = Graphics.FromImage(_bmp))
            using (Font _font = new Font("Courier New", 24, FontStyle.Bold)) // _font 設定要出現在圖片上的文字字型、大小與樣式
            {
                // (封裝 GDI+ 繪圖介面) 所有繪圖作業都需透過 Graphics 物件進行操作
                _graphics.Clear(Color.Black);

                // 如果想啟用「反鋸齒」功能,可以將以下這行取消註解
                //_graphics.TextRenderingHint = TextRenderingHint.AntiAlias;

                // 將亂碼字串「繪製」到之前產生的 Graphics 「繪圖板」上
                var x = 10;
                for(var i = 0; i < captcha.Length; i++)
                {
                    _graphics.DrawString(captcha.Substring(i, 1), _font, CaptchaBrushes[Su.MathUtil.GetRandomInt(CaptchaBrushes.Count)], x, Su.MathUtil.GetRandomInt(15));
                    x += 10 + Su.MathUtil.GetRandomInt(10);
                }

                // 畫線
                
                _graphics.DrawLine(new Pen(CaptchaBrushes[Su.MathUtil.GetRandomInt(CaptchaBrushes.Count)], 1),
                    Su.MathUtil.GetRandomInt(0, Convert.ToInt32((width * 0.9 / 2))), 0, Su.MathUtil.GetRandomInt(Convert.ToInt32(width / 2), Convert.ToInt32(width * 1.9 / 2)), height);
                
                _graphics.DrawLine(new Pen(CaptchaBrushes[Su.MathUtil.GetRandomInt(CaptchaBrushes.Count)], 1),
                    Su.MathUtil.GetRandomInt(Convert.ToInt32(width / 2), Convert.ToInt32(width * 1.9 / 2)), 0, Su.MathUtil.GetRandomInt(0, Convert.ToInt32((width * 0.9 / 2))), height);
                
                _graphics.DrawLine(new Pen(CaptchaBrushes[Su.MathUtil.GetRandomInt(CaptchaBrushes.Count)], 1),
                    0,
                    Su.MathUtil.GetRandomInt(height / 2),
                    width,
                    height / 2 + Su.MathUtil.GetRandomInt(height / 2)
                    );

                _graphics.DrawLine(new Pen(CaptchaBrushes[Su.MathUtil.GetRandomInt(CaptchaBrushes.Count)], 1),
                 0,
                 height / 2 + Su.MathUtil.GetRandomInt(height / 2),
                 width,
                    Su.MathUtil.GetRandomInt(height / 2)
                 );

                _bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            }

            ms.Seek(0, SeekOrigin.Begin);

            // Controller 的型別為 FileResult
            return new FileStreamResult(ms, "image/jpeg")
            { FileDownloadName = $"{DateTime.Now.Ymdhmsf()}.jpg" };
        }



輸出用的 Controller 和 Action 如下:

namespace Web.Controllers
{
    public class CaptchaController : Controller
    {
        [Route("captcha")]
        public async Task<FileStreamResult> Index()
        {
            //產生 Captcha 並存入 Session 之中。目前是四位數字
            string captcha = (await Ah.ReGetAsync<object>("api/kol/create-captcha-code")).ToString();

            //產生圖檔並回傳 FileStreamResult
            return Su.Wu.CreateCaptcha(captcha);
        }
    }
}
More...
Bike, 2022/9/25 下午 10:03:44
資安問題 -- 使用者輸入的資料成為檔名的一部份
使用者輸入的資料包括 cookie 的資料, header 的資料,form 和 query string 的資料。

可以使用以下的 function 避免寫入錯誤的檔案:

var newFilename = filename.Replace("..", "").ValidFilenameCharacters();
More...
Bike, 2022/9/22 下午 09:40:56
|< 123456789 >|
頁數 1 / 9 下一頁
~ Uwinfo ~