headless CMS_model view controller

headless CMS_model view controller目录介绍HeadlessCMS什么是HeadlessCMS?HeadlessCMS的优点HeadlessCMS解决方案的局限性使用HCMS的缺点HCMS的局限性何时何地使用HeadlessCMS?RawCMS:构建自己的HeadlessCMS为什么另一个HeadlessCMS?RawCms特征选择架构服务层认证Lambda表…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

目录

介绍

Headless CMS

什么是Headless CMS?

Headless CMS的优点

Headless CMS解决方案的局限性

使用HCMS的缺点

HCMS的局限性

何时何地使用Headless CMS?

RawCMS:构建自己的Headless CMS

为什么另一个Headless CMS?

RawCms特征选择

架构

服务层

认证

Lambda表达式

使用lambda添加自定义端点

验证数据

更改保存数据

插件

如何使用RawCMS

从Docker安装

从Zip Release安装

建立你自己的

兴趣点


介绍

在本文中,我们将了解Headless CMS,我们将了解它的优点以及何时使用方便。此外,我们将列举实际的主要限制。为了更好地理解HCMS如何在幕后工作,我将解释如何设计和构建RawCMS,一个带有Oauth2Aspnet.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消耗的数据存储的垂直解决方案,大部分事情已经完成,因此您必须专注于数据设计而不是技术细节(例如浪费时间考虑有效载荷,何时可以免费的使用OdataGrahql)。
  • 垂直解决方案HCMS做一件事。这使得学习和维护变得非常容易。
  • 灵活性:一旦你选择了你的HCMS(无论是本地还是云端),你的开发人员都可以使用他们喜欢的任何语言来实现前端。这意味着您可以自由地使用技术限制。

Headless CMS解决方案的局限性

与传统的CMS相比,HCMS相当年轻,因此,即使很多产品在过去几年诞生,大多数产品也不是那么成熟,无法完全取代传统的API后端。在这个阶段中,我将分享我对我发现的限制的经验。功能可能会因特定产品而异,如果是本地或saas解决方案。

实际上,主要有两种CMS Headless限制:

  • 使用HCMS的缺点
  • 您安装的产品的限制

使用HCMS的缺点

HCMS需要雇佣多个团队来实现工作并行化的好处。此外,由于HCMS没有任何渲染,所有的表示逻辑都被要求提供给客户端。这对于解耦很有用,但在所有情况下,您只有一个消费者解耦优势并不那么相关,并且您在数据获取过程中引入了更多的复杂性和延迟。另一个问题是关于业务逻辑。在哪里实施?如果你不想实现HCMS,你必须把它放到表示层,并且有多个消费者,当逻辑存在于多个地方时,您将复制它,陷入问题中。否则,尝试将其放入HMS,您会发现大多数云解决方案\产品都不那么灵活。这引入了下一个主题,所有HCMS的限制是什么?

HCMS的局限性

测试最重要的HCMS解决方案,我遇到了许多困难的情况,以下是最常见的限制列表。考虑到这取决于产品,有人可能有或没有,但一般来说,大多数都很常见。

  • 针对外部提供程序的身份验证:大多数解决方案不允许针对外部系统对用户进行身份验。我说的是最常见的情况,即您拥有一个中央身份验证系统,并且所有各方都会传递用户令牌\票证以代表用户进行操作。换句话说,如果我有一个oauth2服务器,我想在前端进行身份验证,并使用令牌向内部网的所有应用程序进行调用,而不仅仅是HCMS,并被识别为我自己。
  • 非标准输出格式:有些使用graphqlOdata,这很好,因为它为数据消耗提供了标准方法。问题是某些并不意味着全部,所以你必须注意选择你的HCMS
  • 业务逻辑:在大多数情况下,不可能在运行时定义业务逻辑,在某些情况下也不可能扩展核心应用程序。
  • 可扩展性:很难找到一个解决方案,您可以编写自己的代码并更改业务逻辑或添加额外的东西。部分原因是许多供应商将其HCMS设计为哑数据存储,部分原因是管理可扩展性的复杂性。

何时何地使用Headless CMS

Headless CMS是一个很好的机会,但在这里,我们必须了解使用它来优化成本/效益比的最佳方案。问题在于,使用常规HCMS,定制非常有限,因此如果您不在正确的情况下,很难将HCMS混合以实现业务需求。而且,像裸数据存储一样使用它会使它变得毫无意义。

