Office365開發系列——開發一個全功能的Word Add-In

来源:http://www.cnblogs.com/yanxiaodi/archive/2017/07/16/7192280.html
-Advertisement-
Play Games

2016年10月我參加了在北京舉行的DevDays Asia 2016 - Office 365應用開發”48小時黑客馬拉松“,我開發的一個Word Add-In Demo——WordTemplateHelper獲得了二等獎。在會場有幸結識了陳希章老師,在與陳老師的交流中受益良多,得知陳老師在準備一 ...


2016年10月我參加了在北京舉行的DevDays Asia 2016 - Office 365應用開發”48小時黑客馬拉松“,我開發的一個Word Add-In Demo——WordTemplateHelper獲得了二等獎。在會場有幸結識了陳希章老師,在與陳老師的交流中受益良多,得知陳老師在準備一個Office解決方案系列後,我想把這個Demo的開發過程簡要介紹給大家,以支持陳老師的無私奉獻,也希望更多的開發者參與到Office365的開發中來。

Office相關開發主要可以參考這個地址:https://dev.office.com/getting-started

本篇文章主要介紹其中的Office載入項開發,即Office Add-ins:https://msdn.microsoft.com/ZH-CN/library/office/jj220082.aspx

 

一、什麼是Office Add-Ins

什麼是Office Add-ins呢?在陳老師的上一篇文章中,對整個Office發展歷史都進行了梳理,我個人的理解就是,開發者可以在Office提供的平臺上,對Office做出一定的擴展以實現各種功能,比如之前錄製的巨集,寫的VBS的腳本,某種意義上都可以看做是Office的Add-ins。當然這隻是個人理解,不一定准確。目前的Office Add-Ins只支持Office2013以後的版本,開發方式也和以前的VBS有了很大的區別。

現在的Office Add-ins結構是這樣的:

m_tablet_H-2

一個Office Add-in其實是一個Web App,可以將其部署在任意位置,它可以在一個Office應用程式中運行。有一個manifest.xml清單文件用來指定該Web App如何來呈現,包括定義Web App 的URL。當Office載入這個Add-in時,實際上是提供了一個瀏覽器的環境,來運行指定的Web App。也就是說,現在開發一個Office Add-in,其實跟開髮網頁程式差不多,這對熟悉html+JavaScript+css的前端開發人員是非常容易上手的。微軟提供了豐富的JavaScript API來對Office進行操作,能實現什麼就取決於開發者的想象力了。

一個Word Add-In的實例:

clip_image003

 

二、Word Template Helper需求分析

我在得知有這個活動時,並沒有想好要做什麼,一直到坐上赴京的高鐵,才慢慢有了一個想法,這個想法也是來自平時的工作需要。在工作中經常要撰寫大量的文檔,如各種軟體需求規格說明書、公函、文書、操作手冊等,這些文檔都有規定的格式,一般情況下我是將一些已經寫好的Word文檔保存在一個文件夾里當做模板,下次寫這種文檔的時候複製一份,刪刪減減的再改。為何不自己寫個程式,將這些具有固定模式的文檔作為Word模板呢?雖然Word也有自己的模板,但實際上是非常有限的,並不能完全滿足我們的需要。如果這個功能做成一個模板商店,大家可以自由上傳、分享各自的模板,也許會方便許多。

Word自帶的模板是這樣的:

clip_image002

這些通用模板對專業性比較強的工作來說是遠遠不夠的。Word Template Helper的效果是這樣的:

clip_image003imageimage

主意有了,那麼就來看一下如何實現。我參加活動時的項目托管在碼雲上,為了寫這篇文章,我重新梳理了這個小demo,在Github上建了一個項目,並嘗試使用最新的.NET Core來實現後臺API部分。接下來就跟我一起動手吧。

三、項目架構

首先分析一下該項目的結構。文檔的模板數據,如模板標題、屬性等,需要保存在資料庫里,還需要一個Web API項目提供數據,Office Add-in為一個純前端項目,使用Angular2框架,採用非同步調用Web API的數據,實現搜索、載入模板等功能。插件的UI使用微軟提供的Fabric UI。整個項目的技術棧如下所示:

image

至於文檔的實體——Word文檔,是以Word格式文件存儲還是直接保存在資料庫中呢?如果是正式項目的話,當然是保存在雲存儲中是最合適的,但對於一個sample來說,直接保存在資料庫中也未嘗不可。因為是參加開發馬拉松,怎麼快怎麼來吧。包括ORM框架也是,只是為了快速實現採用的方式,不是最佳實踐。

