Visual Studio項目模板與嚮導開發

来源:http://www.cnblogs.com/lixiaobin/archive/2017/02/05/VSIX.html
-Advertisement-
Play Games

創建Visual Studio插件,項目模板,項目創建嚮導等原來也是如此簡單。本文通過開發Prism的Plugin實例講解其中的實現過程與註意事項。 ...


在【Xamarin+Prism開發詳解系列】裡面經常使用到【Prism unity app】的模板創建Prism.Forms項目:

備註:由於Unity社區已經不怎麼活躍,下一個版本將會有Autofac,DryIOC,Ninject的項目模板。

image

自動彈出選擇框:

image

對於我這類還沒有動手寫過模板的人來說,確實挺新奇的。於是就決定自己也寫個類似的試試,目的就是通過嚮導創建跨平臺Plugin項目,類似Plugin for xamarin,不過是針對Prism,對應平臺可以自由選擇創建。試了之後才發現也有不少註意的地方,特寫下此文做備忘。

項目地址:https://github.com/NewBLife/Prism.Forms.Plugin

插件下載地址:TemplatesForPrism.vsix

1、安裝插件開發用的Extensibility模板與工具

image

2、新建VSIX Project插件項目

source.extension.vsixmanifest 這個文件相當重要,裡面可以指定安裝目標,模板,嚮導等。

最後我實現的例子:

安裝目標:VS2013以上(2017估計不行)

image

資產:Project模板,Item模板,Wizard嚮導

image

3、從【C# Item Template】與【C# Project Template】模板創建項目。

4、從【類庫】模板創建Wizard項目。(Wizard嚮導只能是類庫)

以上步驟之後的項目結構:

image

介紹:

  • Prism.Forms.Plugin.Nuspec:Plugin打包文件模板
  • Prism.Forms.Plugin:Plugin項目模板
  • Prism.Forms.Plugin.Wizard:Plugin創建嚮導
  • TemplatesForPrism:VSIX插件項目

 

 

 

 

5、添加引用

  • Prism.Forms.Plugin.Nuspec:Microsoft.VisaulStudio.CoreUtility
  • Prism.Forms.Plugin:Microsoft.VisaulStudio.CoreUtility
  • Prism.Forms.Plugin.WizardMicrosoft.VisaulStudio.TemplateWizardinterface,envdte
  • TemplatesForPrism:Prism.Forms.Plugin.Nuspec,Prism.Forms.Plugin,Prism.Forms.Plugin.Wizard

6、添加生成嚮導

6.1、NewProjectWizard項目選擇嚮導創建新建一個WinForm選擇框,返回選擇的結果。

image

繼承IWiazrd嚮導介面實現:

using EnvDTE;
using Microsoft.VisualStudio.TemplateWizard;
using System;
using System.Collections.Generic;
using System.IO;

namespace Prism.Forms.Plugin.Wizard
{
    public class NewProjectWizard : IWizard
    {
        private DTE _dte = null;

        private string _solutionDir = null;

        private string _templateDir = null;

        private string _projectName = null;

        PluginNewProjectDialogResult _dialogResult;

        public void BeforeOpeningFile(ProjectItem projectItem)
        {
        }

        public void ProjectFinishedGenerating(Project project)
        {
if
(_dialogResult.CreateAndroid) CreateProject(Target.Droid.ToString()); if (_dialogResult.CreateiOS) CreateProject(Target.iOS.ToString()); if
 (_dialogResult.CreateUwp)
                CreateProject(Target.UWP.ToString());

        }

void CreateProject(string platform) { string name = $"{_projectName}.{platform}"; string projectPath = System.IO.Path.Combine(_solutionDir, Path.Combine(_projectName, name)); string templateName = $"Prism.Forms.Plugin.{platform}"; string templatePath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(_templateDir), $"{templateName}.zip\\{templateName}.vstemplate"
);

            _dte.Solution.AddFromTemplate(templatePath, projectPath, name);

        }

        public void ProjectItemFinishedGenerating(ProjectItem projectItem)
        {
        }

        public void RunFinished()
        {
        }

        public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
        {
            try
            {
                _dte = automationObject as DTE;

                _projectName = replacementsDictionary["$safeprojectname$"];

                _solutionDir = System.IO.Path.GetDirectoryName(replacementsDictionary["$destinationdirectory$"]);

                _templateDir = System.IO.Path.GetDirectoryName(customParams[0] as string);

                PluginNewProjectDialog dialog 
= new PluginNewProjectDialog(); dialog.ShowDialog(); _dialogResult =
 dialog.Result;


                if (_dialogResult.Cancelled)

                    throw new WizardBackoutException();

            }
            catch (Exception)
            {
                if (Directory.Exists(_solutionDir))

                    Directory.Delete(_solutionDir, true);
                throw;
            }
        }

