在本教程中,你将创建一个 CoffeeBot 应用程序。该应用程序就像机器人咖啡机的控制器。遗憾的是,它实际上不会为你提供咖啡,但它将演示大量有用的编程技术。该应用程序将有一个 Vue.js 客户端和一个 Spring Boot 资源服务器。它将使用 JHipster 进行引导,节省大量时间并演示一些最佳实践,包括在客户端和服务器中自动配置端到端 JWT 身份验证。你还将使用 Split 来展示如何分别使用 Split 的 Javascript SDK 和 Java SDK 在运行时动态地使用功能标志来管理前端和后端的功能集。
该应用程序本身提供饮料。服务器维护着一份饮料清单,主要是咖啡,附有尺寸、价格和名称。为简单起见,饮料列表将使用内存中的开发数据库进行保存,但如果需要,可以轻松地重新配置以实现持久性。客户端从服务器请求饮料列表,如果用户通过身份验证,则传递任何身份验证数据。
第一个分割点或决策点发生在服务器检索饮料列表以返回给客户端时。想象一下添加了几种新饮料,但配方是实验性的。营销人员希望在将自己的品牌押注于新饮料之前进行一些用户调查。因此,新饮料仅限于一小部分用户,以便在更广泛地发布之前可以对他们进行调查。因此,服务器将用户名传递给 Split 并请求基于特定 split 和用户名的处理或条件状态。根据这个值(在我们的例子中是一个简单的布尔值),服务器要么返回所有饮料(包括实验饮料),要么返回饮料的子集。
客户端接收此列表并将饮料列表显示为按钮列表。第二次分割纯粹与客户有关。添加了一项新功能:能够在饮料中添加奶油。正如你可以想象的那样,考虑到人们对咖啡中奶油的喜爱程度,虚拟骚乱已经开始要求此功能。人们在叫嚷。但经理们希望在广泛发布之前确保奶油功能正常工作(CoffeeBot 有时会失灵)。因此,根据经过身份验证的用户,添加奶油的能力被切换。
Java + Vue.js 教程依赖项
**Java:**我在本教程中使用了 Java 12。你可以访问AdaptOpenJdk 网站下载并安装 Java 。或者你可以使用SDKMAN甚至Homebrew等版本管理器。
**Node:**按照 Node网站上的说明安装 Node 。本教程是使用 Node 12.14.0 编写的。
**JHipster:**一旦安装了 Java 和 Node,就可以安装 JHipster。按照其网站上的说明进行操作(如果出现问题,有助于排除故障)或只需运行此命令:npm install -g generator-jhipster@6.9.0使用npm.
拆分:如果你还没有免费的拆分帐户,请注册一个。这就是实现功能标志的方式。
使用 JHipster 引导你的 Spring Boot 应用程序
要创建示例 CoffeBot 应用程序,你将使用 JHipster。正如其网站上所述,“JHipster 是一个快速生成、开发和部署现代 Web 应用程序和微服务架构的开发平台。” 它允许你快速启动具有各种前端和服务器配置的基于 Java 的项目。本教程将使用 Vue.js。
JHipster 的优点之一是它为你创建了一个包含 Java 服务器应用程序和 Vue.js 前端应用程序的组合项目。它还包括将创建数据模型实体和 Web 服务控制器的生成器。它做了很多事情并创建了很多文件。如果你对这些平台相对陌生,那么项目结构可能看起来有点令人难以承受,但他们在网站上记录了所有内容,做得很好。他们布置的项目遵循当前的最佳实践,因此它是一个很好的学习工具。
打开 shell 并为你的项目创建一个根目录,例如 CoffeeBotApp.
导航到该目录。你将在此处生成项目文件。
通常,当你运行 JHipster 时,它会询问你许多有关你正在引导的项目的问题。但是,为了简化事情,你将使用此.yo-rc.json文件来预先配置项目,从而绕过询问。
在根项目目录中,创建一个.yo-rc.json包含以下内容的文件。此配置的一些亮点是:
**applicationType:**整体应用程序:典型的标准应用程序(本质上不是微服务)
baseName : coffeebot– 应用程序的名称
packageName : com.split.coffeebot– 基础 Java 包
authenticationType : jwt– JSON Web 令牌身份验证
devDatabaseType : h2Memory– 开发数据库使用内存中的 H2 数据库,该数据库不会跨会话持久化
**clientFramework **: vue– 使用Vue.js作为前端客户端框架
SkipFakeData : true– JHipster 默认情况下会为数据模型生成一组随机的假数据,我们希望在本教程中跳过这些数据
有很多选择。请参阅文档以深入了解这一点。
{
"generator-jhipster": {
"promptValues": {
"packageName": "com.split.coffeebot"
},
"jhipsterVersion": "6.9.0",
"applicationType": "monolith",
"baseName": "coffeebot",
"packageName": "com.split.coffeebot",
"packageFolder": "com/split/coffeebot",
"serverPort": "8080",
"authenticationType": "jwt",
"cacheProvider": "ehcache",
"enableHibernateCache": true,
"websocket": false,
"databaseType": "sql",
"devDatabaseType": "h2Memory",
"prodDatabaseType": "mysql",
"searchEngine": false,
"messageBroker": false,
"serviceDiscoveryType": false,
"buildTool": "gradle",
"enableSwaggerCodegen": false,
"jwtSecretKey": "ZDg4ZjkzMDJkNWQ4YWJlMjUxOTY3YjE1MDNjY2ZkMzJjYWQwYjJiOTkyMWQ3YTE5ZTgwNWY3Y2E1ZDg0OWViZjM0Nzg1NDE3MjNlMGY1MDBkNTg4YWU1MmZmNTU1ZGEzOTJiMTVlMWZjZDc5NDUyMTlmZmRmYTU0NDJjMDdiODA=",
"embeddableLaunchScript": false,
"useSass": true,
"clientPackageManager": "npm",
"clientFramework": "vue",
"clientTheme": "none",
"clientThemeVariant": "",
"creationTimestamp": 1601147759112,
"testFrameworks": [],
"jhiPrefix": "jhi",
"entitySuffix": "",
"dtoSuffix": "DTO",
"otherModules": [
{
"name": "generator-jhipster-vuejs",
"version": "1.8.0"
}
],
"enableTranslation": false,
"blueprints": [
{
"name": "generator-jhipster-vuejs",
"version": "1.8.0"
}
],
"skipFakeData": true
}
}
通过运行以下命令(在包含该文件的根项目目录中.yo-rc.json)创建入门应用程序。
jhipster
当 JHipster 创建项目时,你将看到大量控制台输出。它应该以以下行结束。
INFO! Congratulations, JHipster execution is complete!
代码语言: Swift (斯威夫特)
JHipster 已经创建了一个 Git 存储库并进行了初始提交。此时,你可以通过打开两个 shell(一个用于客户端,一个用于服务器)并运行以下命令来运行入门应用程序。
Spring Boot 服务器:
./gradlew
Vue.js 客户端:
npm start
生成 Spring Boot 数据模型
现在你想要使用 JHipster 生成数据模型或实体。它们定义了将存储在数据库中并由 REST 服务提供服务的数据结构。当你使用 JHipster 的生成器创建实体时,JHipster 和 Spring 会为你完成许多出色的幕后工作。它创建表示数据结构的 Java 类,并使用允许将数据保存到数据库的 JPA 注释进行注释。它还创建一个实现创建、读取、更新和删除 (CRUD) 功能的资源文件,该文件会自动受到所选身份验证方案(在我们的示例中为 JWT)的保护。
在前端,生成器创建必要的文件,允许你与实体的资源服务器进行交互,以及前端文件来创建、更新和检查持久数据实体(要访问它,你必须以管理员用户身份登录)。
在项目根目录中,创建一个新文件:entities.jdl. 该文件定义了一种具有四个属性的实体类型,以及该实体中使用的枚举类型。
enum DrinkSize {
Small,
Medium,
Large,
XLarge,
XXLarge
}
entity Drink {
name String required,
size DrinkSize required,
caffeineMilligrams Integer required,
priceDollars Integer required,
}
通过运行生成实体文件:
jhipster import-jdl entities.jdl
当它询问你是否覆盖文件时,只需键入ato overwrite this and all others。
现在是运行入门应用程序并探索引导功能的好时机。请记住,你需要运行两个不同的进程。
** Spring Boot Java 服务器**```
./gradlew
Vue.js 客户端(你可能需要等待一分钟左右服务器才能完成运行):
npm start
客户端应用程序应自动打开。如果没有,请打开 http://localhost:9000
使用默认凭据以管理员用户身份登录admin:admin。查看“管理”菜单下的所有功能。另请查看“实体”菜单。你可以在此处查看、添加和更新你创建的实体。在我们的例子中,这是Drink实体,它有四个属性:name、size、caffeine mgs和Price Dollars。
Vue.js 客户端使用 TypeScript 和类组件,并将模板和组件声明拆分为两个单独的文件。如果你习惯于更传统的.vue单文件结构,一开始这可能看起来有点奇怪,但大多数差异都是不言自明的。如果你需要帮助,请查看文档中的官方页面以获取更多信息。
将功能标志添加到你的 Spring Boot Java 服务器
你将使用 Split 在客户端和服务器上实现功能标志。你应该已经注册了一个免费帐户(如果没有,请立即注册)。目前,你要将 Java Split SDK 集成到 Spring Boot 应用程序中。我将引导你完成此过程,但如果你需要更多信息或遇到麻烦,请查看他们的 Java SDK 文档。
首先,将Split依赖添加到build.gradle文件中(在项目根目录中)。该build.gradle文件包含大量内容。只需在末尾附近添加以下行,就在开始的注释行上方//jhipster-needle-gradle-dependency。
dependencies {
....
compile 'io.split.client:java-client:4.0.1'
//jhipster-needle-gradle-dependency - JHipster will add additional dependencies here
}
你将需要你的 Split API 密钥。打开你的拆分仪表板。通过转到仪表板左上角的方形工作区图标(可能显示默认为DE)找到 API 密钥,单击它,然后单击Admin Settings。单击左侧面板中“工作区设置”下的“API 密钥”。
你将看到已创建四个 API 密钥,其中两个用于生产,两个用于暂存。服务器端 SDK 使用和客户端 Javascript 使用有不同的密钥。SDK和staging-default密钥是你稍后需要的。
将 API 密钥添加到配置文件的末尾application.yml。
src/main/resources/application.yml
#application:
split:
api-key: <yourSplitApiKey>代码语言: 小黄瓜 (gherkin )
我只是指出,在这里你将其添加到全局配置文件中,但在更实际的用例中,你可能会使用两个不同的 API 密钥,一个用于暂存和生产,分别将它们添加到 和application-dev.yml文件application-prod.yml中。
创建一个名为 的 Java 文件,SplitConfig.java该文件将在 Spring Boot 应用程序中配置 Split 客户端。它创建了一个可用于依赖注入的 Spring Bean,并且由于 Bean 的默认行为是创建一个单例实例,因此这与 Split 自己的指导一致,建议只创建一个客户端实例。
src/main/java/com/split/coffeebot/config/SplitConfig.java
@Configurationpublic class SplitConfig {
@Value("#{ @environment['split.api-key'] }")
private String splitApiKey;
@Bean
public SplitClient splitClient() throws Exception {
SplitClientConfig config = SplitClientConfig.builder()
.setBlockUntilReadyTimeout(1000)
.enableDebug()
.build();
SplitFactory splitFactory = SplitFactoryBuilder.build(splitApiKey, config);
SplitClient client = splitFactory.client();
client.blockUntilReady();
return client;
}
}
你还需要向DrinkRepository. 它JPARepository为你提供了相当多的功能,无需任何自定义,但在这个应用程序中,你将需要一个自定义方法,该方法允许你从标准方法中排除一些饮料findAll()。该方法findByNameNotIn()是一个JPA查询方法,其语法和实现由Spring Boot提供。你所要做的就是定义方法以使其可供使用。有关更多信息,请参阅Spring Data JPA 查询方法的文档。
src/main/java/com/split/coffeebot/repository/DrinkRepository.java
@SuppressWarnings("unused") @Repository public interface DrinkRepository extends JpaRepository<Drink, Long> {
List<Drink> findByNameNotIn(Collection<String> names);
}
现在创建一个CoffeeBotResource.java文件,其中包含 CoffeeBot 应用程序的业务逻辑和 REST 端点。
src/main/java/com/split/coffeebot/web/rest/CoffeeBotResource.java
package com.split.coffeebot.web.rest;
…
@RestController
@RequestMapping("/api/coffee-bot")public class CoffeeBotResource {
private final Logger log = LoggerFactory.getLogger(CoffeeBotResource.class);
SplitClient splitClient;
DrinkRepository drinkRepository;
public CoffeeBotResource(SplitClient splitClient, DrinkRepository drinkRepository) {
this.splitClient = splitClient;
this.drinkRepository = drinkRepository;
}
private Drink makeDrink(String name, DrinkSize size, Integer caffeineMg, Integer price) {
Drink drink = new Drink();
drink.setCaffeineMilligrams(caffeineMg);
drink.setName(name);
drink.setSize(size);
drink.setPriceDollars(price);
return drink;
}
@EventListener
public void onApplicationEvent(ContextRefreshedEvent event) {
drinkRepository.save(makeDrink("Water", DrinkSize.Small, 0, 1));
drinkRepository.save(makeDrink("Soda", DrinkSize.Medium, 30, 3));
drinkRepository.save(makeDrink("Coffee", DrinkSize.XLarge, 50, 5));
drinkRepository.save(makeDrink("Coffee", DrinkSize.Small, 30, 3));
drinkRepository.save(makeDrink("Coffee", DrinkSize.Medium, 40, 3));
drinkRepository.save(makeDrink("Latte", DrinkSize.Large, 100, 8));
drinkRepository.save(makeDrink("Latte", DrinkSize.Small, 80, 6));
drinkRepository.save(makeDrink("Latte", DrinkSize.Medium, 60, 5));
}
@GetMapping("/list-drinks")
public List<Drink> listDrinks() {
Optional<String> userName = SecurityUtils.getCurrentUserLogin();
String treatment = splitClient.getTreatment(userName.get(),"drink-types");
if (treatment.equals("on")) {
return drinkRepository.findAll();
}
else {
return drinkRepository.findByNameNotIn(Arrays.asList("Latte", "Soda"));
}
}
}
和方法作为辅助方法,用于在应用程序启动时创建一些示例makeDrink()数据onApplicationEvent()(请记住,它使用的是内存数据库,不会在会话之间保留任何数据)。
该类使用 Spring 的依赖注入来使两个对象可用:DrinkRepository,这是自动创建的接口,定义应用程序如何操作实体(饮料);,SplitClient它是负责与 Split 通信并获取给定密钥和治疗名称的治疗的客户端。
你很快就会创建这种治疗方法。现在,请注意该getTreatment()方法至少需要两个参数。一种是文本键,它是任意字符串值,通常是用户名、帐户 ID 或用于区分用户的另一个唯一键。另一个是分割名称,它指定使用哪种处理来进行分割。
专业提示:可选的第三个getTreatment参数(我们在这里不会使用)是一个属性 映射对象,包含名称-值对中的用户属性。即使是敏感的用户数据也可以在此映射中传递,因为这些数据都不会发送到斯普利特的云。相反,属性映射会在本地内存中与你在拆分 UI 中输入的定位规则进行比较。更多内容请参见 Split SDK 文档:使用属性映射进行自定义定位。
如果治疗是on,它会返回所有可用的饮料。如果处理不是on(off或control或任何其他值),则返回除Latte和之外的所有饮料Soda。这演示了一种基于拆分来分叉代码的简单方法。更复杂的分割和治疗用例是可能的。
在你搬家之前,最后一项改变。打开SecurityConfiguration文件并允许api/coffee-bot资源路径上的所有流量。这将允许匿名用户获得饮料清单。
你要添加这一行:
.antMatchers("/api/coffee-bot/**").permitAll() 代码语言: Bash (bash )
至configure(HttpSecurity http)方法。立场很重要。该行需要添加到该.antMatchers("/api/**").authenticated()行之前。
src/main/java/com/split/coffeebot/config/SecurityConfiguration.java
@Overridepublic void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
...
.and()
.authorizeRequests()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/account/reset-password/init").permitAll()
.antMatchers("/api/account/reset-password/finish").permitAll()
.antMatchers("/api/coffee-bot/**").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/prometheus").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
...
// @formatter:on
}
创建特征标志处理
如果你对治疗和 Split 不熟悉,你可能需要阅读Split 网站上的入门信息。简而言之,分割定义了一个决策点,一个标志,可以在代码中使用它来修改呈现给一组用户的功能。键(以及可选的属性映射)是根据拆分中定义的规则确定标志状态的值。这个决定是在运行时调用该方法时做出的。SplitClientgetTreatment()
在我们非常简单的示例中,你将创建一个名为Drink-types的拆分。这个分割将是一个简单的开/关分割,就像一个布尔标志(除了开和关之外,还可以有多个值)。分割将默认为off,但on如果admin用户在场,则分割将变为 。这是一个非常幼稚的例子。举个例子,在生产中,你可以根据用户群的一部分来定义这种划分,以在细分中推出新功能;或者,你可以先将功能仅提供给公共 Beta 测试人员,然后再将其发布给整个用户群。
打开拆分仪表板。你应该位于默认工作区中。
单击左侧的“拆分” 。
单击蓝色的“创建拆分”按钮。
为分割命名:drink-types。你可以将其余部分留空。
单击创建。
从环境下拉列表中选择暂存-默认。
单击添加规则按钮。
请注意,在定义处理部分下,定义了两个值:on和off。对于我们的用例来说,这非常棒。
在“创建单个目标”下,单击“添加目标”按钮。添加名为 的用户admin。这意味着用户admin将受到治疗on。
单击面板右上角的保存更改。
单击下一个面板上的“确认”以确认更改。
更新 Vue.js 客户端应用程序
客户端代码使用axios向资源服务器发出请求。你还需要安装 Split 模块依赖项。从项目根目录添加依赖项。
npm install --save axios @splitsoftware/splitio@10.14.2
更新home.component.ts文件以创建 CoffeeBot 应用程序。你需要为下面代码中的位置添加Javascript – staging-default键。const SPLIT_AUTH_KEY
src/main/webapp/app/core/home/home.component.ts
import Component from 'vue-class-component';
import { Inject, Vue, Watch } from 'vue-property-decorator';
import LoginService from '@/account/login.service';
import { SplitFactory } from '@splitsoftware/splitio';
import { IClient } from '@splitsoftware/splitio/types/splitio';
import axios from 'axios';const SPLIT_AUTH_KEY = <yourSplitAuthKey>;
@Component
export default class Home extends Vue {
@Inject('loginService')
private loginService: () => LoginService;
private splitClient: IClient = null;
// our list of drinks
private drinks = [];
// holds the drink that is the current order
private currentOrder = null;
// cream or no cream?
private withCream = false;
// the current Split.io treatment
private treatment = null;
public openLogin(): void {
this.loginService().openLogin((<any>this).$root);
}
public get authenticated(): boolean {
return this.$store.getters.authenticated;
}
public get username(): string {
return this.$store.getters.account ? this.$store.getters.account.login : '';
}
async getTreatment() {
// create a configured SplitFactory
const splitFactory = SplitFactory({
core: {
authorizationKey: SPLIT_AUTH_KEY, // your Split.io auth key
key: this.username, // identifier for this treatment (username in this case)
trafficType: 'user'
},
startup: {
readyTimeout: 1.5 // 1.5 sec
}
});
// create the split client (NOT READY TO USE YET)
this.splitClient = splitFactory.client();
// block untli the client is ready
this.splitClient.on(this.splitClient.Event.SDK_READY, function() {
// client is ready, get the treatment
this.treatment = this.splitClient.getTreatment('drink-types');
}.bind(this));
}
// triggered when username changes to update list
// of drinks and Split.io treatment
@Watch('username')
async usernameChanged(newVal: string, oldVal: String) {
// get treatment from split.io
await this.getTreatment();
// call the REST service to load drinks
await this.loadDrinks();
// clear the current order
this.currentOrder = null;
}
async loadDrinks() {
const response = await axios.get('http://localhost:8080/api/coffee-bot/list-drinks');
console.log(response);
if (response && response.status === 200) {
this.drinks = response.data;
}
else {
this.drinks = [];
}
}
async mounted() {
await this.getTreatment();
await this.loadDrinks();
}
beforeDestroy() {
this.splitClient.destroy();
}
}
该组件的身份验证部分是通过 JHipster 引导程序免费提供的。通过该方法从 Spring Boot 资源服务器加载数据loadDrinks(),该方法只是将饮料存储在本地数组中,巧妙地称为drinks. 当安装组件和用户更改时(因为可用的饮料取决于治疗,而治疗由用户决定),则会调用此方法。你可能会注意到此方法没有传递用户名。这是因为用户名会自动通过 JWT(JSON Web 令牌)传递到服务器,由 Spring Security 处理,身份验证代码由 JHipster 引导。
另一个重要的函数是usernameChanged()方法,当属性更改时调用该方法username。每次有新用户时,都需要创建新的 Split 客户端并重新加载处理。你还需要从服务器重新加载饮料。这个方法处理所有这些。
请注意此处的一般流程。首先,SplitFactory使用分割身份验证密钥和新用户名配置 a。该SplitFactory实例用于创建SplitClient实例。然而,客户此时不一定准备好。该代码会阻塞,直到SDK_READY事件被触发(参见下面的代码),然后才尝试从 获取处理SplitClient,否则它将仅返回control处理。
// block until SDK is readythis.splitClient.on(this.splitClient.Event.SDK_READY, function () {
// ready now, so get treatment
this.treatment = this.splitClient.getTreatment('drink-types');
}.bind(this))
home.vue现在更新与该组件对应的模板文件。
src/main/webapp/app/core/home/home.vue
<template>
<div class="container">
<div class="home row justify-content-md-center">
<div class="col-md-6">
<div class="title">
<h1 class="display-4">CoffeeBot!</h1>
<p class="lead">Drink coffee!</p>
<p>{{username ? username : "anonymous"}}, treatment: {{treatment}}</p>
</div>
<div v-for="drink in drinks">
<b-button variant="outline-primary" class="menu-button" @click="currentOrder=drink">{{drink.name}} {{drink.size}} - ${{drink.priceDollars}}</b-button>
</div>
<div v-if="currentOrder !== null">
<div v-if="treatment==='on'">
<b-button v-if="!withCream" variant="success" class="menu-button" @click="withCream=true">Add Cream - FREE</b-button>
<b-button v-if="withCream" variant="danger" class="menu-button" @click="withCream=false">Remove Cream</b-button>
</div>
<div class="card order">
<h4>Current order:</h4>
<div>
{{currentOrder.name}} {{currentOrder.size}} {{withCream ? "(w/ cream)" : ""}} - ${{currentOrder.priceDollars}}
</div>
</div>
</div>
</div>
</div>
</div></template>
<script lang="ts" class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="./home.component.ts"></script>
<style>
.menu-button {
margin-bottom: 10px;
width: 100%;
}
.order {
text-align: center;
padding:20px;
}
.title h1, .title p {
text-align: center;
}</style>
请注意,该文件使用 Vue 的条件语法根据处理状态有条件地渲染添加和删除奶油按钮。在这种情况下,即使组件中有一些与添加和删除奶油功能相关的代码未切换,这就是我们管理功能状态所需要做的全部工作。
<div v-if="treatment==='on'">
<b-button v-if="!withCream" variant="success" class="menu-button" @click="withCream=true">Add Cream - FREE</b-button>
<b-button v-if="withCream" variant="danger" class="menu-button" @click="withCream=false">Remove Cream</b-button></div>
尝试完成的 Spring Boot + Vue.js 教程应用程序
你现在可以尝试完成的应用程序。启动或重新启动服务器和客户端。你可能希望在启动客户端之前让服务器完成启动。
Spring Boot Java 服务器:
./gradlew
Vue.js 客户端:
npm start
客户端应用程序应自动打开。如果没有,请打开 http://localhost:9000
当你第一次加载应用程序时,你会看到用户是anonymous并且治疗是对照治疗。
使用默认管理员凭据 ( ) 登录admin:admin,你将看到扩展的饮料列表。当你添加一个饮料时,你会看到一个名为"Add Cream"的按钮。该按钮的可见性或功能可能受到名为"split treatment"的处理方式的控制。换句话说,根据某种分割处理的规则或逻辑,决定了是否显示或启用这个"Add Cream"按钮。
注销并以默认用户 ( user:user) 身份登录,你将看到处理方式,并且off你将获得与该用户相同的饮料列表anonymous。此外,你无法选择添加奶油。
了解有关 Spring Boot、功能标志和生产中测试的更多信息
在本教程中,你创建了一个全栈应用程序,其中包括 Vue.js 客户端和 Spring Boot 资源服务器。Vue.js 客户端使用 TypeScript 进行更现代、无错误的开发(因为如果使用得当,类型检查可以大大减少运行时错误)。Spring Boot 服务器使用 Spring Security 和 Spring JPA 等技术来快速轻松地定义数据模型(或实体),将该实体的实例保存到数据库,并在 REST 接口中提供该数据。
客户端和服务器都使用 JWT 身份验证来保护安全。Split用于实现功能标志,在服务器端使用Java SDK实现拆分,在客户端使用Javascript SDK实现拆分。
所有这一切都是使用 JHipster 引导的,这使得使用现代最佳实践和技术启动新的全栈项目变得非常容易。
你可以在Split 的示例 GitHub上找到所有设置的 JHipster 的完整源代码。
共同学习,写下你的评论
评论加载中...
作者其他优质文章