SDK for HTTP and gRPC services.
Specify HOSTNAME
environment variable on your local machine to distinguish your local logs from the rest of the logs in the centralized Seq instance.
If HOSTNAME
is empty, OS user name will be used instead.
Specify SeqUrl
with Seq url in the json settings or in the environment variables to forward logs to the Seq.
Specify ConsoleOutputLogLevel
with Error
or Warning
in the json settings or in the environment variables to disable standart output in console.
Specify RemoteSettingsReadTimeout
as a TimeSpan to override default remote settings reading time-out. Default timeout is 5 seconds.
Specify RemoteSettingsRequired
with true
to make remote settings mandatory. By default the remote settings are optional.
Specify ElasticsearchLogs.IndexPrefixName
with the index name preffix in the json settings or in the environment variables to use specific index name Elasticsearch. By default IndexPrefixName = log
.
Specify ElasticsearchLogs.NodeUrls
with the URL addresses of Elasticsearch nodes in the json settings or in the environment variables to write logs to Elasticsearch.
{
"ElasticsearchLogs": {
"NodeUrls": ["http://elasticsearch-1.elk-logs:9200", "http://elasticsearch-2.elk-logs:9200", "http://elasticsearch-3.elk-logs:9200"],
"IndexPrefixName": "logs"
}
}
ElasticsearchLogs__NodeUrls__1 = "http://elasticsearch-1.elk-logs:9200"
ElasticsearchLogs__NodeUrls__2 = "http://elasticsearch-2.elk-logs:9200"
ElasticsearchLogs__NodeUrls__3 = "http://elasticsearch-3.elk-logs:9200"
Specify Serilog.minimumLevel
with Serilog config in the json settings or in the environment variables to manage log level.
{
"Serilog": {
"minimumLevel": {
"default": "Information",
"override": {
"Microsoft": "Warning",
"System": "Warning"
}
}
}
}
Verbose
: Verbose is the noisiest level, rarely (if ever) enabled for a production app.
Debug
: Debug is used for internal system events that are not necessarily observable from the outside, but useful when determining how something happened.
Information
: Information events describe things happening in the system that correspond to its responsibilities and functions. Generally these are the observable actions the system can perform.
Warning
: When service is degraded, endangered, or may be behaving outside of its expected parameters, Warning level events are used.
Error
: When functionality is unavailable or expectations broken, an Error event is used.
Fatal
: The most critical level, Fatal events demand immediate attention.
For all unhandled exceptions, not-succesfull (x < 200 and x >= 300) HTTP status codes, and request validation violations SDK produces a error response like:
{
"errors": {
"": [
"Summary error message"
],
"requestField": [
"Error related to the particular requestField"
]
}
}
There is action filter ErrorResponseActionFilter
and middlware UnhandledExceptionsMiddleware
which handles this. Bseides error response formatting ErrorResponseActionFilter
validates
request model so you don't need to do if(!ModelState.IsValid) return BadRequest(ModelState);
in each action.
For the not-successfull HTTP status codes error response is formatted from the ModelState
so, all you need to response an error is fill ModelState
using ModelState.AddModelError()
.
Since ErrorResponseActionFilter
overrides error response returned from a controller action, it is not needed to pass ModelState
to BadRequest()
, NotFound()
and other methods which
return IActionResult
- just fill ModelState
and return not-successful HTTP status code from the action.
If you want to add JWT-based Bearer authentication for HTTP endpoints in your service, add AddJwtAuth
call to your Startup
constructor:
public class Startup : SwisschainStartup<AppConfig>
{
public Startup(IConfiguration config) : base(config)
{
AddJwtAuth(Config.Auth.JwtSecret, "exchange.swisschain.io");
}
}
If your JWT token contains tenant-id
claim, you can use GetTenantId
extension method to get current tenant ID of the HTTP request in your controllers:
[Route("api/who-am-i")]
public class WhoAmIController : ControllerBase
{
[HttpGet)]
public string Get()
{
var tenantId = User.GetTenantId();
return tenantId;
}
}
Your JWT token SHOULD containt:
exp
claim RFC-7519aud
claim RFC-7519. JWT token should contain an audience specified in theAddJwtAuth
call. It can contain an array of the audience still one of which is required by your service.
For the developing and debugging purposes you can get JWT token using jwt.io. Just put all required claims to the payload and the same secret as you've specified in
AddJwtAuth
call in your service.
If you want to add scope-based authorization for HTTP endpoints in your service, add services.AddScopeBasedAuthorization
call to your Startup.ConfigureServicesExt
:
public class Startup : SwisschainStartup<AppConfig>
{
...
public override void ConfigureServicesExt(IServiceCollection services)
{
services.AddScopeBasedAuthorization();
}
}
Then you can use Authorize
attribute on your controllers and action-methods to require scope for requests execution:
[Route("api/orders")]
public class OrdersController : ControllerBase
{
[HttpGet)]
[Authorize("exchange.swisschain.io/orders:get")]
public async Task<IActionResult> Get()
{
...
}
[HttpGet)]
[Authorize("exchange.swisschain.io/orders:add")]
public async Task<IActionResult> Add()
{
...
}
}
Your JWT token should contain the scope (rfc8693) specified on your controller or action method. You can use Authorize
attribute without any scope still just to require the request to be authorized.
<api-name>.swisschain.io/<resources>:<operation>
. You can use as granular scopes for your service as you need. For instance, you can omit operation, resource or event api-name, if you don't need it and make scopes more granular later on.