(点击放大图像)
2016 年 10 月我参加了在北京举行的 DevDays Asia 2016 - Office 365 应用开发 “48 小时黑客马拉松 ”,我开发的一个 Word Add-in Demo —— WordTemplateHelper 获得了二等奖。在会场有幸结识了陈希章老师,在与陈老师的交流中受益良多,得知陈老师在准备一个 Office 解决方案系列后,我想把这个 Demo 的开发过程简要介绍给大家,以支持陈老师的无私奉献,也希望更多的开发者参与到 Office 365 的开发中来。
Office 相关开发主要可以参考这个地址: https://dev.office.com/getting-started
本篇文章主要介绍其中的 Office 加载项开发.
注意:文中网址均不支持点击跳转,请单击文末 阅读原文 以更好地查看相关代码。
一、什么是 Office Add-ins
什么是 Office Add-ins 呢?在陈老师这两篇文章中:
已经对整个 Office 发展历史都进行了梳理。我个人的理解是,开发者可以在 Office 提供的平台上,对 Office 做出一定的扩展以实现各种功能,比如之前录制的宏,写的 VBS 的脚本,某种意义上都可以看做是 Office 的 Add-ins。当然这只是个人理解,不一定准确。目前的 Office Add-ins 只支持 Office 2013 以后的版本,开发方式也和以前的 VBS 有了很大的区别。
现在的 Office Add-ins 结构是这样的:
(点击放大图像)
一个 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 的实例:
(点击放大图像)
二、Word Template Helper 需求分析
我在得知有这个活动时,并没有想好要做什么。一直到坐上赴京的高铁,才慢慢有了一个想法,这个想法也是来自平时的工作需要。在工作中经常要撰写大量的文档,如各种软件需求规格说明书、公函、文书、操作手册等,这些文档都有规定的格式。一般情况下我是将一些已经写好的 Word 文档保存在一个文件夹里当做模板,下次写这种文档的时候复制一份,删删减减的再改。为何不自己写个程序,将这些具有固定模式的文档作为 Word 模板呢?虽然 Word 也有自己的模板,但实际上是非常有限的,并不能完全满足我们的需要。如果这个功能做成一个模板商店,大家可以自由上传、分享各自的模板,也许会方便许多。
Word 自带的模板是这样的:
(点击放大图像)
这些通用模板对专业性比较强的工作来说是远远不够的。Word Template Helper 的效果是这样的:
(点击放大图像)
主意有了,那么就来看一下如何实现。我参加活动时的项目托管在码云上,为了写这篇文章我重新梳理了这个小 demo,在 Github 上建了一个项目,并尝试使用最新的. NET Core 来实现后台 API 部分。接下来就跟我一起动手吧。
三、项目架构
首先分析一下该项目的结构。文档的模板数据,如模板标题、属性等,需要保存在数据库里,还需要一个 Web API 项目提供数据,Office Add-in 为一个纯前端项目,使用 Angular2 框架,采用异步调用 Web API 的数据,实现搜索、加载模板等功能。插件的 UI 使用微软提供的 Fabric UI。整个项目的技术栈如下所示:
(点击放大图像)
至于文档的实体 —— 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 项目:
(点击放大图像)
选择 Web API:
(点击放大图像)
2. 安装 EF Core
在 Nuget 管理器中搜索安装一下几个 Nuget 包:
Microsoft.EntityFrameworkCore.SqlServer:EF Core SQL Server
Microsoft.EntityFrameworkCore.Tools:EF 命令行工具
Microsoft.EntityFrameworkCore.Tools.DotNet:EF Core 命令行工具
(点击放大图像)
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 的扩展与更新里下载。这个工具可以很方便的根据数据库生成所需的实体类。
首先添加一个模板类型的枚举:
(点击放大图像)
添加一个模板类:
(点击放大图像)
因为还需要组织机构模板、用户收藏等几个表,这里就不写了,可参考 Github 上的示例。
4. 创建数据库上下文
有了 Model 后,需要指定哪些实体包含在数据模型中。添加一个 Data 文件夹,在其中创建一个名为 WordTemplateContext.cs 的文件:
(点击放大图像)
这样就为每个实体创建了一个 DbSet,对应数据库中的表,实体对应表中的行。
5. 使用依赖注入注册上下文
ASP.NET Core 默认实现了依赖注入。要把刚才建立的 WordTemplateContext 注册成服务,需要在 Startup.cs 中添加以下代码:
(点击放大图像)
注意要添加 using Microsoft.EntityFrameworkCore; 不然会找不到 UseSqlServer 方法。
数据库连接字符串在 appsettings.json 中配置:
(点击放大图像)
这里使用了 LocalDb,用于测试。当需要正式部署时,这里需要更改为正式数据库服务器的地址及用户名密码。
6. 初始化数据库
下面使用命令行初始化数据库。在 Data 目录下新建一个 DbInitializer 类,输入以下方法:
(点击放大图像)
确保数据被创建。然后修改 Startup.cs 文件中的 Configure 方法:
(点击放大图像)
7. 创建 API 接口
现在写个 Controller 看看。在 Controller 文件夹中添加一个控制器:
(点击放大图像)
这里可以使用依赖注入,将数据库上下文注入进来:
(点击放大图像)
我们以一个搜索模板的 API 为例:
(点击放大图像)
命令行转到项目目录,运行以下命令
dotnet run
可以使用前端调试利器 Postman 来测试:
(点击放大图像)
API 项目运行的具体地址需要记一下,后面做 Add-in 的时候要用到。具体代码请参考 Github。
8. 允许跨域访问
为了支持 Add-in 能够跨域访问我们的接口,还需要安装以下的库:
(点击放大图像)
然后在 Startup.cs 的 ConfigureServices 方法中添加以下代码:
(点击放大图像)
在需要跨域的 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
下面来开发 Add-in 部分。
1. 新建 Add-in 项目
在解决方案上点击右键,添加一个 Word Web 外接程序:
(点击放大图像)
添加完成后,多了两个项目:
(点击放大图像)
其中一个是清单文件,带 Web 后缀的就是 Web App 了。
2. 设置 manifest.xml
清单文件是非常重要的一个文件,描述加载项的所有设置。这个文件是自动生成的,但需要我们手动修改一些地方。好在文件中都有注释,所以修改还比较容易:
(点击放大图像)
最重要的是修改 SourceLocation 这个节点,这个地址设置的是 Web App 托管的位置。在 Web 端开发并部署后,要将这个节点改为正确的位置才能发布。下面这个节点也要改掉。
(点击放大图像)
3.Web App 分析
可以先运行一下这个模板试试,直接 F5:
(点击放大图像)
点击此处就可以调出这个插件:
(点击放大图像)
会自动打开 Word 并加载这个插件,文档中的文本就是插件插入的。那么是哪里的代码起作用的呢?
打开 Home.js 文件,找到如下代码:
(点击放大图像)
这里的 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
(点击放大图像)
然后使用 cd WordTemplateHelperSource 命令转到项目目录,运行以下命令:
npm install
这个命令会安装 ng 项目所需的依赖,如果安装不成功,建议切换成淘宝 npm 镜像进行安装。
使用以下命令运行 ng 项目:
ng serve
可以在 Chrome 浏览器中浏览 http://localhost:4200 来查看效果:
(点击放大图像)
注意如果在 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。其中主要的几个方法如下:
查询搜索的方法:
(点击放大图像)
这样可以得到服务器上存储的文档模板,实际是以 Ooxml 格式保存的 string。
对于这个 sample 来说,使用 Office JavaScript API 并没有太难的东西,主要用到了两个方法:getOoxml() 和 insertOoxml(),前者可以读取当前 word 文档的 Ooxml 格式,后者可以设置当前 word 文档的 Ooxml 格式。Ooxml 就是 Office2007 之后版本使用的格式,如 docx 这种。
原 API 提供的都是 callback 函数,为了使用方便我将其封装成 Promise:
(点击放大图像)
当搜索到合适的模板后,可以单击按钮,调用 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 文件中:
(点击放大图像)
应用后就变成这样子:
(点击放大图像)
7. 打包 Add-in
刚才只是在一个新项目里开发了一个静态 Web App,还要将其打包,复制到 WordTemplateHelperWeb 项目中。使用 ng build –prod 来打包 Angular 应用。打包后的文件会输出到 dist 目录下:
(点击放大图像)
注意还有一个需要注意的地方,如果仅这样打包的话,是不支持 IE 浏览器的,但 Office Add-In 实际上内置的浏览器就是 IE 内核,所以我们需要做如下修改,找到 src 目录中的 polyfills.ts 文件,将下面部分的注释取消:
(点击放大图像)
还要根据提示,运行 npm install 命令安装几个必须的依赖。这样才能在 IE 系列浏览器中正常运行。再次运行 ng build –prod 进行打包。–prod 参数的意义是以生产模式进行 build,这样生成的代码体积更小,运行速度更快。
将 WordTemplateHelperWeb 项目中的原文件除了 Web.config 外,全部删除。把 dist 目录中的文件复制过来。
虽然本机开发时可以直接调试运行,但为了模拟真实的使用情况,我们把这个 Web App 也正式发布一下。如果我们有 Azure 或其他主机的话就直接部署到服务器上,现在只用本机 IIS 来承载这个 Web App:
(点击放大图像)
这样该 Add-in 的地址就是: http://localhost/WordTemplateHelperWeb,
下面把 API 运行起来,进入 WordTemplateHelperApi 目录,运行 dotnet run 命令:
(点击放大图像)
这样 API 项目的地址是: http://localhost:5000/api/
这两个地址不要混淆。刚才在打包 WebApp 的时候也要注意,在 common\app-global.ts 文件中的 API 地址也要改成和实际 API 地址一样的才可以:
(点击放大图像)
现在打开 WordTemplateHelperManifest 清单文件,修改如下位置:
(点击放大图像)
(点击放大图像)
这里填的是 Add-in 的地址,一定不要搞错了。
6. 运行测试
现在可以重新运行 Add-in 项目了,将启动项目设置为 WordTemplateHelper,运行:
(点击放大图像)
我们可以粘贴一个模板,并上传到服务器上:
(点击放大图像)
点击 Upload 按钮即可将当前文档作为模板上传到服务器上分享。
(点击放大图像)
搜索到相应的模板后,点击 apply 按钮即可将模板内容插入到当前文档。
我们可以搜索模板,添加自己的模板,并将模板内容应用到当前文档中。针对组织和个人还可以分别进行管理,我的设想是,这个小插件能够做成一个模板商店之类的平台,用户可以自由的交换彼此的文档模板,并可以收藏、添加到本人组织的模板库中等等。稍加扩展就可以做成一个正式产品了。
7. 载入加载动画
在页面加载时可以加一个载入提示,使用户体验更加友好。具体代码可参考 index.html 中的 css 样式。
六、小结
Office Add-in 是一个比较新的开发领域,跟以前的开发方式有所不同,但熟悉前端的同学可以迅速进入这个领域,实际上就是写网页。这个实例从后端接口到前台实现,是一个比较完整的项目,希望对 Office 开发有兴趣的同学下载代码研究一下,开发出更加实用的 Add-in。因为这个项目并没有实际部署,所以没有上传到商店中。下载代码的用户请勿用于商业用途。特此说明。
作者简介
闫晓迪,微软最有价值专家,热爱分享。熟悉 ASP.NET、WPF、UWP、TypeScript 等开发技术,多次参加微软组织的开发马拉松活动并获奖。
评论