        public bool ShouldAddProjectItem(string filePath)
        {
            return true;
        }
    }
}

模板選擇後執行RunStarted==》彈出Winform選擇框==》執行ProjectFinishedGenerating創建子項目

 

6.2、項目名稱safeprojectgroupname嚮導創建

在NewProjectWizard嚮導的CreateProject方法裡面我們設置了每個子項目的名稱為"{_projectName}.{platform}",所以到達子項目的時候$safeprojectname$裡面已經帶了iOS,Droid,UWP等名稱,所以有必要進行處理。

using EnvDTE;
using Microsoft.VisualStudio.TemplateWizard;
using System.Collections.Generic;
using System.Threading;

namespace Prism.Forms.Plugin.Wizard
{
    public class SafeProjectGroupName : IWizard
    {
        public const string ParameterName = "$safeprojectgroupname$";

        static ThreadLocal<string> projectGroupName = new ThreadLocal<string>();
        bool isRootWizard;

        public void BeforeOpeningFile(ProjectItem projectItem)
        {
        }

        public void ProjectFinishedGenerating(Project project)
        {
        }

        public void ProjectItemFinishedGenerating(ProjectItem projectItem)
        {
        }

        public void RunFinished()
        {
            // If we were the ones to set the value, we must clear it.
            if (isRootWizard)
                projectGroupName.Value = null;
        }

        public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
        {
            if (projectGroupName.Value == null)
            {
                var name = replacementsDictionary["$safeprojectname$"];
                if (name.EndsWith(Target.Abstractions.ToString()))
                {
                    projectGroupName.Value = name.Replace($".{Target.Abstractions.ToString()}", string.Empty);
                }
                else if (name.EndsWith(Target.Droid.ToString()))
                {
                    projectGroupName.Value = name.Replace($".{Target.Droid.ToString()}", string.Empty);
                }
                else if (name.EndsWith(Target.iOS.ToString()))
                {
                    projectGroupName.Value = name.Replace($".{Target.iOS.ToString()}", string.Empty);
                }
                else if (name.EndsWith(Target.UWP.ToString()))
                {
                    projectGroupName.Value = name.Replace($".{Target.UWP.ToString()}", string.Empty);
                }
                // If there wasn't a value already, it means we're the root wizard itself.
                isRootWizard = true;
            }

            replacementsDictionary[ParameterName] = projectGroupName.Value;
        }

        public bool ShouldAddProjectItem(string filePath)
        {
            return true;
        }
    }
}

Debug測試方法:

  • 給類庫添加署名,否則無法註冊
  • 管理員許可權啟動VS2015 開發人員命令提示工具
  • 導航到wizard生成目錄
  • 執行gacutil -if Prism.Forms.Plugin.Wizard.dll 命令註冊類庫
  • 執行gacutil –l  列出類庫的Token信息(Prism.Forms.Plugin.Wizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b2335488e79a16da, processorArchitecture=MSIL)
  • 模板文件的WizarExtension節點添加以上Token信息與入口類名稱
  • 安裝模板,設置斷點
  • 啟動另一個Visual Studio程式
  • 調試-》附加到進程-》附加新啟動的Visual Studio進程
  • 新Visual Studio視窗選擇建立的模板,將進入斷點

7、添加模板文件並設置

星星Prism.Forms.Plugin.Nuspec模板設置

<?xml version="1.0" encoding="utf-8"?>
<VSTemplate Version="3.0.0" Type="Item" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010">
  <TemplateData>
    <Name>Plugin nuspec for Prism.Forms</Name>
    <Description>Create a nuspec file for Prism.Forms.Plugin solution</Description>
    <Icon>Prism.Forms.Plugin.Nuspec.ico</Icon>
    <TemplateID>325a0391-d11c-4432-8658-b70405881e87</TemplateID>
    
<ProjectType>General</ProjectType>
    <RequiredFrameworkVersion>2.0</RequiredFrameworkVersion>
    <NumberOfParentCategoriesToRollUp>1</NumberOfParentCategoriesToRollUp>
    <DefaultName>PrismFormsPlugin.nuspec</DefaultName>
  </TemplateData>
  <TemplateContent>
    <ProjectItem TargetFileName="$fileinputname$.nuspec" ReplaceParameters="true">Prism.Forms.Plugin.nuspec</ProjectItem>
  </TemplateContent>
</VSTemplate>

