65.9K
CodeProject 正在变化。 阅读更多。
Home

如何创建具有页面渲染、TypeScript 和 "require" 的 ASP.NET Core API 项目

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2021年4月10日

CPOL

5分钟阅读

viewsIcon

10968

downloadIcon

150

设置一个 ASP.NET Core API 项目,并使用 TypeScript 和 "require" 来渲染页面以及在浏览器中加载 JavaScript 文件。

目录

引言

在我最近的文章《自适应分层知识管理》(第一部分第二部分)(好吧,这是无耻的自我宣传),我创建了一个 ASP.NET Core Web API 项目,而不是 ASP.NET Core Web App 项目,因为我正在实现一个 API,而不是一个 Web 应用程序。尽管如此,我的 API 包含用于管理和测试 API 功能的默认页面,并且为了渲染这些页面和 .js 文件,我最终实现了一个控制器来返回 .html.js 文件,这是一种非常错误的做法。

在这篇简短的文章中,我将引导您完成如何创建一个 ASP.NET Core Web API 项目,该项目还包括渲染浏览器请求的文件。这里有一些细微差别!

为什么不使用 Swagger?

Swagger 非常适合为端点测试生成 UI。本文的重点是演示如何添加 Web 页面,以满足以下情况:a) 更用户友好,b) 更复杂,例如需要与多个端点交互的管理页面。

为什么不使用 ASP.NET Core Web App?

因为有时您不需要 Razor 项目(VS2019 中的一个选项),也不想要 Model-View-Controller 项目(VS2019 中的另一个选项),更不需要 Blazor(VS2019 中的又一个选项)。您只想拥有一个 API,其中包含一些内置页面,这些页面比 Swagger 功能更强大,但由于您编写的是 API,因此不需要 Razor、Blazor 或 MVC。(就我个人而言,我认为无论复杂程度如何,Web 应用程序都不应该“要求”使用这三个选项之一,但那是我的看法。)

基本设置

所以您已经创建了一个 Web API 项目

并且您有了漂亮的控制器

[ApiController]
[Route("[controller]")]
public class SpiffyController : ControllerBase
{
  [HttpGet]
  public object Get()
  {
    return Content("<b>Hello World</b>", "text/html");
  }
}

然后,按照这个优秀的 StackOverflow 帖子

您在 Configure 方法的 Startup.cs 中添加这两行

app.UseDefaultFiles();
app.UseStaticFiles();
  • UseDefaultFiles设置默认页面为网站提供一个起点。要从 wwwroot 提供默认页面而不使用完全限定的 URI,请调用 UseDefaultFiles 方法-- 提供默认文档
  • UseStaticFiles静态文件存储在项目 Web 根目录中。默认目录是 {content root}/wwwroot -- ASP.NET Core 中的静态文件

创建 wwwroot 文件夹并在其中放入 index.html(以及您在顶层想要的任何其他文件)

本文中我的 index.html 文件如下所示

<!DOCTYPE html>

<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>TypeScript HTML App</title>
</head>
<body>
  <div>
    <button id="btnHi">Say hello</button>
    <div id="response"></div>
    <button id="btnGoodbye">Goodbye</button>
  </div>
</body>
</html>

goodbye.html 文件如下所示

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title></title>
</head>
<body>
  <h2>Goodbye!</h2>
</body>
</html>

此时,运行项目,我们看到

太棒了,我们渲染了 index.html

然后,您创建一个 Scripts 文件夹并添加一个 TypeScript 文件(请注意,我将该文件夹直接放在项目下)

这会给您带来这个消息

所以您这样做,并将 ECMAScript 版本设置为至少 2017

我不想让我的 .js 文件进入同一个 Scripts 文件夹,我只希望它们位于 wwwroot/js

因此,我转到项目属性并将 TypeScript 编译器输出重定向

我看到它起作用了

但是 index.html 不知道如何加载将从 TypeScript 文件编译的 .js 文件。因此,我们在 index.html 中添加了这行

<script src="/js/app.js"></script>

现在,我的演示脚本文件如下所示

window.onload = () => {
  new App().init();
};

class App {
  public init() {
    document.getElementById("btnHi").onclick = () => this.Hi();
    document.getElementById("btnGoodbye").onclick = () => window.open('goodbye.html', '_self');
  }