這個sample的開發環境配置如下:

Windows 10 x64,

VS 2017(請確保全裝了Office開發工具)

VS Code

Node.js v7.10.0

NPM v4.2.0

ASP.NET Core 1.1

 

四、Web API開發

VS2017已經正式發佈了,我使用最新的.NET Core來實現Web API層。

1.新建項目

新建一個空白解決方案,命名為WordTemplateHelpe,然後在其中添加一個ASP.NET Core項目:

clip_image002[5]

選擇Web API:

clip_image004

2.安裝EF Core

在nuget管理器中搜索安裝一下幾個Nuget包:

Microsoft.EntityFrameworkCore.SqlServer:EF Core SQL Server

Microsoft.EntityFrameworkCore.Tools:EF命令行工具

Microsoft.EntityFrameworkCore.Tools.DotNet:EF Core命令行工具

clip_image006

3.建立Models

目前最新的EF都推薦使用Code First模式,即直接寫Model,EF框架會自動創建所需的資料庫。如果習慣DB First的話,也有一個很好的工具推薦:EntityFramework-Reverse-POCO-Code-First-Generator:https://visualstudiogallery.msdn.microsoft.com/ee4fcff9-0c4c-4179-afd9-7a2fb90f5838

 

可以直接在VS的擴展與更新里下載。這個工具可以很方便的根據資料庫生成所需的實體類。

首先添加一個模板類型的枚舉:

    /// <summary>
    /// 類型
    /// </summary>
    public enum TemplateType
    {
        /// <summary>
        /// Private
        /// </summary>
        [Description("Private")]
        Private = 0,
        /// <summary>
        /// Public
        /// </summary>
        [Description("Public")]
        Public = 1,
        /// <summary>
        /// Organization
        /// </summary>
        [Description("Organization")]
        Organization = 2,

    }

添加一個模板類:

    public class PrivateTemplateInfo
    {
        ///<summary>
        /// Id
        ///</summary>
        public string Id { get; set; }

        ///<summary>
        /// User Id
        ///</summary>
        public string UserId { get; set; }

        ///<summary>
        /// Template Id
        ///</summary>
        public string TemplateId { get; set; }

        ///<summary>
        /// Create Time
        ///</summary>
        public DateTime CreateTime { get; set; }
    }

 

因為還需要組織機構模板、用戶收藏等幾個表,這裡就不寫了,可參考Github上的示例。

4.創建資料庫上下文

有了Model後,需要指定哪些實體包含在數據模型中。添加一個Data文件夾,在其中創建一個名為WordTemplateContext.cs的文件:

    public class WordTemplateContext:DbContext
    {
        public WordTemplateContext(DbContextOptions<WordTemplateContext> options) : base(options)
        {

        }

        public DbSet<WordTemplateInfo> WordTemplateInfoes { get; set; }
        public DbSet<UserFavoriteInfo> UserFavoriteInfoes { get; set; }
        public DbSet<PrivateTemplateInfo> PrivateTemplateInfoes { get; set; }
        public DbSet<OrganizationTemplateInfo> OrganizationTemplateInfoes { get; set; }

    }

 

這樣就為每個實體創建了一個DbSet,對應資料庫中的表,實體對應表中的行。

 

5.使用依賴註入註冊上下文

ASP.NET Core預設實現了依賴註入。要把剛纔建立的WordTemplateContext註冊成服務,需要在Startup.cs中添加以下代碼:

        public void ConfigureServices(IServiceCollection services)
        {
            
            // Add framework services.
            services.AddDbContext<WordTemplateContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
            services.AddMvc();
        }

 

註意要添加using Microsoft.EntityFrameworkCore;不然會找不到UseSqlServer方法。

資料庫連接字元串在appsettings.json中配置:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=.;User ID=sa;Password=12QWasZX;Initial Catalog=WordTemplate;"
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  }
}

 

這裡使用了LocalDb,用於測試。當需要正式部署時,這裡需要更改為正式資料庫伺服器的地址及用戶名密碼。

6.初始化資料庫

下麵使用命令行初始化資料庫。在Data目錄下新建一個DbInitializer類,輸入以下方法:

    public static class DbInitializer
    {
        public static void Initialize(WordTemplateContext context)
        {
            context.Database.EnsureCreated();

            //TODO
            context.SaveChanges();
        }
    }

 

 

確保數據被創建。然後修改Startup.cs文件中的Configure方法:

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, WordTemplateContext context)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseMvc();
            DbInitializer.Initialize(context);
        }

 

 