註意點:

  • Item模板的ProjectType預設為CSharp,有時候沒法找到,最好設置為General。
  • 模板參照文件的生成操作都設置為無。
  • 記得設置分類Category,這樣好找。

image

效果如下:(在添加新項裡面)

image

星星Prism.Forms.Plugin模板設置

image

每個文件夾下麵為相應的模板文件

  • Prism.Forms.Plugin.Abstractions:介面定義
  • Prism.Forms.Plugin.Droid:Android平臺特性的介面實現
  • Prism.Forms.Plugin.iOS:iOS平臺特性的介面實現
  • Prism.Forms.Plugin.UWP:UWP平臺特性的介面實現

最外面的入口模板文件Prism.Forms.Plugin.vstemplate設置:

<?xml version="1.0" encoding="utf-8"?>
<VSTemplate Version="3.0.0" 
Type="ProjectGroup"
 xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010">
  <TemplateData>
    <Name>Plugin for Prism.Forms</Name>
    <Description>Creates all files necessary to create a plugin for Prism.Forms</Description>
    <Icon>Prism.Forms.Plugin.ico</Icon>
    <ProjectType>CSharp</ProjectType>
    <RequiredFrameworkVersion>2.0</RequiredFrameworkVersion>
    <SortOrder>30</SortOrder>
    <TemplateID>16bac5e1-199d-4e08-9ed3-2ef287221be1</TemplateID>
    <DefaultName>PrismFormsPlugin</DefaultName>
    <ProvideDefaultName>true</ProvideDefaultName>
    <PromptForSaveOnCreation>true</PromptForSaveOnCreation>
    <NumberOfParentCategoriesToRollUp>1</NumberOfParentCategoriesToRollUp>
  </TemplateData>
  <TemplateContent>
<ProjectCollection> <ProjectTemplateLink ProjectName="$safeprojectname$.Abstractions">

        Prism.Forms.Plugin.Abstractions\Prism.Forms.Plugin.Abstractions.vstemplate
      
</ProjectTemplateLink> </ProjectCollection>
  </TemplateContent>
  <WizardExtension>     <Assembly>Prism.Forms.Plugin.Wizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b2335488e79a16da</Assembly>     <FullClassName>Prism.Forms.Plugin.Wizard.NewProjectWizard</FullClassName>   </WizardExtension>
</VSTemplate>

註意點:

  • 入口模板文件的Type必須為ProjectGroup,否則只創建一個工程項目
  • 模板內容必須使用ProjectTemplateLink添加每個子項目的模板文件設置
  • 由於使用嚮導可選擇方式創建子項目,所有這裡只添加了介面的模板文件
  • WizarExtension節點為嚮導程式設置,調用Prism.Forms.Plugin.Wizard.NewProjectWizard

子項目模板Prism.Forms.Plugin.Abstractions.vstemplate設置:

<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" 
Type="Project">
  <TemplateData>
    <Name>Prism.Forms.Plugin.Abstractions</Name>
    <Description>Implementation Interface for Prism.Forms.Plugin </Description>
    <ProjectType>
CSharp
</ProjectType>
    <ProjectSubType>
    </ProjectSubType>
    <SortOrder>1000</SortOrder>
    
<CreateNewFolder>true</CreateNewFolder>
    <DefaultName>Prism.Forms.Plugin.Abstractions</DefaultName>
    <ProvideDefaultName>true</ProvideDefaultName>
    <LocationField>Enabled</LocationField>
    <EnableLocationBrowseButton>true</EnableLocationBrowseButton>
    <Icon>__TemplateIcon.ico</Icon>
    <PromptForSaveOnCreation>true</PromptForSaveOnCreation>
  </TemplateData>
  <TemplateContent>
    <Project TargetFileName="$safeprojectname$.Abstractions.csproj" File="Prism.Forms.Plugin.Abstractions.csproj" ReplaceParameters="true">
      <ProjectItem ReplaceParameters="true" TargetFileName="I$safeprojectgroupname$.cs">IPrismFormsPlugin.cs</ProjectItem>
      <Folder Name="Properties" TargetFolderName="Properties">
        <ProjectItem ReplaceParameters="true" TargetFileName="AssemblyInfo.cs">AssemblyInfo.cs</ProjectItem>
      </Folder>
    </Project>
  </TemplateContent>
<WizardExtension> <Assembly>Prism.Forms.Plugin.Wizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b2335488e79a16da</Assembly> <FullClassName>Prism.Forms.Plugin.Wizard.SafeProjectGroupName</FullClassName> </WizardExtension>
</VSTemplate>

