elsa core—3.elsa 服务
在本快速入门中,我们将介绍一个用于设置Elsa Server的最小ASP.NET Core应用程序。我们还将安装一些更常用的activities(活动),如Timer、Cron和sendmail,以能够实现简单的重复工作流。
此应用程序的目的是作为工作流服务。这意味着它将承载和执行工作流,但不会承载dashboard(仪表板) UI。当然,也可以将工作流主机与承载dashboard UI的web应用程序相结合,将在随后的文章中展示。 |
我们将:
- 创建ASP.NET Core应用程序。
- 使用EF Core和SQLite提供配置持久性。
- 注册各种activities (活动)以在工作流中使用。
- 使用workflow Builder API创建简单的工作流。
- 公开Elsa API Endpoints供外部应用程序(包括Elsa Dashboard)使用。
- 使用Postman验证Elsa API Endpoints。
项目:
创建一个新的空ASP.NET核心项目,名为ElsaQuickstarts.Server.apidemps:
dotnet new web -n "ElsaQuickstarts.Server.ApiEndpoints"
CD 到创建的项目文件夹中:
cd ElsaQuickstarts.Server.ApiEndpoints
cd ElsaQuickstarts.Server.ApiEndpoints
添加以下包:
dotnet add package Elsa
dotnet add package Elsa.Activities.Http
dotnet add package Elsa.Activities.Temporal.Quartz
dotnet add package Elsa.Persistence.EntityFramework.Sqlite
dotnet add package Elsa.Server.Api
Heartbeat(心跳)工作流
仅出于演示目的,我们将创建一个简单的“心跳”工作流,它会定期将当前时间写入标准。这将展示以下内容:
- Elsa 支持程序化和动态工作流(例如,使用 Elsa Dashboard 可视化创建)。
- 如果我们有 Elsa Dashboard 设置,我们可以直观地查看这个 Heartbeat 工作流程,即使我们要在这以编程方式创建它。
- 我们可以使用 Postman 与 Elsa API endpoints交互并查询 Heartbeat 工作流生成的工作流实例。
继续创建一个名为 HeartbeatWorkflow.cs 的新文件并添加以下代码:
using Elsa.Activities.Console;
using Elsa.Activities.Temporal;
using Elsa.Builders;
using NodaTime;
namespace ElsaQuickstarts.Server.ApiEndpoints
{
public class HeartbeatWorkflow : IWorkflow
{
private readonly IClock _clock;
public HeartbeatWorkflow(IClock clock) => _clock = clock;
public void Build(IWorkflowBuilder builder) =>
builder
.Timer(Duration.FromSeconds(10))
.WriteLine(context => $"Heartbeat at {_clock.GetCurrentInstant()}");
}
}
上述工作流有两个活动。第一个活动 Timer 将导致此工作流每 10 秒执行一次。第二个活动 WriteLine 将当前时间写入标准输出。请注意它采用字符串委托的重载。这会允许在运行时提供动态的属性值。类似与在Elsa 1工作流中使用 JavaScript 和 Liquid 表达式。当然,不同之处在于您现在可以在使用 Workflow Builder API 编写工作流时使用普通的旧 C# 语法。
另请注意,HeartbeatWorkflow 类可以接受构造函数注入的服务,就像向 DI 系统注册的任何其他类型一样。
IClock :它是由 NodaTime 提供的抽象,它是 .NET 的替代日期和时间的 API。 |
Startup
接下来,打开 Startup.cs 并将其内容替换为以下内容:
using Elsa;
using Elsa.Persistence.EntityFramework.Core.Extensions;
using Elsa.Persistence.EntityFramework.Sqlite;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace ElsaQuickstarts.Server.ApiEndpoints
{
public class Startup
{
public Startup(IWebHostEnvironment environment, IConfiguration configuration)
{
Environment = environment;
Configuration = configuration;
}
private IWebHostEnvironment Environment { get; }
private IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
var elsaSection = Configuration.GetSection("Elsa");
// Elsa services.
services
.AddElsa(elsa => elsa
.UseEntityFrameworkPersistence(ef => ef.UseSqlite())
.AddConsoleActivities()
.AddHttpActivities(elsaSection.GetSection("Server").Bind)
.AddQuartzTemporalActivities()
.AddJavaScriptActivities()
.AddWorkflowsFrom<Startup>()
);
// Elsa API endpoints.
services.AddElsaApiEndpoints();
// Allow arbitrary client browser apps to access the API.
// In a production environment, make sure to allow only origins you trust.
services.AddCors(cors => cors.AddDefaultPolicy(policy => policy
.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
.WithExposedHeaders("Content-Disposition"))
);
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app
.UseCors()
.UseHttpActivities()
.UseRouting()
.UseEndpoints(endpoints =>
{
// Elsa API Endpoints are implemented as regular ASP.NET Core API controllers.
endpoints.MapControllers();
});
}
}
}
使用 Entity Framework Core 时,Elsa 将默认使用合并数据库上下文,并将自动为您运行迁移。如果您不想使用池化数据库上下文,请改用 UseNonPooledEntityFrameworkPersistence 方法。如果您更喜欢自己运行迁移,请确保在使用 UseEntityFrameworkPersistence 方法时传递 autoRunMigrations: false (它是一个默认设置为 true 的可选参数)。 |
请注意,我们正在访问名为“Elsa”的配置部分。然后我们使用此部分来检索名为“Http”和“Smtp”的子部分。接下来让我们用这些部分更新 appsettings.json:
Appsettings.json
打开 appsettings.json 并添加以下部分:
{
"Elsa": {
"Http": {
"BaseUrl": "https://localhost:5001"
}
}
}
我们设置“BaseUrl”的原因是 HTTP 活动库提供了一个绝对 URL 提供程序,可供活动和工作流表达式使用。由于这个绝对 URL 提供者可以在实际 HTTP 请求的上下文之外使用(例如,当一个计时器事件发生时),我们不能依赖例如IHttpContextAccessor,因为不会有任何 HTTP 上下文。
运行
运行程序并等待,直到看到以下输出:
Now listening on: http://localhost:5000
Now listening on: https://localhost:5001
Application started. Press Ctrl+C to shut down.
等待大约 10 秒后,可以看到以下输出:
info: Elsa.Bookmarks.BookmarkIndexer[0]
Indexed 0 bookmarks in 00:00:00.0077348
Heartbeat at 2021-05-07T19:43:47Z
info: Elsa.Bookmarks.BookmarkIndexer[0]
Indexing bookmarks
Postman
启动 Postman 或任何其他HTTP 请求的工具。接下来将尝试一些公开的 API。
列出工作流蓝图
首先,让我们查询工作流注册表:
GET /v1/workflow-registry
Host: localhost:5001
JSON 响应将包含所有已注册工作流的“summary(摘要)”视图(目前只有一个):
{
"items": [
{
"id": "HeartbeatWorkflow",
"name": "HeartbeatWorkflow",
"displayName": "HeartbeatWorkflow",
"description": null,
"version": 1,
"tenantId": null,
"isSingleton": false,
"isEnabled": false,
"isPublished": true,
"isLatest": true
}
],
"page": null,
"pageSize": null,
"totalCount": 1
}
获取单一工作流蓝图
要获取完整的工作流蓝图定义,请发出以下 HTTP 请求:
GET /v1/workflow-registry/HeartbeatWorkflow
Host: localhost:5001
响应将包括更多详细信息,包括activities (活动)和connections(联系):
{
"$id": "1",
"version": 1,
"isSingleton": false,
"isEnabled": false,
"isPublished": true,
"isLatest": true,
"variables": {
"$id": "2",
"data": {}
},
"persistenceBehavior": "WorkflowBurst",
"deleteCompletedInstances": false,
"customAttributes": {
"$id": "3",
"data": {}
},
"activities": [
{
"$id": "4",
"id": "activity-1",
"type": "Timer",
"parentId": "HeartbeatWorkflow",
"persistWorkflow": false,
"loadWorkflowContext": false,
"saveWorkflowContext": false,
"properties": {
"$id": "5",
"data": {
"Timeout": "0:00:10"
}
}
},
{
"$id": "6",
"id": "activity-2",
"type": "WriteLine",
"parentId": "HeartbeatWorkflow",
"persistWorkflow": false,
"loadWorkflowContext": false,
"saveWorkflowContext": false,
"properties": {
"$id": "7",
"data": {
"Text": "Heartbeat at 2021-05-07T19:58:22Z"
}
}
}
],
"connections": [
{
"$id": "8",
"sourceActivityId": "activity-1",
"targetActivityId": "activity-2",
"outcome": "Done"
}
],
"id": "HeartbeatWorkflow",
"name": "HeartbeatWorkflow",
"displayName": "HeartbeatWorkflow",
"type": "HeartbeatWorkflow",
"parentId": "HeartbeatWorkflow",
"persistWorkflow": true,
"loadWorkflowContext": false,
"saveWorkflowContext": false,
"properties": {
"$id": "9",
"data": {}
}
}
列出工作流实例
随着 HeartbeatWorkflow 的执行,每 10 秒将创建一个新的工作流实例。要获取工作流实例列表,请发出以下请求:
GET /v1/workflow-instances?workflow=HeartbeatWorkflow&page=0&pageSize=2
Host: localhost:5001
响应应类似于以下内容:
{
"items": [
{
"id": "e380d0a7fd4a4b6ba236fbdc0adf0ddb",
"definitionId": "HeartbeatWorkflow",
"tenantId": null,
"version": 1,
"workflowStatus": "Finished",
"correlationId": null,
"contextType": null,
"contextId": null,
"name": null,
"createdAt": "2021-05-07T19:43:46.4198083Z",
"lastExecutedAt": "2021-05-07T19:43:47.4602325Z",
"finishedAt": "2021-05-07T19:43:47.4626325Z",
"cancelledAt": null,
"faultedAt": null
},
{
"id": "418d0b535a89413e9ca2014a3b476b93",
"definitionId": "HeartbeatWorkflow",
"tenantId": null,
"version": 1,
"workflowStatus": "Finished",
"correlationId": null,
"contextType": null,
"contextId": null,
"name": null,
"createdAt": "2021-05-07T19:43:56.175008Z",
"lastExecutedAt": "2021-05-07T19:43:56.3284055Z",
"finishedAt": "2021-05-07T19:43:56.3285439Z",
"cancelledAt": null,
"faultedAt": null
}
],
"page": 0,
"pageSize": 2,
"totalCount": 110
}
列出活动
要获取可用活动的完整列表,您可以发出以下 HTTP 请求:
GET /v1/activities
Host: localhost:5001
以下是部分响应,让您了解它的样子
[
{
"type": "ReadLine",
"displayName": "Read Line",
"description": "Read text from standard in.",
"category": "Console",
"traits": 1,
"outcomes": [
"Done"
],
"properties": []
},
{
"type": "WriteLine",
"displayName": "Write Line",
"description": "Write text to standard out.",
"category": "Console",
"traits": 1,
"outcomes": [
"Done"
],
"properties": [
{
"name": "Text",
"type": "System.String",
"uiHint": "single-line",
"label": "Text",
"hint": "The text to write.",
"supportedSyntaxes": [
"JavaScript",
"Liquid"
]
}
]
},
{
"type": "HttpEndpoint",
"displayName": "HTTP Endpoint",
"description": "Handle an incoming HTTP request.",
"category": "HTTP",
"traits": 2,
"outcomes": [
"Done"
],
"properties": [
{
"name": "Path",
"type": "Microsoft.AspNetCore.Http.PathString",
"uiHint": "single-line",
"label": "Path",
"hint": "The relative path that triggers this activity.",
"supportedSyntaxes": [
"JavaScript",
"Liquid"
]
},
{
"name": "Methods",
"type": "System.Collections.Generic.HashSet`1[System.String]",
"uiHint": "check-list",
"label": "Methods",
"hint": "The HTTP methods that trigger this activity.",
"options": [
"GET",
"POST",
"PUT",
"DELETE",
"PATCH",
"OPTIONS",
"HEAD"
],
"defaultValue": [
"GET"
],
"defaultSyntax": "Json",
"supportedSyntaxes": [
"Json",
"JavaScript",
"Liquid"
]
},
{
"name": "ReadContent",
"type": "System.Boolean",
"uiHint": "checkbox",
"label": "Read Content",
"hint": "A value indicating whether the HTTP request content body should be read and stored as part of the HTTP request model. The stored format depends on the content-type header.",
"supportedSyntaxes": [
"Literal",
"JavaScript",
"Liquid"
]
},
{
"name": "TargetType",
"type": "System.Type",
"uiHint": "single-line",
"label": "Target Type",
"category": "Advanced",
"supportedSyntaxes": []
}
]
}
]:
elsa core—3.elsa 服务的更多相关文章
- 手把手教你使用spring cloud+dotnet core搭建微服务架构:服务治理(-)
背景 公司去年开始使用dotnet core开发项目.公司的总体架构采用的是微服务,那时候由于对微服务的理解并不是太深,加上各种组件的不成熟,只是把项目的各个功能通过业务层面拆分,然后通过nginx代 ...
- spring cloud+dotnet core搭建微服务架构:服务发现(二)
前言 上篇文章实际上只讲了服务治理中的服务注册,服务与服务之间如何调用呢?传统的方式,服务A调用服务B,那么服务A访问的是服务B的负载均衡地址,通过负载均衡来指向到服务B的真实地址,上篇文章已经说了这 ...
- spring cloud+dotnet core搭建微服务架构:Api网关(三)
前言 国庆假期,一直没有时间更新. 根据群里面的同学的提问,强烈推荐大家先熟悉下spring cloud.文章下面有纯洁大神的spring cloud系列. 上一章最后说了,因为服务是不对外暴露的,所 ...
- spring cloud+dotnet core搭建微服务架构:配置中心(四)
前言 我们项目中有很多需要配置的地方,最常见的就是各种服务URL地址,这些地址针对不同的运行环境还不一样,不管和打包还是部署都麻烦,需要非常的小心.一般配置都是存储到配置文件里面,不管多小的配置变动, ...
- spring cloud+dotnet core搭建微服务架构:配置中心续(五)
前言 上一章最后讲了,更新配置以后需要重启客户端才能生效,这在实际的场景中是不可取的.由于目前Steeltoe配置的重载只能由客户端发起,没有实现处理程序侦听服务器更改事件,所以还没办法实现彻底实现这 ...
- spring cloud+dotnet core搭建微服务架构:Api授权认证(六)
前言 这篇文章拖太久了,因为最近实在太忙了,加上这篇文章也非常长,所以花了不少时间,给大家说句抱歉.好,进入正题.目前的项目基本都是前后端分离了,前端分Web,Ios,Android...,后端也基本 ...
- 依赖注入[8]: .NET Core DI框架[服务消费]
包含服务注册信息的IServiceCollection对象最终被用来创建作为DI容器的IServiceProvider对象.当需要消费某个服务实例的时候,我们只需要指定服务类型调用IServicePr ...
- 依赖注入[7]: .NET Core DI框架[服务注册]
包含服务注册信息的IServiceCollection对象最终被用来创建作为DI容器的IServiceProvider对象.服务注册就是创建出现相应的ServiceDescriptor对象并将其添加到 ...
- 【视频】使用ASP.NET Core开发GraphQL服务
GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时. GraphQL来自Facebook,它于2012年开始开发,2015年开源. GraphQL与编程语言无关,可以使用很 ...
随机推荐
- JDK 和 CGLib 实现动态代理和区别
JDK 和 CGLib 实现动态代理和区别 在日常的开发中,Spring AOP 是一个非常常用的功能.谈到 AOP,自然离不开动态代理. 那么,基于 JDK 和 CGLib 如何实现动态代理,他们之 ...
- create-react-app 创建的项目执行npm run eject后,运行报错
create-react-app 创建的项目执行npm run eject后,运行报错:Cannot find module '@babel/plugin-transform-react-jsx-so ...
- 双击映射坚果云网盘并打开的AHK源代码
双击映射坚果云网盘并打开的AHK源代码 #SingleInstance,force ;当此脚本已经运行时自动替换旧实例再次运行.#Persistent ;让脚本持久运行(即直到用户关闭或遇到 Exit ...
- React Hooks的理解
一.是什么 Hook 是 React 16.8 的新增特性.它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性 至于为什么引入hook,官方给出的动机是解决长时间使 ...
- 【LeetCode】39. 组合总和
39. 组合总和 知识点:递归:回溯:组合:剪枝 题目描述 给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 ...
- python 将Mnist数据集转为jpg,并按比例/标签拆分为多个子数据集
现有条件:Mnist数据集,下载地址:跳转 下载后的四个.gz文件解压后放到同一个文件夹下,如:/raw Step 1:将Mnist数据集转为jpg图片(代码来自这篇博客) 1 import os 2 ...
- JAVA基础语法:java编程规范和常用数据类型(转载)
JAVA基础语法:java编程规范和常用数据类型 摘要 本文主要介绍了最基本的java程序规则,和常用数据类型,其中侧重说了数组的一些操作. 面向java编程 java是纯面向对象语言,所有的程序都要 ...
- Apache虚拟web主机构建
目录 一.构建虚拟web主机 1.1.虚拟web主机概述 二.搭建虚拟web主机步骤 2.1.基于域名搭建虚拟主机 ①为虚拟主机提供域名解析 ②为虚拟主机准备网页文档 ③添加虚拟主机配置 ④设置访问路 ...
- ACM学习笔记:二叉堆
title : 堆 date : 2021-8-3 tags : ACM,数据结构 什么是堆 堆是一棵具有特定性质的二叉树,堆的基本要求是堆中所有结点的值必须大于等于(或小于等于)其孩子结点的值,这也 ...
- QT从入门到入土(八)——项目打包和发布
引言 新手上路可谓是困难重重,你永远不知道下一个困难会在什么时候出现,在完成了运动控制卡封装发布过程中可谓是举步维艰.因此记录一下qt5+vs2019的打包发布方法. 打包一般分为两步: 将编译后的e ...