Headless CMS 的内部
目录
介绍
Headless CMS
什么是Headless CMS?
Headless CMS的优点
Headless CMS解决方案的局限性
使用HCMS的缺点
HCMS的局限性
何时何地使用Headless CMS?
RawCMS:构建自己的Headless CMS
为什么另一个Headless CMS?
RawCms特征选择
架构
服务层
认证
Lambda表达式
使用lambda添加自定义端点
验证数据
更改保存数据
插件
如何使用RawCMS
从Docker安装
从Zip Release安装
建立你自己的
兴趣点
- 下载代码(来自GitHub)
- 见文档
- 从Docker Hub拉出
介绍
在本文中,我们将了解Headless CMS,我们将了解它的优点以及何时使用方便。此外,我们将列举实际的主要限制。为了更好地理解HCMS如何在幕后工作,我将解释如何设计和构建RawCMS,一个带有Oauth2的Aspnet.Core Headless CMS,扩展插件系统,业务逻辑支持。该解决方案可在GitHub上获得,并作为演示版在docker hub上发布。
Headless CMS
什么是Headless CMS?
传统的CMS结合了内容和渲染部分,同时,Headless CMS仅关注内容。这似乎是一种限制,因为勉强说,你失去了一些东西。HCMS的目的是将逻辑与内容分离,从而实现简单的变更管理,并在许多组件中分解复杂的应用程序,每个组件都有其单一的责任。
朝着这个方向前进,HCMS可以取代实际上你正在调用的后端,并节省了许多创建CRUD语句的有用工作。
HCMS诞生于创建多组件应用程序,您可以快速更改表示逻辑和设计,这是一个很大的改进,当您在现代网站或应用程序上工作时,由于业务需求,您需要每年更换一次UI 。
许多供应商出售他们的产品并将其标记为“HCMS”仅仅是因为它是分离的(并且因为它听起来很酷并且可能推动销售改进)。在我看来,我与原始的整体定义有着严格的联系:Headless cms意味着API首先是非单片CMS,完全与接口或其他组件分离。
Headless CMS的优点
为什么要使用Headless CMS?我可以简单地说,在某些情况下,解耦系统,更容易更换前端并加快开发阶段是有用的,但我觉得有必要使用无序列表更好地解释。
- 全渠道准备:在Headless CMS中创建的内容是“纯粹的”,您可以在您想要的每个上下文中使用。如果您在其上存储了一些新闻内容,您也可以在公共网站或内部网上发布,将数据输入到一个地方。
- 低运营成本:Headless CMS是产品,所以,一旦你选择了一个好的产品,我预计它将是即插即用的。此外,与自定义解决方案相比,更新和错误修复来自供应商的免费提供。
- 缩短产品上市时间:Headless CMS促进了敏捷的工作方式。您可以让多个团队参与后端和前端,这样可以减少时间。此外,由于HCMS区域是API消耗的数据存储的垂直解决方案,大部分事情已经完成,因此您必须专注于数据设计而不是技术细节(例如浪费时间考虑有效载荷,何时可以免费的使用Odata或Grahql)。
- 垂直解决方案:HCMS做一件事。这使得学习和维护变得非常容易。
- 灵活性:一旦你选择了你的HCMS(无论是本地还是云端),你的开发人员都可以使用他们喜欢的任何语言来实现前端。这意味着您可以自由地使用技术限制。
Headless CMS解决方案的局限性
与传统的CMS相比,HCMS相当年轻,因此,即使很多产品在过去几年诞生,大多数产品也不是那么成熟,无法完全取代传统的API后端。在这个阶段中,我将分享我对我发现的限制的经验。功能可能会因特定产品而异,如果是本地或saas解决方案。
实际上,主要有两种CMS Headless限制:
- 使用HCMS的缺点
- 您安装的产品的限制
使用HCMS的缺点
HCMS需要雇佣多个团队来实现工作并行化的好处。此外,由于HCMS没有任何渲染,所有的表示逻辑都被要求提供给客户端。这对于解耦很有用,但在所有情况下,您只有一个消费者解耦优势并不那么相关,并且您在数据获取过程中引入了更多的复杂性和延迟。另一个问题是关于业务逻辑。在哪里实施?如果你不想实现HCMS,你必须把它放到表示层,并且有多个消费者,当逻辑存在于多个地方时,您将复制它,陷入问题中。否则,尝试将其放入HMS,您会发现大多数云解决方案\产品都不那么灵活。这引入了下一个主题,所有HCMS的限制是什么?
HCMS的局限性
测试最重要的HCMS解决方案,我遇到了许多困难的情况,以下是最常见的限制列表。考虑到这取决于产品,有人可能有或没有,但一般来说,大多数都很常见。
- 针对外部提供程序的身份验证:大多数解决方案不允许针对外部系统对用户进行身份验。我说的是最常见的情况,即您拥有一个中央身份验证系统,并且所有各方都会传递用户令牌\票证以代表用户进行操作。换句话说,如果我有一个oauth2服务器,我想在前端进行身份验证,并使用令牌向内部网的所有应用程序进行调用,而不仅仅是HCMS,并被识别为我自己。
- 非标准输出格式:有些使用graphql或Odata,这很好,因为它为数据消耗提供了标准方法。问题是“某些”并不意味着“全部”,所以你必须注意选择你的HCMS。
- 业务逻辑:在大多数情况下,不可能在运行时定义业务逻辑,在某些情况下也不可能扩展核心应用程序。
- 可扩展性:很难找到一个解决方案,您可以编写自己的代码并更改业务逻辑或添加额外的东西。部分原因是许多供应商将其HCMS设计为哑数据存储,部分原因是管理可扩展性的复杂性。
何时何地使用Headless CMS?
Headless CMS是一个很好的机会,但在这里,我们必须了解使用它来优化成本/效益比的最佳方案。问题在于,使用常规HCMS,定制非常有限,因此如果您不在正确的情况下,很难将HCMS混合以实现业务需求。而且,像裸数据存储一样使用它会使它变得毫无意义。
何时使用HCMS很方便:
- 在一段时间里,UI上有很多变化
- 许多共享相同信息的应用程序和一个管理它的团队
- 您对数据的业务逻辑很少
- 你可以聘请多个团队(be + fe)
您何时不应该使用HCMS:
- 有一个符合您需求的垂直解决方案(例如,您希望博客使用wordpress)
- 你有很多业务逻辑
- 你不是数据的主人
RawCMS:构建自己的Headless CMS
在本章中,我们将看到RawCMS是什么以及我如何使用ASP.NET Core,mongodb,Docker和一些幻想创建Headless CMS。
为什么另一个Headless CMS?
RawCMS的目的是在没有HCMS的共同限制的情况下生成HCMS(......以及在新技术上训练有趣的东西;-))
RawCms特征选择
所以我们将提出的功能:
- 可以使用oauth2自省(或内置的auth系统)对其他auth系统进行身份验证的可能性
- 可以使用挂钩/事件系统添加业务逻辑的可能性
- 可以添加自定义端点来管理与数据无关的事件的可能性
- 可以在插件系统中添加功能的可能性
- 验证数据的可能性
- 使用多种协议公开数据,如webapi,GraphQL,Odata
架构
基本上,我将实现的架构如下。实际上,插件部分有一些限制,缺少工作流管理,但其他部分功能齐全。
服务层
服务层是系统的核心部分。使用mongodb实体上的常规JObject映射,您可以在mongo集合中存储您想要的任何内容,所有数据都是无类型的。
这是本类中最相关的部分,用于解释它的工作原理。
public class CRUDService
{public JObject Get(string collection, string id){//Create filter by id (all entity MUST have an id field, called _id by convention)FilterDefinition<BsonDocument> filter = Builders<BsonDocument>.Filter.Eq("_id", BsonObjectId.Create(id));IFindFluent<BsonDocument, BsonDocument> results = _mongoService.GetCollection<BsonDocument>(collection).Find<BsonDocument>(filter);return ConvertBsonToJson(json);}public ItemList Query(string collection, DataQuery query){FilterDefinition<BsonDocument> filter = FilterDefinition<BsonDocument>.Empty;if (query.RawQuery != null){filter = new JsonFilterDefinition<BsonDocument>(query.RawQuery);}InvokeAlterQuery(collection, filter);IFindFluent<BsonDocument, BsonDocument> results = _mongoService.GetCollection<BsonDocument>(collection).Find<BsonDocument>(filter).Skip((query.PageNumber - 1) * query.PageSize).Limit(query.PageSize);long count = Count(collection, filter); return new ConverToItemList(results, (int)count, query.PageNumber, query.PageSize);}public JObject Update(string collection, JObject item, bool replace){//Invoke validation eventsInvokeValidation(item, collection);// create collection if not existsEnsureCollection(collection);FilterDefinition<BsonDocument> filter = Builders<BsonDocument>.Filter.Eq("_id", BsonObjectId.Create(item["_id"].Value<string>()));//Invoke presave eventsInvokeProcess(collection, ref item, SavePipelineStage.PreSave);//insert id (mandatory)BsonDocument doc = BsonDocument.Parse(item.ToString());doc["_id"] = BsonObjectId.Create(item["_id"].Value<string>());//set into "incremental" update modedoc = new BsonDocument("$set", doc); UpdateOptions o = new UpdateOptions(){IsUpsert = true,BypassDocumentValidation = true};if (replace){_mongoService.GetCollection<BsonDocument>(collection).ReplaceOne(filter, doc, o);}else{BsonDocument dbset = new BsonDocument("$set", doc);_mongoService.GetCollection<BsonDocument>(collection).UpdateOne(filter, dbset, o);}//Post save eventsInvokeProcess(collection, ref item, SavePipelineStage.PostSave);return JObject.Parse(item.ToJson(js));}
}
认证
认证部分完成添加身份服务器并使用基于RawCms设置的不同配置。通过这种方式,我们可以使用内部身份服务器(其他人获取我们的令牌,我们拥有用户数据)或与其他认证系统集成(我们在请求标头中获取令牌,我们够能将其推送到其他oauth系统上)。
这是代码中最相关的部分。此代码在身份验证插件启动期间调用,并从数据库获取配置。与该类的认证配置无关的所有代码部分都被省略。
public override void ConfigureServices(IServiceCollection services)
{base.ConfigureServices(services);//configuration came from constructorservices.Configure<ConfigurationOptions>(configuration);services.AddSingleton<IUserStore<IdentityUser>>(x => { return userStore; });//... registering all identity server services for user and roles (all code omitted)services.AddSingleton<IUserClaimsPrincipalFactory<IdentityUser>, RawClaimsFactory>();// configure identity server with in-memory stores, keys, clients and scopesservices.AddIdentityServer().AddDeveloperSigningCredential().AddInMemoryPersistedGrants().AddInMemoryIdentityResources(config.GetIdentityResources()).AddInMemoryApiResources(config.GetApiResources()).AddInMemoryClients(config.GetClients()).AddAspNetIdentity<IdentityUser>().AddProfileServiceCustom(userStore);if (config.Mode == OAuthMode.External){OAuth2IntrospectionOptions options = new OAuth2IntrospectionOptions{//... set option basing on config (code omitted) };options.Validate();services.AddAuthentication(OAuth2IntrospectionDefaults.AuthenticationScheme).AddOAuth2Introspection(x =>{x = options;});}else{services.AddAuthentication(OAuth2IntrospectionDefaults.AuthenticationScheme).AddIdentityServerAuthentication("Bearer", options =>{//... set option basing on config (code omitted)});}services.AddMvc(options =>{//this apply custom authentication like apitoken other than oauth standardoptions.Filters.Add(new RawAuthorizationAttribute(config.ApiKey, config.AdminApiKey));});
}
Lambda表达式
Lamba是一个简单的命令模式实现,该名称的灵感来自无服务器模型,您可以将函数公开为rest端点。基于此,您可以通过实现lamba来调整系统中的所有内容。每个lambda实例都在运行时发现,并根据lamba类型和事件调用,并将数据上下文传递给它。
下面给出一些lambda示例。
使用lambda添加自定义端点
public class DummyRest : RestLambda
{public override string Name => "DummyRest";public override string Description => "I'm a dumb dummy request";public override JObject Rest(JObject input){JObject result = new JObject(){{ "input",input},{ "now",DateTime.Now},};return result;}
}
验证数据
public class MyCustomValidation : SchemaValidationLambda
{public override string Name => "My custom Validation";public override string Description => "Provide entity validation";public override List<Error> Validate(JObject input, string collection){//do here all check with datareturn ImplementCheckHere(input, collection);}
}
更改保存数据
public class AuditLambda : PreSaveLambda
{public override string Name => "AuditLambda";public override string Description => "Add audit settings";public override void Execute(string collection, ref JObject Item){if (!Item.ContainsKey("_id") || string.IsNullOrEmpty(Item["_id"].ToString())){Item["_createdon"] = DateTime.Now;}Item["_modifiedon"] = DateTime.Now;}
}
插件
插件系统背后的想法是创建一个项目,开发您的功能,将DLL扔进bin文件夹并使其可用于应用程序。其中的主要部分将被讨论成一篇专门的文章,因为解释和偏离主题需要很长时间。我只想在这里展示一下插件系统的原理。这也意味着您可以使用nuget作为交付系统或功能市场。
public class GraphQLPlugin : RawCMS.Library.Core.Extension.Plugin
{public override string Name => "GraphQL";public override string Description => "Add GraphQL CMS capabilities";public override void Init(){Logger.LogInformation("GraphQL plugin loaded");}public override void ConfigureServices(IServiceCollection services){//will be triggered on Startup.cs ConfigureServicesbase.ConfigureServices(services);}private void SetConfiguration(Plugin plugin, CRUDService crudService){//used to receive configuration from system}public override void Configure(IApplicationBuilder app, AppEngine appEngine){// will be triggered on Startup.cs Configurebase.Configure(app, appEngine);}
}
如何使用RawCMS
为了让用户测试这个解决方案,我实现了很多选项。
从Docker安装
这是最方便的。您可以在文档内找到一个docker compose示例,或者您可以使用docker run然后链接到mongodb实例。
docker run rawcms -p 80:8081
或使用docker compose:
version: '3'
services:rawcms:build: .ports:- "54321:54321" links:- mongoenvironment:- MongoSettings__ConnectionString=mongodb://mongo:27017/rawCms- PORT=54321- ASPNETCORE_ENVIRONMENT=Dockermongo:image: mongo
环境变量MongoSettings__ConnectionString用于将连接字符串传递给应用程序。
从Zip Release安装
如果您尚未准备好容器,可以从GitHub版本下载zip文件,并将其作为常规ASP.NET Core应用程序手动部署。
建立你自己的
第三种可能性是分解解决方案,并在本地发挥作用。目前,您的设置中没有任何nuget包,因此建议的最佳解决方案是将github repo添加为子模块或子树。
兴趣点
HMCS是解耦架构和避免无用工作的绝佳机会。这可能会带来诸如减少时间和成本等好处,使各方独立。当然,这不是灵丹妙药,您必须了解垂直解决方案是否更方便,或者您的企业登录是否避免你使用它。
我试图实现HCMS,我们看到了一个非常重要的话题。这很有趣,我们了解如何实现最重要的主题,以超越HCMS的实际技术限制。
原文地址:https://www.codeproject.com/Articles/1278159/Inside-Headless-CMS
Headless CMS 的内部相关推荐
- 什么是 Headless CMS?
什么是 Headless CMS? Headless 内容管理是当今 Web 设计的关键开发,它将前端客户端应用程序与后端内容管理系统分离开. 因此,Headless CMS 负责(后端)内容管理服务 ...
- Headless CMS - 打破“设计优先”的怪圈
什么是 Headless CMS? 为什么 Headless CMS 带有真正的革命性?因为它严格的将内容和格式分离,使我们回归到内容管理的本源.这种变化必然会带来一些不确定性.因此,在开始您的第一个 ...
- java html5 cms,企业内部cms业务管理系统html5简洁的模板
模板描述:企业内部 cms业务管理系统 html5 简洁的 模板.企业内部cms业务管理系统html5简洁的模板,bootstrap框架,pc和wap通用 代码结构 1. 引入CSS 2. 引入JS ...
- Headless CMS Sanity 数据建模——定义文档内容的结构
定义内容的结构. 使用代码定义内容模型是设计使然.它使版本控制变得更容易,并使开发人员能够控制数据结构的布局方式.我们让在界面中添加.更改和删除字段变得毫不费力. 观看Schema 工作原理的视频,或 ...
- restful解决什么问题_当您陷入RESTful,WordPress和一个困难的地方时,如何解决CMS问题...
restful解决什么问题 by Jessica Duffin Wolfe 杰西卡·达芬·沃尔夫(Jessica Duffin Wolfe) 当您陷入RESTful,WordPress和一个困难的地方 ...
- sanity测试_Sanity.io入门-您可以自定义的无头CMS
sanity测试 If you're looking for a headless CMS with amazing features and tons of customization, look ...
- 2020年那种语言最受欢迎_2020年15种最佳和最受欢迎的CMS平台(比较)
2020年那种语言最受欢迎 Are you wondering what CMS platform to use for building your website? 您是否想知道使用什么CMS平台来 ...
- 一篇文章教你弄懂java CMS垃圾回收日志
文章目录 一.CMS垃圾回收器介绍 二.CMS JVM运行参数 三.CMS收集器运行过程 1.初始标记(CMS initial mark) 2.并发标记(CMS concurrent mark) 3. ...
- 30.jvm.gc(GC之详解CMS收集过程和日志分析)
30.jvm.gc(GC之详解CMS收集过程和日志分析) 30.1.话题引入 30.2.ParNew and CMS 30.3.日志 30.3.1.GC日志初体验 30.3.2.Minor GC 30 ...
最新文章
- 深度学习(二十六)Network In Network学习笔记-ICLR 2014
- 蓝桥杯 第三届C/C++预赛真题(7) 放棋子(水题)
- [译]关于NODE_ENV,哪些你应该了解
- 怎么让员工服从管理_为什么现在的员工执行力和服从性越来越差,管理一严格就辞职?...
- php中get_featured_posts()是什么意思,WordPress的Get_Posts()函数详解
- python复习-正则表达式
- 微信小程序-上传多张图片加进度条(支持预览、删除)
- max_workers解释
- vue-tv-focusable
- 使用jxls导出报错:Connot load XLS transformer please make sure a Transformer implementation is in classpath
- macbook视频格式转换_如何将Mac视频格式转换
- SUMO地图中添加交通流
- 删除安卓7.1源码中自带的Japanese IME输入法
- 智能证件录入系统——电子护照阅读器
- 笔记本手机都能用的充电器,做的只有乒乓球大小,AOHi 65W氮化镓充电器体验
- Java类的加载及父类子类加载顺序
- java.lang.NullPointerException: pattern
- C4Dr18安装完成双击图标无任何反应,缺失libmmd.dll
- CSS进阶篇——展示 (display)
- 项目计划管理软件:GanttProject
热门文章
- java字符串拼接_这样写Java,同事直呼666
- html5+上下左右边界顺序,详解canvas绘制多张图的排列顺序问题
- android 原生开发 3d地图 下载_arcgis api 3.x for js 入门开发系列二不同地图服务展示(附源码下载)...
- 计算机系毕业生自我评价,计算机系应届毕业生自我评价范文
- probe request帧结构_WIFI基础知识(802.11)
- spark的python开发安装方式_windows下安装spark-python
- vue svg sprite loader_Vue项最佳实践
- java中注释的嵌套,java – 使用mybatis注释获取嵌套对象
- 三菱的触摸屏usb驱动_如何实现一个TK6071IP的触摸屏驱动两个三菱FX1S-30MT的PLC?...
- python之数字操作