Skip to content

拡張ライブラリ ​

第6版作成 最終更新 (日本時間)
確認バージョン1.5.8.1

プリザンターには、起動時に外部の DLL を読み込む「拡張ライブラリ」機能があり、任意の Web API や Web ページを追加できます。このページでは、クラスライブラリで API コントローラを作り、プリザンターに組み込む手順と、プリザンターの認証を利用する方法を説明します。

読み込みの仕組み ​

  • 起動時の処理は Startup.cs にあり、GetExtendedLibraryPaths で対象パスを取得しています(1.5.8.1 のソースで確認)。
  • 対象は Assembly.GetEntryAssembly().Location のフォルダ(Implem.Pleasanter.dll があるフォルダ)直下の ExtendedLibraries と、その1 階層下のサブフォルダです(該当コード)。2 階層以上深いフォルダの DLL は読み込まれません。
  • 各フォルダの *.dll をまとめて読み込み、MvcBuilder に AddApplicationPart で登録します。
  • DLL の中に Implem.Pleasanter.NetCore.ExtendedLibrary.ExtendedLibrary クラスがあれば、その Initialize メソッド(引数なしの static メソッド)も呼び出します。

図を読み込み中…

実装上の決まりごと ​

インターフェースの実装などは求められず、次の規約だけで動きます。

要素必須内容
ExtendedLibrary クラス任意無くても読み込みは成功する
Initialize()任意ExtendedLibrary クラスがあるときだけ呼ばれる
コントローラ任意ControllerBase を継承したクラスがあれば MVC に登録される

読み込みで注意すること ​

  • 読み込みに try-catch がありません。Assembly.LoadFrom や Initialize で例外が出ると、プリザンターの起動そのものが止まります。本番に置く前にテスト環境で起動を確かめてください。
  • DLL を読むのは起動時だけです。差し替えたら必ずプリザンターを再起動します。
  • 独自の AssemblyLoadContext は使わず、本体と同じ既定のコンテキストに読み込みます。サブフォルダに分けても、アセンブリが分離されるわけではありません。
  • 本体が既に読み込んでいるのと同じ名前の DLL(Newtonsoft.Json.dll など)を一緒に置いても、先に読み込まれた本体側が使われます。バージョンが違う DLL を置いて新しいバージョンにしかない API を使うと、実行時に MissingMethodException や TypeLoadException になり得ます。ExtendedLibraries には自作の DLL だけを置き、本体が使っているライブラリは含めないでください。

URL(ルーティング) ​

AddApplicationPart で登録されたコントローラは、本体と同じルーティングに乗ります。

  • [Route] 属性を付けたコントローラ(下の手順の api/[controller] など)は属性ルーティングで、その URL になります。
  • 属性を付けない MVC コントローラは、本体の Startup.Configure の規約ルート({controller}/{action} など)で呼ばれます(Startup.cs)。CustomPageController の Index なら /CustomPage/Index です。

手順 ​

1. プロジェクトを作成する ​

プリザンターは 1.5.0.0 から .NET 10 になっているため、.NET 10 のクラスライブラリを作ります。

bash
dotnet new classlib -n MyExtendedLibrary -f net10.0

2. ASP.NET Core への参照を追加する ​

xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>disable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <!-- ASP.NET Core MVC参照 -->
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
  </ItemGroup>
</Project>

3. API コントローラを作成する ​

Hello と Echo を返すだけの例です。

csharp
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace MyExtendedLibrary.Controllers
{
    [AllowAnonymous]
    [ApiController]
    [Route("api/[controller]")]
    public class CustomApiController : ControllerBase
    {
        [HttpGet("hello")]
        public IActionResult Hello()
        {
            return Ok(new { message = "Hello from Extended Library!" });
        }

        [HttpPost("echo")]
        public IActionResult Echo([FromBody] object data)
        {
            return Ok(new { echo = data });
        }
    }
}

4. DLL を配置する ​

ビルドした DLL を次のどちらかに置きます。ライブラリごとに置き場所を分けられるので、サブディレクトリに置く方法がおすすめです。

配置場所パス
基本{プリザンターのフォルダ}/ExtendedLibraries/
サブディレクトリ{プリザンターのフォルダ}/ExtendedLibraries/MyPlugin/

{プリザンターのフォルダ} は Implem.Pleasanter.dll があるフォルダです(静的ファイルを置く wwwroot フォルダではありません)。

5. 動作を確認する ​

/api/CustomApi/hello に GET でアクセスすると、Hello from Extended Library! が返ります。

初期化処理(Initialize) ​

起動時に初期化処理を行いたい場合は、次の名前空間・クラス名で Initialize を用意します。

csharp
namespace Implem.Pleasanter.NetCore.ExtendedLibrary
{
    public static class ExtendedLibrary
    {
        public static void Initialize()
        {
            // 拡張DLL起動時の初期化処理
            // バックグラウンドワーカーの起動など
            Console.WriteLine("ExtendedLibrary initialized!");
        }
    }
}

ただし名前空間が固定されているのはかなり面倒な制約になるため、実際に使う場面は多くありません。Initialize を使わないなら、名前空間は自由です。

プリザンターの認証を使う ​

プリザンターの Context を使うと、呼び出し元がプリザンターに登録済みのユーザかどうかを確認できます。Implem.Pleasanter をプロジェクト参照に追加して使います。認証の仕組み全体は 認証基盤(Cookie・API キー・Bearer) にまとめています。

