-
Notifications
You must be signed in to change notification settings - Fork 31
howto
The aim of this document is to show how to get devon4net things done in a easy way.
The .Net Core 3.1 template allows you to start developing an n-layer server application to provide the latest features. The template can be used in Visual Studio Code and Visual Studio 2019.
The application result can be deployed as a console application, microservice or web page.
To start developing with devon4Net template, please follow this instructions:
Open your favourite terminal (Win/Linux/iOS) Go to future project's path Type dotnet new --install Devon4Net.WebAPI.Template Type dotnet new Devon4NetAPI Go to project's path You are ready to start developing with devon4Net
Create a new dotnet` API` project from scratch Add the nuget package reference to your project: Install-Package Devon4Net.Application.WebAPI.Configuration
Set up your project as follows in program.cs file:
public static void Main(string[] args)
{
// Please use
// Devonfw.Configure<Startup>(args);
// Or :
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.InitializeDevonFw()
.Build()
.Run();
}
Set up your project as follows in startup.cs file:
private IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureDevonFw(Configuration);
SetupDatabase(services);
...
}
private void SetupDatabase(IServiceCollection services)
{
// Default is the database connection name in appsettings.json file
services.SetupDatabase<TodoContext>(Configuration, "Default", DatabaseType.InMemory);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.ConfigureDevonFw();
...
}
Add the devonfw configuration options in your appsettings.json file
To start using devon4net in your .net core application add this configuration in your appsettings.json file:
"devonfw": {
"UseDetailedErrorsKey": true,
"UseIIS": false,
"UseSwagger": true,
"Environment": "Development",
"KillSwitch": {
"killSwitchSettingsFile": "killswitch.appsettings.json"
},
"Kestrel": {
"UseHttps": true,
"HttpProtocol": "Http2", //Http1, Http2, Http1AndHttp2, none
"ApplicationPort": 8082,
"KeepAliveTimeout": 120, //in seconds
"MaxConcurrentConnections": 100,
"MaxConcurrentUpgradedConnections": 100,
"MaxRequestBodySize": 28.6, //In MB. The default maximum request body size is 30,000,000 bytes, which is approximately 28.6 MB
"Http2MaxStreamsPerConnection": 100,
"Http2InitialConnectionWindowSize": 131072, // From 65,535 and less than 2^31 (2,147,483,648)
"Http2InitialStreamWindowSize": 98304, // From 65,535 and less than 2^31 (2,147,483,648)
"AllowSynchronousIO": true,
"SslProtocol": "Tls12", //Tls, Tls11,Tls12, Tls13, Ssl2, Ssl3, none. For Https2 Tls12 is needed
"ServerCertificate": {
"Certificate": "localhost.pfx",
"CertificatePassword": "localhost"
},
"ClientCertificate": {
"DisableClientCertificateCheck": true,
"RequireClientCertificate": false,
"CheckCertificateRevocation": true,
"ClientCertificates": {
"Whitelist": [
"3A87A49460E8FE0E2A198E63D408DC58435BC501"
],
"DisableClientCertificateCheck": false
}
}
},
"IIS": {
"ForwardClientCertificate": true,
"AutomaticAuthentication": true,
"AuthenticationDisplayName" : ""
}
}
Also, for start using the devon4net components, you should add the next json options in your appsettings.json or appsettings.Development.json file:
{
"ExtraSettingsFiles": [
"Put a directory path (relative/absolute/linux-like) like /run/secrets/global where there are many settings/secret files to load",
"Put a specific file name (with/without path) like /app-configs/app/extra-settings.json"
],
"ConnectionStrings": {
"Default": "Todos",
"Employee": "Employee",
"RabbitMqBackup": "Add your database connection string here for messaging backup",
"MediatRBackup": "Add your database connection string here for messaging backup"
},
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"Swagger": {
"Version": "v1",
"Title": "devon4net API",
"Description": "devon4net API Contract",
"Terms": "https://www.devonfw.com/terms-of-use/",
"Contact": {
"Name": "devonfw",
"Email": "[email protected]",
"Url": "https://www.devonfw.com"
},
"License": {
"Name": "devonfw - Terms of Use",
"Url": "https://www.devonfw.com/terms-of-use/"
},
"Endpoint": {
"Name": "V1 Docs",
"Url": "/swagger/v1/swagger.json",
"UrlUi": "swagger",
"RouteTemplate": "swagger/v1/{documentName}/swagger.json"
}
},
"JWT": {
"Audience": "devon4Net",
"Issuer": "devon4Net",
"TokenExpirationTime": 60,
"ValidateIssuerSigningKey": true,
"ValidateLifetime": true,
"ClockSkew": 5,
"Security": {
"SecretKeyLengthAlgorithm": "",
"SecretKeyEncryptionAlgorithm": "",
"SecretKey": "",
"Certificate": "",
"CertificatePassword": "",
"CertificateEncryptionAlgorithm": ""
}
},
"Cors": []
//[
// {
// "CorsPolicy": "CorsPolicy1",
// "Origins": "http://example.com,http://www.contoso.com",
// "Headers": "accept,content-type,origin,x-custom-header",
// "Methods": "GET,POST,HEAD",
// "AllowCredentials": true
// },
// {
// "CorsPolicy": "CorsPolicy2",
// "Origins": "http://example.com,http://www.contoso.com",
// "Headers": "accept,content-type,origin,x-custom-header",
// "Methods": "GET,POST,HEAD",
// "AllowCredentials": true
// }
//]
,
"CircuitBreaker": {
"CheckCertificate": false,
"Endpoints": [
{
"Name": "AnsibleTower",
"BaseAddress": "PUT THE IP ADDRESS HERE",
"Headers": {
},
"WaitAndRetrySeconds": [
0.0001,
0.0005,
0.001
],
"DurationOfBreak": 0.0005,
"UseCertificate": false,
"Certificate": "localhost.pfx",
"CertificatePassword": "localhost",
"SslProtocol": "3072" //TLS12
},
{
"Name": "CyberArk",
"BaseAddress": "PUT THE IP ADDRESS HERE",
"Headers": {
},
"WaitAndRetrySeconds": [
0.0001,
0.0005,
0.001
],
"DurationOfBreak": 0.0005,
"UseCertificate": false,
"Certificate": "localhost.pfx",
"CertificatePassword": "localhost",
"SslProtocol": "3072" //TLS12
},
{
"Name": "SmaxHcm",
"BaseAddress": "PUT THE IP ADDRESS HERE",
"Headers": {
},
"WaitAndRetrySeconds": [
0.0001,
0.0005,
0.001
],
"DurationOfBreak": 0.0005,
"UseCertificate": false,
"Certificate": "localhost.pfx",
"CertificatePassword": "localhost",
"SslProtocol": "3072" //TLS12
}
]
},
"Headers": {
"AccessControlExposeHeader": "Authorization",
"StrictTransportSecurityHeader": "",
"XFrameOptionsHeader": "DENY",
"XssProtectionHeader": "1;mode=block",
"XContentTypeOptionsHeader": "nosniff",
"ContentSecurityPolicyHeader": "",
"PermittedCrossDomainPoliciesHeader": "",
"ReferrerPolicyHeader": ""
},
"Log": {
"UseAOPTrace": false,
"LogLevel": "Debug",
"SqliteDatabase": "logs/log.db",
"LogFile": "logs/{0}_devonfw.log",
"SeqLogServerHost": "http://127.0.0.1:5341",
"GrayLog": {
"GrayLogHost": "127.0.0.1",
"GrayLogPort": "12201",
"GrayLogProtocol": "UDP",
"UseSecureConnection": true,
"UseAsyncLogging": true,
"RetryCount": 5,
"RetryIntervalMs": 15,
"MaxUdpMessageSize": 8192
}
},
"RabbitMq": {
"EnableRabbitMq": false,
"Hosts": [
{
"Host": "127.0.0.1",
"Port": 5672,
"Ssl": false,
"SslServerName": "localhost",
"SslCertPath": "localhost.pfx",
"SslCertPassPhrase": "localhost",
"SslPolicyErrors": "RemoteCertificateNotAvailable" //None, RemoteCertificateNotAvailable, RemoteCertificateNameMismatch, RemoteCertificateChainErrors
}
],
"VirtualHost": "/",
"UserName": "admin",
"Password": "password",
"Product": "devon4net",
"RequestedHeartbeat": 10, //Set to zero for no heartbeat
"PrefetchCount": 50,
"PublisherConfirms": false,
"PersistentMessages": true,
"Platform": "localhost",
"Timeout": 10,
"Backup": {
"UseLocalBackup": false,
"DatabaseName": "devon4netMessageBackup.db"
}
},
"MediatR": {
"EnableMediatR": false,
"Backup": {
"UseLocalBackup": false,
"DatabaseName": "devon4netMessageBackup.db"
}
},
"LiteDb": {
"DatabaseLocation": "devon4net.db"
},
"AnsibleTower": {
"EnableAnsible": false,
"Name": "AnsibleTower",
"CircuitBreakerName": "AnsibleTower",
"ApiUrlBase": "/api/v2/?format=json",
"Version": "1.0.5.29",
"Username": "",
"Password": ""
},
"CyberArk": {
"EnableCyberArk": false,
"Username": "",
"Password": "",
"CircuitBreakerName": "CyberArk"
},
"SmaxHcm": {
"EnableSmax": false,
"Username": "",
"Password": "",
"TenantId": "",
"CircuitBreakerName": "SmaxHcm",
"ProviderId": ""
},
"Kafka": {
"EnableKafka": true,
"Administration": [
{
"AdminId": "Admin1",
"Servers": "127.0.0.1:9092"
}
],
"Producers": [
{
"ProducerId": "Producer1", // devon identifier
"Servers": "127.0.0.1:9092", // Initial list of brokers as a CSV list of broker host or host:port. The application may also use `rd_kafka_brokers_add()` to add brokers during runtime
"ClientId": "client1", //Client identifier
"Topic": "devonfw", // topics to deliver the message
"MessageMaxBytes": 1000000, //Maximum Kafka protocol request message size. Due to differing framing overhead between protocol versions the producer is unable to reliably enforce a strict max message limit at produce time and may exceed the maximum size by one message in protocol ProduceRequests, the broker will enforce the the topic's `max.message.bytes` limit (see Apache Kafka documentation)
"CompressionLevel": -1, // [0-9] for gzip; [0-12] for lz4; only 0 for snappy; -1 = codec-dependent default compression level
"CompressionType": "None", // None, Gzip, Snappy, Lz4, Zstd
"ReceiveMessageMaxBytes": 100000000,
"EnableSslCertificateVerification": false,
"CancellationDelayMaxMs": 100, // The maximum length of time (in milliseconds) before a cancellation request is acted on. Low values may result in measurably higher CPU usage
"Ack": "None", //Zero=Broker does not send any response/ack to client, One=The leader will write the record to its local log but will respond without awaiting full acknowledgement from all followers. All=Broker will block until message is committed by all in sync replicas (ISRs). If there are less than min.insync.replicas (broker configuration) in the ISR set the produce request will fail
"Debug": "", //A comma-separated list of debug contexts to enable. Detailed Producer debugging: broker,topic,msg. Consumer: consumer,cgrp,topic,fetch
"BrokerAddressTtl": 1000, //How long to cache the broker address resolving results (milliseconds)
"BatchNumMessages": 1000000, // Maximum size (in bytes) of all messages batched in one MessageSet, including protocol framing overhead. This limit is applied after the first message has been added to the batch, regardless of the first message's size, this is to ensure that messages that exceed batch.size are produced. The total MessageSet size is also limited by batch.num.messages and message.max.bytes
"EnableIdempotence": false, //When set to `true`, the producer will ensure that messages are successfully produced exactly once and in the original produce order. The following configuration properties are adjusted automatically (if not modified by the user) when idempotence is enabled: `max.in.flight.requests.per.connection=5` (must be less than or equal to 5), `retries=INT32_MAX` (must be greater than 0), `acks=all`, `queuing.strategy=fifo`. Producer instantation will fail if user-supplied configuration is incompatible
"MaxInFlight": 5,
"MessageSendMaxRetries": 5,
"BatchSize": 100000000 // Maximum size (in bytes) of all messages batched in one MessageSet, including protocol framing overhead. This limit is applied after the first message has been added to the batch, regardless of the first message's size, this is to ensure that messages that exceed batch.size are produced. The total MessageSet size is also limited by batch.num.messages and message.max.bytes
}
],
"Consumers": [
{
"ConsumerId": "Consumer1", // devon identifier
"Servers": "127.0.0.1:9092",
"GroupId": "group1",
"Topics": "devonfw", // Comma separated topics to subscribe
"AutoCommit": true, //Automatically and periodically commit offsets in the background. Note: setting this to false does not prevent the consumer from fetching previously committed start offsets. To circumvent this behaviour set specific start offsets per partition in the call to assign()
"StatisticsIntervalMs": 0, //librdkafka statistics emit interval. The application also needs to register a stats callback using `rd_kafka_conf_set_stats_cb()`. The granularity is 1000ms. A value of 0 disables statistics
"SessionTimeoutMs": 10000, //Client group session and failure detection timeout. The consumer sends periodic heartbeats (heartbeat.interval.ms) to indicate its liveness to the broker. If no hearts are received by the broker for a group member within the session timeout, the broker will remove the consumer from the group and trigger a rebalance. The allowed range is configured with the **broker** configuration properties `group.min.session.timeout.ms` and `group.max.session.timeout.ms`. Also see `max.poll.interval.ms`
"AutoOffsetReset": "Largest", //Action to take when there is no initial offset in offset store or the desired offset is out of range: 'smallest','earliest' - automatically reset the offset to the smallest offset, 'largest','latest' - automatically reset the offset to the largest offset, 'error' - trigger an error which is retrieved by consuming messages and checking 'message->err'
"EnablePartitionEof": true, //Verify CRC32 of consumed messages, ensuring no on-the-wire or on-disk corruption to the messages occurred. This check comes at slightly increased CPU usage
"IsolationLevel": "ReadCommitted", //Controls how to read messages written transactionally: `ReadCommitted` - only return transactional messages which have been committed. `ReadUncommitted` - return all messages, even transactional messages which have been aborted.
"EnableSslCertificateVerification": false,
"Debug": "" //A comma-separated list of debug contexts to enable. Detailed Producer debugging: broker,topic,msg. Consumer: consumer,cgrp,topic,fetch
}
]
}
}
In this guide we will explain how to generate a new WebApi project from an OpenAPI 3.0.0 specification. This means that we are going to use a “contract first” strategy. This is going to be possible due to these type of files that contain all the information about entities, operations, etc…
In order to make it work we are using CobiGen, a powerful tool for generating source code. CobiGen allows users to generate all the structure and code of the components, helping to save a lot of time otherwise wasted on repetitive tasks.
The devonfw distributions can be obtained from the TeamForge releases library and are packaged in zips files that include all the needed tools, software and configurations.
It is not necessary to install nor configure anything. Just extracting the zip content is enough to have a fully functional devonfw. The only thing you have to do is run create-or-update-workspace.bat and then update-all-workspaces.bat to set up all the needed tools.
We are going to use the template of devon4net as a base to generate all the code, so what we have to do now is to download said template using the following steps.
First of all you have to set up all the environment for .NET, you can do this using the following tutorial. Next we are going to create a new folder where we want to have the WebAPI project, lastly we are going to open the terminal there.
Type the following:
dotnet new -i Devon4Net.WebAPI.Template
and then:
dotnet new Devon4NetAPI
In order to let CobiGen generate all the files, we first have to make some modifications to our OpenAPI file.
It is obligatory to put the “x-rootpackage” tag to indicate where CobiGen will place the generated files as well as the "x-component" tags for each component, keep in mind that due to CobiGen’s limitations each component must have its own entity.
You can read more information about how to configure your OpenAPI file and a working example here.
Cobigen allow us to generate the files in two different ways. One of them is using Eclipse which it can be done by using the its graphical interface. The other way to generate the code is using the Cobigen` CLI` tool.
In order to generate the files using Eclipse we need to follow some simple steps.
First we are going to import our basic devon4net WebAPI Project into Eclipse. to do so open Eclipse with the “eclipse-main.bat” file that can be found in the devon distribution root folder. Once we are inside of Eclipse we go to File > Open projects from file system… and, under "Directory", search for your project.
Next we copy our OpenAPI file into the root folder of the project.
And then we right click on OpenAPI file and then select CobiGen > Generate… It will display a window like this:
To select all .NET features choose CRUD devon4net Server otherwise you can select only those that interest you.
Ones you select all the files that you want to generate, click on the “Finish” button to generate all the source code.
In order to generate the files using the Cobigen` CLI` it is needed to do the following steps:
-
Go to devonfw distribution folder
-
Run console.bat, this will open a console.
-
Go to the folder you downloaded the devon4net template and your yml file.
-
Run the command:
cobigen generate {yourOpenAPIFile}.yml
-
A list of increments will be printed so that you can start the generation. It has to be selected CRUD devon4net Server increment.
At this point it is needed to make some modifications in the code in order to configure correctly the server. To do so it is needed to locate the services and the repositories files that were created in Devon4Net.WebAPI.Implementation
Services location:
Repositories location:
Now, we are going to open the following file Devon4Net.WebAPI.Implementation\Configure\DevonConfiguration.cs. In there we have to add the Dependency Injection for the services and the repositories that Cobigen has generated. The following image is an example of what is needed to add.
Moreover it is needed to remove the last line in order to be able to run the application:
throw new NotImplementedException(...);
Cobigen is generating an empty context that has to be filled with manually in order to be able to work with the database. The context can be found in [Project_Name]/Devon4Net.WebAPI.Implementation/Domain/Database/CobigenContext.cs.
In order to finish the configuration of the services it is needed to go to each service file of the managements generated.
In there we will see some "NotImplementedExceptions", so it is needed to read carefully each comment inside of each exception in order to be able to use the service. It can be shown an example of the service with its NotImplementedExceptions comments:
After doing all the steps defined above, open a terminal in path: [Project_Name]/Devon4Net.Application.WebAPI and then type:
dotnet run
This will deploy our application in our localhost with the port 8081, so when you click here (https://localhost:8082/swagger) you can see, in swagger, all the services and the data model.
You can specify the` HTTP` protocol to be used on your devon4net application modifying some node values at devonfw node in your appsettings configuration file.
In order to create a valid certificate for development purposes the Open` SSL` tools are needed.
Run the next commands in a shell:
1. openssl req -x509 -nodes -new -sha256 -days 1024 -newkey rsa:2048 -keyout RootCA.key -out RootCA.pem -subj "/C=ES/ST=Valencia/L=Valencia/O=Certificates/CN=localhost.local"
2. openssl x509 -outform pem -in RootCA.pem -out RootCA.crt
If you want to convert your certificate run the command:
openssl pkcs12 -export -out localhost.pfx -inkey RootCA.key -in RootCA.crt
Run the next commands in a shell:
1. openssl req -new -nodes -newkey rsa:2048 -keyout localhost.key -out localhost.csr -subj "/C=ES/ST=Valencia/L=Valencia/O=Certificates/CN=localhost.local"
2. openssl x509 -req -sha256 -days 1024 -in localhost.csr -CA RootCA.pem -CAkey RootCA.key -CAcreateserial -extfile domains.ext -out localhost.crt
Where the domains.ext file should contain:
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = localhost.local
DNS.3 = 127.0.0.1
DNS.4 = fake1.local
DNS.5 = fake2.local
If you want to convert your certificate run the command:
openssl pkcs12 -export -out localhost.pfx -inkey localhost.key -in localhost.crt
Add the database connection on the SetupDatabase
method at Startup.cs
private void SetupDatabase(IServiceCollection services)
{
services.SetupDatabase<TodoContext>(Configuration, "Default", WebAPI.Configuration.Enums.DatabaseType.InMemory);
}
Where:
Param | Description |
---|---|
|
Is the database context definition |
Default |
Is the connection string defined at ConnectionString node at the appsettings configuration file |
WebAPI.Configuration.Enums.DatabaseType.InMemory |
Is the database driver selection. In this case |
The supported databases are:
-
SqlServer
-
Sqlite
-
InMemory
-
Cosmos
-
PostgreSQL
-
MySql
-
MariaDb
-
FireBird
-
Oracle
-
MSAccess
In the appsettings.json configuration file, you can use the next values on the SecretKeyLengthAlgorithm and SecretKeyEncryptionAlgorithm nodes at` JWT` configuration:
Algorithm | Description |
---|---|
|
"http://www.w3.org/2001/04/xmlenc#aes128-cbc" |
|
"http://www.w3.org/2001/04/xmlenc#aes192-cbc" |
|
"http://www.w3.org/2001/04/xmlenc#aes256-cbc" |
|
"http://www.w3.org/2001/04/xmlenc#des-cbc" |
Aes128KeyWrap |
"http://www.w3.org/2001/04/xmlenc#kw-aes128" |
Aes192KeyWrap |
"http://www.w3.org/2001/04/xmlenc#kw-aes192" |
Aes256KeyWrap |
"http://www.w3.org/2001/04/xmlenc#kw-aes256" |
RsaV15KeyWrap |
"http://www.w3.org/2001/04/xmlenc#rsa-1_5" |
|
"http://www.w3.org/2001/04/xmlenc#ripemd160" |
RsaOaepKeyWrap |
"http://www.w3.org/2001/04/xmlenc#rsa-oaep" |
Aes128KW |
"A128KW" |
Aes256KW |
"A256KW" |
RsaPKCS1 |
"RSA1_5" |
RsaOAEP |
"RSA-OAEP" |
|
"http://www.w3.org/2001/10/xml-exc-c14n#" |
ExclusiveC14nWithComments |
"http://www.w3.org/2001/10/xml-exc-c14n#WithComments" |
|
"http://www.w3.org/2000/09/xmldsig#enveloped-signature" |
|
"http://www.w3.org/2001/04/xmlenc#sha256" |
|
"http://www.w3.org/2001/04/xmldsig-more#sha384" |
|
"http://www.w3.org/2001/04/xmlenc#sha512" |
Sha256 |
"SHA256" |
Sha384 |
"SHA384" |
Sha512 |
"SHA512" |
EcdsaSha256Signature |
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256" |
EcdsaSha384Signature |
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384" |
EcdsaSha512Signature |
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512" |
HmacSha256Signature |
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256" |
HmacSha384Signature |
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha384" |
HmacSha512Signature |
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha512" |
RsaSha256Signature |
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" |
RsaSha384Signature |
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha384" |
RsaSha512Signature |
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512" |
RsaSsaPssSha256Signature |
"http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1" |
RsaSsaPssSha384Signature |
"http://www.w3.org/2007/05/xmldsig-more#sha384-rsa-MGF1" |
RsaSsaPssSha512Signature |
"http://www.w3.org/2007/05/xmldsig-more#sha512-rsa-MGF1" |
|
"ES256" |
|
"ES384" |
|
"ES512" |
|
"HS256" |
|
"HS384" |
|
"HS512" |
None |
"none" |
|
"RS256" |
|
"RS384" |
|
"RS512" |
RsaSsaPssSha256 |
"PS256" |
RsaSsaPssSha384 |
"PS384" |
RsaSsaPssSha512 |
"PS512" |
Aes128CbcHmacSha256 |
"A128CBC-HS256" |
Aes192CbcHmacSha384 |
"A192CBC-HS384" |
Aes256CbcHmacSha512 |
"A256CBC-HS512" |
This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International).