何时使用HCMS很方便:

  • 在一段时间里,UI上有很多变化
  • 许多共享相同信息的应用程序和一个管理它的团队
  • 您对数据的业务逻辑很少
  • 你可以聘请多个团队(be + fe

您何时不应该使用HCMS

  • 有一个符合您需求的垂直解决方案(例如,您希望博客使用wordpress
  • 你有很多业务逻辑
  • 你不是数据的主人

RawCMS:构建自己的Headless CMS

headless CMS_model view controller

在本章中,我们将看到RawCMS是什么以及我如何使用ASP.NET CoremongodbDocker和一些幻想创建Headless CMS

为什么另一个Headless CMS

RawCMS的目的是在没有HCMS的共同限制的情况下生成HCMS……以及在新技术上训练有趣的东西;-)

RawCms特征选择

所以我们将提出的功能:

  • 可以使用oauth2自省(或内置的auth系统)对其他auth系统进行身份验证的可能性
  • 可以使用挂钩/事件系统添加业务逻辑的可能性
  • 可以添加自定义端点来管理与数据无关的事件的可能性
  • 可以在插件系统中添加功能的可能性
  • 验证数据的可能性
  • 使用多种协议公开数据,如webapiGraphQLOdata

架构

基本上,我将实现的架构如下。实际上,插件部分有一些限制,缺少工作流管理,但其他部分功能齐全。

headless CMS_model view controller

服务层

服务层是系统的核心部分。使用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 events
        InvokeValidation(item, collection);

        // create collection if not exists
        EnsureCollection(collection);

        FilterDefinition<BsonDocument> filter = Builders<BsonDocument>.Filter.Eq
        ("_id", BsonObjectId.Create(item["_id"].Value<string>()));

        //Invoke presave events
        InvokeProcess(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 mode
        doc = 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 events
        InvokeProcess(collection, ref item, SavePipelineStage.PostSave);
        return JObject.Parse(item.ToJson(js));
    }
}

Jetbrains全家桶1年46,售后保障稳定

认证

认证部分完成添加身份服务器并使用基于RawCms设置的不同配置。通过这种方式,我们可以使用内部身份服务器(其他人获取我们的令牌,我们拥有用户数据)或与其他认证系统集成(我们在请求标头中获取令牌,我们够能将其推送到其他oauth系统上)。

这是代码中最相关的部分。此代码在身份验证插件启动期间调用,并从数据库获取配置。与该类的认证配置无关的所有代码部分都被省略。

public override void ConfigureServices(IServiceCollection services)
{
    base.ConfigureServices(services);

    //configuration came from constructor
    services.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 scopes
    services.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 standard
        options.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 data
        return 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 ConfigureServices
        base.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 Configure
        base.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:
    - mongo
    environment:
    - MongoSettings__ConnectionString=mongodb://mongo:27017/rawCms
    - PORT=54321
    - ASPNETCORE_ENVIRONMENT=Docker
  mongo:
    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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/234632.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • pandas’_pandas 删除列

    pandas’_pandas 删除列文章目录1.pd.Series.dropna官方案例2.pd.DataFrame.dropna官方案例1.pd.Series.dropnaSeries.dropna(axis=0,inplace=False,how=None)描述返回删除了缺失值的新Series参数axis:{0or‘index’},default0只有一个轴可以从中删除值inplace:bool,defaultFalse如果为True,则就地修改返回None如果为False,则

  • 怎么学计算机自学,怎样才能学会电脑 快速入门的学习办法

    怎么学计算机自学,怎样才能学会电脑 快速入门的学习办法现在的家庭有电脑已经不是一件很稀罕的事情了,有些人可能是因为年纪、或是因为对电脑的熟悉不高,所以对电脑的使用不太了解,一直徘徊在电脑知识的殿堂门口,为了帮助更多人可以使用上电脑,小编今天为大家带来了一些电脑的入门知识,告诉大家怎样才能学会电脑。一、怎样才能学会电脑1、在学习电脑之前,我们首先要有一个明确的目标,这样才会有方向感。知道自己需要了解什么方面的电脑知识。像是硬件维修、调试、软件编程、电脑…

  • AssetBundle详解

    AssetBundle详解一:AssetBundle介绍AssetBundle是将资源使用Unity提供的一种用于存储资源的压缩格式打包后的集合,它可以存储任何一种Unity可以识别的资源,如模型,纹理图,音频,场景等资源。也可以加载开发者自定义的二进制文件。他们的文件类型是.assetbundle/.unity3d,他们先前被设计好,很容易就下载到我们的游戏或者场景当中。一般情况下AssetBundle的具体开发…

  • ManagementObjectSearcher的使用「建议收藏」

    ManagementObjectSearcher的使用「建议收藏」1.获取本地路径的网络访问地址privateIEnumerable<KeyValuePair<string,string>>GetShareFolders(){va

  • 博客搬家

    博客搬家我的另一个博客http://www.cnblogs.com/dingxiaowei欢迎光临,共同学习!我的新浪微博http://blog.sina.com.cn/dingxiaowei2013转载于:https://www.cnblogs.com/java20130723/archive/2013/01/04/3211485.html…

  • 深度信念网络DBN的一个matlab实例「建议收藏」

    关于深度学习的一些个人浅见:   深度学习通常是训练深度(多层)神经网络,用于模式识别(如语音、图像识别);深度网络指是具有深层(多层)网络结构的神经网络。   深层网络由于神经元多,参数多,拟合表现能力强,有表现欲解决复杂问题的能力。   但是深度网络存在很多局部最优解,深度网络的训练容易停留在局部最优上,初始参数的选择对网络最终收敛在那个位置有很大的影响。

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号