本体をプロジェクト参照するときは、Private="false" と ExcludeAssets="runtime" を付けて、本体とその依存 DLL をビルド出力にコピーしないようにします。出力フォルダから ExtendedLibraries に置くのは自作の DLL(と、デバッグするなら pdb)だけです。

xml
<ItemGroup>
  <ProjectReference
    Include="..\Implem.Pleasanter\Implem.Pleasanter.csproj"
    Private="false"
    ExcludeAssets="runtime" />
</ItemGroup>

ビルド後の自動コピーやデバッガの付け方は 拡張ライブラリの開発・デバッグ にまとめています。

拡張ライブラリのコントローラに掛かるフィルタ ​

拡張ライブラリのコントローラも本体と同じ MVC に組み込まれる(AddApplicationPart)ため、本体のグローバルフィルタがそのまま掛かります(Startup.cs)。

フィルタ掛かり方内容
AuthorizeFilter(RequireAuthenticatedUser)全コントローラログイン(Cookie 認証)していないと拒否。[AllowAnonymous] を付けると外れる
CheckContextAttributes全コントローラIP 制限(/api/ 以外)、テナントの IP 制限・契約期限、CSRF トークン(TokenCheck 有効時のフォーム送信)、Users に居ないユーザーのサインアウトなど
CheckApiContextAttributes属性を付けたコントローラだけ本文の読み取り、IP 制限、JSON 形式の確認、Cookie 認証時の CSRF トークン

API キーだけで呼ばれる API(Cookie が無い)を作るときは、上の手順の例のように [AllowAnonymous] を付けないとグローバルの AuthorizeFilter で拒否されます。本体の API コントローラと同じく [AllowAnonymous] と [CheckApiContextAttributes](名前空間 Implem.PleasanterFilters)を付け、アクションの中で context.Authenticated を確かめる形にすると、IP 制限なども本体の API と揃います。

ログイン済みのブラウザから呼ばれる MVC コントローラでは、引数なしで new Context() を生成するだけです。Cookie のログイン ID で Users テーブルが検索され、context.Authenticated・context.UserId・context.TenantId・context.DeptId・context.HasPrivilege などが設定されます。

csharp
public class CustomPageController : Controller
{
    [HttpGet]
    public ActionResult Index()
    {
        var context = new Context();
        if (!context.Authenticated)
        {
            return StatusCode(403);
        }
        return Content($"Hello, {context.User.Name}", "text/plain");
    }
}

context.IsAuthenticated(Cookie が認証済みか)と context.Authenticated(Users に有効なユーザーが居るか)は別のプロパティです。判定には Authenticated を使います。

API キーで使う ​

API キーで認証するときは、次のように Context を生成します。

csharp
//リクエストでも埋込でも
var apiKey = "外部から与えられたAPIキー";
//Implem.Pleasanter.Libraries.Requests.Apiにデシリアライズ可能であること
var req = JsonSerializer.Serialize(new { ApiKey = apiKey });

var context = new Context(
    sessionStatus: false,
    sessionData: false,
    apiRequestBody: req,
    contentType: "application/json",
    api: true);

context.Authenticated が true なら認証済みで、context.UserId や context.TenantId などのユーザ情報も取得できます。詳細は Context.cs を参照してください。

  • context.LoginId は Cookie(HttpContext.User)から取り出す値で、API キーで認証したときは設定されません。API キーのユーザーのログイン ID は context.User.LoginId で取れます(SetUser)。
  • 本文に ApiKey があればそれを優先し、無ければ Cookie のログイン ID でユーザーを決めます。
  • 無効化(Disabled)・ロックアウト中のユーザーは、API キーが正しくても Authenticated = false です。
  • ユーザーの「API の利用を許可」などの API 利用可否は、Context の生成時には確かめません。本体の API では各バリデータが確かめているので、必要なら拡張ライブラリ側でも context.UserSettings?.AllowApi(context: context) などで確認してください。
  • multipart/form-data で受ける場合は、本体のファイルアップロード API と同じく Authorization: Bearer <API キー> ヘッダーから API キーを取り出し、new { ApiKey = ... } の JSON にして apiRequestBody に渡す方法があります(BinariesController.cs)。

ライセンス

プリザンター Community Edition のライセンスは AGPL です。作成したライブラリも同じ条件で配布する必要があります。

活用例 ​

カレンダーに iCal のインターフェースを追加したり、Webhook を受けるインターフェースを追加したりして、外部システムとの接続性を広げられます。ログインなしで見られるカレンダーを拡張ライブラリで作る設計は 拡張ライブラリで外部公開カレンダーを作る にまとめています。

関連ページ ​

変更履歴

第6版拡張ライブラリの読み込みと開発・デバッグ、拡張ヘッドリンク、SMTP の OAuth 送信の解説と、多言語・外部公開カレンダー・スレッド型サイトなどの改修・設計メモを追加
第5版認証方式(2 段階認証・パスキー・LDAP・フォールバック)と認証基盤の解説、関連する改修・設計メモを追加
第4版本文から元記事や以前の版への言及を除き、正しい動作だけを書く形に整理
第3版「拡張機能」「API」を 1.5.8.1 のソースで検証して修正
第2版記事のファイル名に並び順の番号を付け、元記事リンクを frontmatter の sources に移行
第1版「拡張機能」「API」セクションの記事を追加