註意點:

  • Type為Project。
  • safeprojectgroupname是Prism.Forms.Plugin.Wizard.SafeProjectGroupName嚮導提供的參數,不是VS內部參數。值為用戶選擇模板時輸入的工程名稱。
  • 模板參照文件(IPrismFormsPlugin.cs,AssemblyInfo.cs等)的生成操作設置為無,否則編譯出錯。

IPrismFormsPlugin.cs設置:使用safeprojectgroupname

using System;

namespace $safeprojectgroupname$
{
    public interface I$safeprojectgroupname$
{
}
}

Prism.Forms.Plugin.Abstractions.csproj設置:使用safeprojectgroupname

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{$guid1$}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    
<RootNamespace>$safeprojectgroupname$</RootNamespace> <AssemblyName>$safeprojectname$</AssemblyName>
    <DefaultLanguage>en-US</DefaultLanguage>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    
<DocumentationFile>bin\Release\$safeprojectname$.XML</DocumentationFile>
  </PropertyGroup>
  <ItemGroup>
    <!-- A reference to the entire .NET Framework is automatically included -->
  </ItemGroup>
  <ItemGroup>
    <Compile Include="I$safeprojectgroupname$.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
</Project>

總體文件結構如下:

image

備註:

IPrismFormsPlugin.cs,AssemblyInfo.cs,Prism.Forms.Plugin.Abstractions.csproj等都是通過VS自動生成的,不是手工創建的。具體方法就是事先建立後想要的項目結構與文件,然後使用VS的導出模板功能就可以自動生成了。添加進模板項目的時候一定記得將生成操作設置為無。

image

8、插件source.extension.vsixmanifest文件設置

添加安裝目標,資產。

image

9、生成發佈

使用效果如下:

image

image

這回再也不用每次都添加好幾次工程了,一次搞定。懶人就是這樣想辦法偷懶微笑


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 七. 構建臨時系統 1. 通用編譯指南 a. 確認是否正確設置了 LFS 環境變數 b. 假定你已經正確地設置了宿主系統需求和符號鏈接 c. 對於每個軟體包: (1). 確保解壓軟體包時你使用的是 lfs 用戶 (2). 除非特別說明,刪除解壓出來的目錄和所有編譯過程中生成的 build 目錄 2. ...
  • GPS模塊使用串口通信,那麼它的的數據處理本質上還是串口通信處理,只是GPS模塊的輸出的有其特定的格式,需要字元串處理邏輯來解析其含義。如何高效的處理從GPS模塊接收到的數據幀,是GPS驅動設計的重點,本文使用狀態機的思想來處理GPS輸出的串口數據流,相對於定時從串口環形bufer取數據包然後依次解... ...
  • 一、應用調試1:使用strace命令來跟蹤系統調用1.strace移植 cd /work/debug/strace-4.5.15 tar xjf strace-4.5.15.tar.bz2 cd strace-4.5.15 patch -p1 ../strace-fix-arm-bad-syscal ...
  • 前一篇 簡單的介紹了Fody/PropertyChanged的使用方法, 這一篇,我們詳細介紹它的一些比較重要的特性和規則 1. Attributes 通過在類或屬性上標記這些特性,可以在編譯代碼時,註入特定的功能 ImplementPropertyChangedAttribute 為類標記此特性, ...
  • 在實現API Gateway過程中,另外一個需要考慮的問題就是部分失敗。這個問題發生在分散式系統中當一個服務調用另外一個服務超時或者不可用的情況。API Gateway不應該被阻斷並處於無限期等待下游服務的狀態。但是,如何處理這種失敗依賴於特定的場景和具體服務。如果是產品信息服務無響應,那麼API ...
  • 前言 MoonSharp是一個支持C#調用lua腳本的類庫,支持.net, .net core, mono, unity,因此在.net core中也能夠使用,而且載入和調用lua也很方便簡單; 官網:http://www.moonsharp.org/ 源碼:https://github.com/x ...
  • 前言 在工作中經常遇到C#數組、ArrayList、List、Dictionary存取數據,但是該選擇哪種類型進行存儲數據,對於初學者的我一直不知道該怎麼取捨。於是抽空好好看了下他們的用法和比較,在這裡總結下來,後面有需要改進的再更新。 初始化 數組: ArrayList: List: Dictio ...
  • "文章原link" 場景載入器 人員 | 操作 | 日期 | 備註 | | | Conerlius | Add | 2017 2 5 | 創建 scene的進場順序,方便對每個場景的回退,同一個彈出式的視窗在游戲中只允許出現一個,彈出視窗的進場順序管理 基本類型說明: Manager解釋 對ui的堆 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...