7.創建API介面

現在寫個Controller看看。在Controller文件夾中添加一個控制器:

clip_image002[7]

這裡可以使用依賴註入,將資料庫上下文註入進來:

    [Produces("application/json")]
    [Route("api/WordTemplate/[action]")]
    public class WordTemplateController : Controller
    {
        private readonly WordTemplateContext _context;

        public WordTemplateController(WordTemplateContext context)
        {
            _context = context;
        }

 

我們以一個搜索模板的api為例:

        [HttpGet]
        public async Task<ResponseResultInfo<List<WordTemplateInfo>>> SearchWordTemplateList(string keyword)
        {
            ResponseResultInfo<List<WordTemplateInfo>> respResult = new ResponseResultInfo<List<WordTemplateInfo>>();
            try
            {

                List<WordTemplateInfo> list = await _context.WordTemplateInfoes.Where(x => x.Type == TemplateType.Public && x.Name.Contains(keyword)).OrderByDescending(x => x.CreateTime).ToListAsync();
                respResult.IsSuccess = true;
                respResult.Result = list;
                return respResult;

            }
            catch (Exception ex)
            {
                //LogHelper.ErrorWriteLine("Something wrong. The exception message::{0}", ex);
                respResult.IsSuccess = false;
                respResult.Message = string.Format("Something wrong. The exception message::{0}", ex.Message);
                return respResult;
            }
        }

 

命令行轉到項目目錄,運行以下命令

dotnet run

 

可以使用前端調試利器Postman來測試:

clip_image004[5]

API項目運行的具體地址需要記一下,後面做Add-In的時候要用到。具體代碼請參考Github。

8.允許跨域訪問

為了支持Add-in能夠跨域訪問我們的介面,還需要安裝以下的庫:

clip_image005

然後在Startup.cs的ConfigureServices方法中添加以下代碼:

#region 跨域
            services.AddCors(options =>
            options.AddPolicy("AllowCrossDomain",
            builder => builder.WithOrigins().AllowAnyMethod().AllowAnyHeader().AllowAnyOrigin().AllowCredentials())
            );
            
            #endregion

 

在需要跨域的WordTemplateController上添加一行:

[EnableCors("AllowCrossDomain ")]

 

這樣api就可以支持跨域訪問了。

 

五、Word Add-In開發

有了API,就可以開發Add-In部分了。開篇說到,Add-In實際上是一個Web App,通過JavaScript操作Office文檔對象,具體到這個項目來說,就是使用非同步的js去查詢、上傳、搜索存在伺服器上的模板文件,並動態的對當前Word文檔進行操作。

微軟在Github上開源了這個JavaScript API:https://github.com/Microsoft/Office-js-docs_zh-cn,相關文檔:https://msdn.microsoft.com/zh-cn/library/office/fp142185.aspx

開發步驟可參考:https://github.com/Microsoft/Office-js-docs_zh-cn/blob/master/docs/get-started/create-and-debug-office-add-ins-in-visual-studio.md

下麵來開發Add-In部分。

1.新建Add-In項目

在解決方案上點擊右鍵,添加一個Word Web外接程式:

clip_image002[9]

添加完成後,多了兩個項目:

clip_image003[6]

其中一個是清單文件,帶Web尾碼的就是Web App了。

2.設置manifest.xml

清單文件是非常重要的一個文件,描述載入項的所有設置。這個文件是自動生成的,但需要我們手動修改一些地方。好在文件中都有註釋,所以修改還比較容易:

clip_image005

最重要的是修改SourceLocation這個節點,這個地址設置的是Web App托管的位置。在Web端開發並部署後,要將這個節點改為正確的位置才能發佈。下麵這個節點也要改掉。

clip_image007

3.Web App分析

可以先運行一下這個模板試試,直接F5:

clip_image008

點擊此處就可以調出這個插件:

image

 

會自動打開Word並載入這個插件,文檔中的文本就是插件插入的。那麼是哪裡的代碼起作用的呢?

打開Home.js文件,找到如下代碼:

    function loadSampleData() {
        // Run a batch operation against the Word object model.
        Word.run(function (context) {
            // Create a proxy object for the document body.
            var body = context.document.body;

            // Queue a commmand to clear the contents of the body.
            body.clear();
            // Queue a command to insert text into the end of the Word document body.
            body.insertText(
                "This is a sample text inserted in the document",
                Word.InsertLocation.end);

            // Synchronize the document state by executing the queued commands, and return a promise to indicate task completion.
            return context.sync();
        })
        .catch(errorHandler);
    }

 

 

這裡的Word就是JavaScript API提供的對象,可以方便的對當前文檔內容進行操作。這樣思路就有了,可以通過JavaScript動態去調用Web API獲取查詢結果,將查詢到的文檔內容插入到當前文檔中,就實現了最初的目的。同時還可以將當前文檔的內容保存為模板上傳到伺服器上進行分享,一個完整功能的sample已經呼之欲出了。

4.使用Angular

我們使用最新的Angular4來開發前端頁面。當然如果使用JQuery的話也可以,但現在已經有點out了不是嗎?使用Angular可以快速開發一個MVVM架構的單頁面WebApp,非常適合這個需求。

這個demo的部分代碼參考微軟開源的一個項目:https://github.com/OfficeDev/Office-Add-in-UX-Design-Patterns-Code

Angular上手曲線還是有點陡的,官方給出了Angular CLI工具,可以快速搭建一個Angular應用。首先安裝TypeScript:

npm install -g typescript

 

然後安裝Angular CLI:https://github.com/angular/angular-cli

npm install -g @angular/cli

 

運行以下命令創建一個Angular項目:

ng new WordTemplateHelperSource

 

clip_image002[11]

然後使用cd WordTemplateHelperSource 命令轉到項目目錄,運行以下命令:

npm install

 

這個命令會安裝ng項目所需的依賴,如果安裝不成功,建議切換成淘寶npm鏡像進行安裝。

使用以下命令運行ng項目:

ng serve

 

可以在Chorme瀏覽器中瀏覽http://localhost:4200來查看效果:

clip_image003[8]

註意如果在IE中瀏覽是不正常的,這個問題我們到最後一節再給出解決辦法。

為什麼不直接在WordTemplateHelperWeb建呢?因為Angular應用還要進行打包,會在項目目錄下生成dist目錄,這才是正式要運行的部分。所以等開發完成後,將生成的dist目錄內的文件拷到WordTemplateHelperWeb就可以了。

在開發Angular的過程中,推薦使用VS Code,對TypeScript和Angular的支持都非常好。

因為本篇文章不是Angular的開發教程,所以Angular的具體知識這裡就不展開詳述了,感興趣的話可以自行下載Github代碼運行即可。

5.添加操作Word文件的service

為了操作Word文件,我們需要將其封裝成服務。使用以下命令添加一個service:

ng g service services\word-document\WordDocument

 

這樣會在app目錄中的相應路徑中生成一個名為WordDocumentService的服務。與此類似,生成其他的幾個service。其中主要的幾個方法如下:

查詢搜索的方法:

/**
     * search
     * 
     * @param {string} keyword
     * @returns {Promise<ResponseResultInfo<Array<WordTemplateInfo>>>}
     * 
     * @memberOf WordTemplateApiService
     */
    searchWordTemplateList(keyword: string): Promise<ResponseResultInfo<Array<WordTemplateInfo>>> {
        let url = `${AppGlobal.getInstance().server}/SearchWordTemplateList?keyword=${keyword}`;
        let promise = this.httpService.get4Json<ResponseResultInfo<Array<WordTemplateInfo>>>(url);
        return promise;
    }

 

這樣可以得到伺服器上存儲的文檔模板,實際是以Ooxml格式保存的string。

對於這個sample來說,使用Office JavaScript API並沒有太難的東西,主要用到了兩個方法:getOoxml()和insertOoxml(),前者可以讀取當前word文檔的Ooxml格式,後者可以設置當前word文檔的Ooxml格式。Ooxml就是Office2007之後版本使用的格式,如docx這種。

原API提供的都是callback函數,為了使用方便我將其封裝成Promise:

/**
     * get the ooxml of the doc
     * 
     * 
     * @memberOf WordDocumentService
     */
    getOoxml() {
        // Run a batch operation against the Word object model.
        return Word.run(function (context) {

            // Create a proxy object for the document body.
            var body = context.document.body;

            // Queue a commmand to get the HTML contents of the body.
            var bodyOOXML = body.getOoxml();

            // Synchronize the document state by executing the queued commands, 
            // and return a promise to indicate task completion.
            // return context.sync().then(function () {
            //     console.log("Body HTML contents: " + bodyHTML.value);
            //     return bodyHTML.value;
            // });
            return context.sync().then(() => { return bodyOOXML.value });
        })
            .catch(function (error) {
                console.log("Error: " + JSON.stringify(error));
                if (error instanceof OfficeExtension.Error) {
                    console.log("Debug info: " + JSON.stringify(error.debugInfo));
                }
                return "";
            });
    }

    /**
     * set the ooxml of the doc
     * 
     * @param {string} ooxml 
     * 
     * @memberOf WordDocumentService
     */
    setOoxml(ooxml: string) {
        // Run a batch operation against the Word object model.
        Word.run(function (context) {

            // Create a proxy object for the document body.
            var body = context.document.body;

            // Queue a commmand to insert OOXML in to the beginning of the body.
            body.insertOoxml(ooxml, Word.InsertLocation.replace);

            // Synchronize the document state by executing the queued commands, 
            // and return a promise to indicate task completion.
            return context.sync().then(function () {
                console.log('OOXML added to the beginning of the document body.');
            });
        })
            .catch(function (error) {
                console.log('Error: ' + JSON.stringify(error));
                if (error instanceof OfficeExtension.Error) {
                    console.log('Debug info: ' + JSON.stringify(error.debugInfo));
                }
            });
    }

 

當搜索到合適的模板後,可以單擊按鈕,調用setOoxml()方法,將其插入到當前word文檔中:

applyTemplate(template: WordTemplateInfo) {
    this.wordDocument.setOoxml(template.TemplateContent);
  }

 

這樣就完成了應用模板的功能。

 

如果要實現將當前文檔的內容保存為模板上傳到伺服器上,就可以調用getOoxml()方法得到當前文檔的Ooxml格式文本,上傳到伺服器保存即可。至於其他的加為收藏、添加為機構模板、設置為個人模板等都是設置模板屬性更新了,具體代碼不再贅述。

還有一點需要註意的是,開發的時候,這裡的伺服器地址要寫剛纔我們開發的ASP.NET Core的地址。

6.使用Fabric UI

對於一個Office Add-in來說,具有簡潔美觀、與Office統一的UI是必須的。微軟推薦使用Fabric UI來實現統一的界面樣式,詳見:https://dev.office.com/fabric

這裡提供了樣式、圖標、設計規範等很多資源,甚至還提供了React版的組件,如果使用React開發的話直接拿來用就可以了。這個demo是直接引用的style文件,配置在.angular-cli.json文件中:

clip_image001[6]

應用後就變成這樣子:

clip_image002

7.打包Add-in

剛纔只是在一個新項目里開發了一個靜態Web App,還要將其打包,複製到WordTemplateHelperWeb項目中。使用ng build –prod來打包Angular應用。打包後的文件會輸出到dist目錄下:

clip_image003[10]

註意還有一個需要註意的地方,如果僅這樣打包的話,是不支持IE瀏覽器的,但Office Add-In實際上內置的瀏覽器就是IE內核,所以我們需要做如下修改,找到src目錄中的polyfills.ts文件,將下麵部分的註釋取消:

clip_image005[5]

還要根據提示,運行npm install命令安裝幾個必須的依賴。這樣才能在IE系列瀏覽器中正常運行。再次運行ng build –prod進行打包。--prod參數的意義是以生產模式進行build,這樣生成的代碼體積更小,運行速度更快。

將WordTemplateHelperWeb項目中的原文件除了Web.config外,全部刪除。把dist目錄中的文件複製過來。

雖然本機開發時可以直接調試運行,但為了模擬真實的使用情況,我們把這個Web App也正式發佈一下。如果我們有Azure或其他主機的話就直接部署到伺服器上,現在只用本機IIS來承載這個Web App:

clip_image006

這樣該Add-In的地址就是:http://localhost/WordTemplateHelperWeb,

下麵把api運行起來,進入WordTemplateHelperApi目錄,運行dotnet run命令:

clip_image008

這樣API項目的地址是:http://localhost:5000/api/

這兩個地址不要混淆。剛纔在打包WebApp的時候也要註意,在common\app-global.ts文件中的api地址也要改成和實際api地址一樣的才可以:

    /**
     * api url
     * 
     * @type {string}
     * @memberOf AppGlobal
     */
    public server: string = "http://localhost:5000/api/WordTemplate";

 

 

現在打開WordTemplateHelperManifest清單文件,修改如下位置:

clip_image010

clip_image012

這裡填的是Add-In的地址,一定不要搞錯了。

6.運行測試

現在可以重新運行Add-In項目了,將啟動項目設置為WordTemplateHelper,運行:

clip_image013

我們可以粘貼一個模板,並上傳到伺服器上:

clip_image015

點擊Upload按鈕即可將當前文檔作為模板上傳到伺服器上分享。

clip_image016

搜索到相應的模板後,點擊apply按鈕即可將模板內容插入到當前文檔。

我們可以搜索模板,添加自己的模板,並將模板內容應用到當前文檔中。針對組織和個人還可以分別進行管理,我的設想是,這個小插件能夠做成一個模板商店之類的平臺,用戶可以自由的交換彼此的文檔模板,並可以收藏、添加到本人組織的模板庫中等等。稍加擴展就可以做成一個正式產品了。

7.載入載入動畫

在頁面載入時可以加一個載入提示,使用戶體驗更加友好。具體代碼可參考index.html中的css樣式。

六、小結

這篇文章拖了很久,去年的比賽,今年才把過程整理出來,實在很想對陳老師說一聲抱歉^_^。Office Add-In是一個比較新的開發領域,跟以前的開發方式有所不同,但熟悉前端的同學可以迅速進入這個領域,實際上就是寫網頁。這個實例從後端介面到前臺實現,是一個比較完整的項目,希望對Office開發有興趣的同學下載代碼研究一下,開發出更加實用的Add-In。因為這個項目並沒有實際部署,所以沒有上傳到商店中。下載代碼的用戶請勿用於商業用途。特此說明。

 

Github地址:https://github.com/yanxiaodi/WordTemplateHelper

 


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

-Advertisement-
Play Games
更多相關文章
  • 2017-07-15,這是我學習python的第一天。 首先,python是一門當下很火熱的開發語言,它的創始人是Guido Van Rossum。就目前情況而言,python語言的熱度持續上升,已經超過C#排名第四。Python崇尚優美,簡潔,清晰,是一種優秀並且廣泛使用的語言。 一、Python ...
  • 十進位轉成十六進位: Integer.toHexString(int i) 十進位轉成八進位 Integer.toOctalString(int i) 十進位轉成二進位 Integer.toBinaryString(int i) 十六進位轉成十進位 Integer.valueOf("FFFF",16 ...
  • 金庸經典《射雕英雄傳》里,黃蓉為了讓洪七公交自己和靖哥哥武功,天天對師傅美食相待,在做了“玉笛誰家聽落梅”這樣一些世間珍品之後,告訴師傅說今天要做的是"炒白菜"。洪七公露出非常欣賞的眼光,說:“好,我倒要看看你怎樣化腐朽為神奇。”上周五聽了一個我們內部的深度學習講座,基本這方面處於初始探索階段。上周 ...
  • 函數要短。短才方便閱讀、維護和設計。 函數只做一件事。依照單一職責原則(一個類只會因為一個原因改變)設計函數。一個函數要麼進行流程式控制制或邏輯判斷,要麼改變某事物的狀態,要麼計算並返回結果,要麼調用多個下一抽象級的其他函數(另一種流程式控制制而已)。不要有多餘功能。 我們可以把函數分解成多個抽象層級來設計 ...
  • 0.代碼概述 代碼說明:第一章中的代碼為了突出模塊化拆分的必要性,所以db採用了真實操作。下麵代碼中dao層使用了列印日誌模擬插入db的方法,方便所有人運行demo。 1.項目代碼地址:https://github.com/kingszelda/SpringAopPractice 2.結構化拆分,代 ...
  • 題外,Chrome最近在耗電量方面超過了Edge,不過記憶體占用還是高啊,開發時偶爾用用。這不,http://jqueryui.com/menu/的官方菜單都支持的不好,改改吧! 打開jquery-ui.css 找到.ui-menu .ui-menu-item項 註釋/刪除掉list-style-im ...
  • 今天在切圖的時候,碰到一個相容性的問題,很幸運最後通過張金鑫老師的文章解決了這個問題!但在閱讀張老師文章的時候,我有個地方不明白,在網上查了下也沒找到我想要的答案,後來自己想了半天好像是這麼回事,現在我把我的想法寫出來,如果有不對的地方,大家一定要指出哦。 如圖(事例1): 這是張老師文章中的一段事 ...
  • 一 概述 1.什麼是HTML? HyperText Markup Language,超文本標記語言,客戶端技術的技術,負責頁面展示。 2.HTML的特點 標簽不區分大小寫。 3.請求地址 HTML是客戶端技術的基礎,HTML運行在客戶端,面向整個互聯網,為了能夠保證正確地定位資源,在書寫請求地址時, ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...