  private Hi(): void {
    XhrService.get(`${window.location.origin}/Spiffy`)
      .then(xhr => {
        document.getElementById("response").innerHTML = xhr.responseText;
      });
    }
  }

  class XhrService {
    public static async get(url: string, ): Promise<XMLHttpRequest> {
    const xhr = new XMLHttpRequest();

    return new Promise((resolve, reject) => {
      xhr.onreadystatechange = () => {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
            resolve(xhr);
          } else {
            reject(xhr);
          }
        }
      };
  
    xhr.open("GET", url, true);
    xhr.setRequestHeader("Content-Type", "application/json");

    xhr.send();
    });
  }
}

现在,单击按钮,我们看到

使用 Require

开发者将 RequireJS 描述为“JavaScript 文件和模块加载器”。RequireJS 加载纯 JavaScript 文件以及更定义的模块。它针对浏览器使用进行了优化,包括在 Web Worker 中,但它也可以在其他 JavaScript 环境中使用,如 Rhino 和 Node。它实现了异步模块 API。使用模块化脚本加载器,如 RequireJS,将提高代码的速度和质量。另一方面,Webpack 被详细描述为“JavaScript 及其相关文件的打包器”。 -- require vs. webpack

上面的例子很简单,但如果我的 TypeScript 类在单独的文件中怎么办?我倾向于更喜欢使用 require 而不是像 Webpack 这样的打包器,即使只是因为它更容易配置(在我看来)并且我习惯了它。

不要这样做!您将获得所有不需要的东西。这样做

npm install --save @types/requirejs

这仅安装了 required.ts 文件

Scripts 文件夹中创建一个 AppConfig.ts 文件

import { App } from "./App"

require(['App'],
  () => {
    const appMain = new App();
    appMain.run();
  }
);

并将 app.ts 重构为两个文件

App.ts

import { XhrService } from "./XhrService";

export class App {
  public run() {
    document.getElementById("btnHi").onclick = () => this.Hi();
    document.getElementById("btnGoodbye").onclick = () => window.open('goodbye.html', '_self');
  }

  private Hi(): void {
    XhrService.get(`${window.location.origin}/Spiffy`)
      .then(xhr => {
        document.getElementById("response").innerHTML = xhr.responseText;
      });
  }
}

XhrService.ts

export class XhrService {
  public static async get(url: string,): Promise<XMLHttpRequest> {
  const xhr = new XMLHttpRequest();

  return new Promise((resolve, reject) => {
    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4) {
        if (xhr.status >= 200 && xhr.status < 300) {
          resolve(xhr);
        } else {
          reject(xhr);
        }
      }
    };

    xhr.open("GET", url, true);
    xhr.setRequestHeader("Content-Type", "application/json");
    
    xhr.send();
    });
  }
}

运行 Web API 项目,可以使用项目 Debug 属性选项将 Launch Browser 设置为控制器名称

这将命中 API 端点,或者不设置

这将渲染 index.html,其中包含 App.ts 中实现的功能。

完整的项目目录现在看起来像这样

在 Visual Studio 或 Chrome 中进行调试

TypeScript 文件可以在 Visual Studio 中进行调试

不幸的是,因为 TypeScript .ts 文件位于 Scripts 文件夹中,而不是 wwwrooot/js 文件夹中,当您尝试设置断点时,Chrome 会显示此错误

我们可以通过在 Startup.cs 中 *添加* 此行来解决此问题

app.UseStaticFiles(new StaticFileOptions
{
  FileProvider = new PhysicalFileProvider(
  Path.Combine(env.ContentRootPath, "Scripts")),
  RequestPath = "/Scripts"
});

现在 Chrome 可以找到 .ts 文件,您可以在 Chrome 控制台中进行调试

结论

我们现在有一个工作示例

  1. 将 TypeScript 添加到 ASP.NET Core Web App 项目。
  2. 添加 require,以便我们可以引用 TypeScript 文件,并且无需使用打包器。
  3. 提供默认文件,例如 index.js
  4. TypeScript .ts 文件与已编译的 .js 文件分开。
  5. 帮助 Chrome 的调试器找到 .ts 文件。

现在,我可以去修复我在介绍中提到的那个我辉煌的项目了!

历史

  • 2021 年 4 月 10 日:初始版本
© . All rights reserved.