Introducing Post Command line features in dotnet tools

As part of our latest v5.12 release we’ve expanded our x and app dotnet tools to support being able to inspect and invoke ServiceStack APIs from the command-line with just the API name and a JS Object literal:

Post Command Docs

Ultimately we hope it becomes a valuable addition to your toolbox where can explore remote ServiceStack APIs from the command-line instead of having to refer to /metadata pages and AppHost configuration.

Here’s the Usage for inspecting a remote ServiceStack API:

Usage: x inspect <base-url>
       x inspect <base-url> <request>
       x inspect <base-url> <request> -lang <csharp|python|typescript|dart|java|kotlin|swift|fsharp|vbnet>
       x inspect <base-url> <request> -lang <cs|py|ts|da|ja|kt|sw|fs|vb>

Where inspecting the base url will provide high-level overview of a ServiceStack App, its registered content types, plugins and auth providers along with an APIs routes and Input & Output (i.e. Request/Response DTO):

$ x inspect https://techstacks.io
Base URL:           https://techstacks.io
Name:               TechStacks!
Version:            5.111

Content Types:      application/json, application/xml, application/jsv, text/html, text/jsonreport, text/csv
Plugins:            html, csv, autoroutes, metadata, ssref, httpcache, svg, sharp, auth, sitemap, cors, validation, autoquerymeta, autoquery, openapi, session
Auth Providers:     twitter (oauth), github (oauth), jwt (Bearer), servicestack (credentials)

| #   | Api                    | Routes                    | Response                       |
|-----|------------------------|---------------------------|--------------------------------|
| 1   | Ping                   | /ping                     |                                |
| 2   | GetOrganization        | GET:/orgs/{Id}            | GetOrganizationResponse        |
| 3   | GetOrganizationBySlug  | GET:/organizations/{Slug} | GetOrganizationResponse        |
| 4   | GetOrganizationMembers | GET:/orgs/{Id}/members    | GetOrganizationMembersResponse |
| 5   | GetOrganizationAdmin   | GET:/orgs/{Id}/admin      | GetOrganizationAdminResponse   |
...

You can further inspect a single API with its Request DTO Name:

$ x inspect https://techstacks.io LockTechStack

Which will output the APIs description, any tags it was annotated with, its defined routes as well as any Auth Requirements along with all the available Auth Providers registered, e.g:

# LockTechStack
Limit updates to TechStack to Owner or Admin users

Tags:               [TechStacks]
Routes:             /admin/techstacks/{TechnologyStackId}/lock

# Requires Auth
Auth Providers:     twitter (oauth), github (oauth), jwt (Bearer), servicestack (credentials)
Roles:              Admin


# C# DTOs:


using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using ServiceStack;
using ServiceStack.DataAnnotations;


public partial class LockStackResponse
{
}

///<summary>
///Limit updates to TechStack to Owner or Admin users
///</summary>
[Route("/admin/techstacks/{TechnologyStackId}/lock")]
public partial class LockTechStack
    : IReturn<LockStackResponse>, IPut
{
    [Validate("GreaterThan(0)")]
    public virtual long TechnologyStackId { get; set; }

    public virtual bool IsLocked { get; set; }
}

and then be able to invoke APIs using x send or HTTP Verb, you can view a summary usage with:

$ x send

Output:

Usage: x <send|GET|POST|PUT|DELETE|PATCH> <base-url> <request>
       x <send|GET|POST|PUT|DELETE|PATCH> <base-url> <request> {js-object}
       x <send|GET|POST|PUT|DELETE|PATCH> <base-url> <request> < body.json

Options:
 -raw                   Show raw HTTP Headers and Body
 -json                  Show Body as JSON
 -token <token>         Use JWT or API Key Bearer Token
 -basic <user:pass>     Use HTTP Basic Auth
 -authsecret <secret>   Use Admin Auth Secret
 -ss-id <session-id>    Use ss-id Session Id Cookie
 -cookies <file>        Store and Load Cookies from file

By default it will return a human-friendly output so you can view the API response with just a glance, e.g:

$ x send https://techstacks.io FindTechnologies "{Ids:[1,2,6],VendorName:'Google',Take:5,Fields:'Id,Name,VendorName,FavCount,ViewCount'}"

Output:

offset:   0
total:    18

results:
| # | id | name                   | vendorName   | viewCount | favCount |
|---|----|------------------------|--------------|-----------|----------|
| 1 |  1 | ServiceStack           | ServiceStack |      4204 |        5 |
| 2 |  2 | PostgreSQL             | PostgreSQL   |      2291 |        4 |
| 3 |  6 | AWS RDS                | Amazon       |       625 |        1 |
| 4 |  7 | AngularJS              | Google       |      5012 |        1 |
| 5 | 13 | Google Closure Library | Google       |       390 |        1 |
| 9 | 72 | Google Cloud Platform  | Google       |       269 |        1 |

When preferred you can view the raw JSON body response with -json, e.g:

$ x send https://techstacks.io FindTechnologies "{Ids:[1,2,6],VendorName:'Google',Take:10,Fields:'Id,Name,VendorName,FavCount,ViewCount'}" -json

Output:

{"offset":0,"total":18,"results":[{"id":1,"name":"ServiceStack","vendorName":"ServiceStack","viewCount":4204,"favCount":5},{"id":2,"name":"PostgreSQL","vendorName":"PostgreSQL","viewCount":2291,"favCount":4},{"id":6,"name":"AWS RDS","vendorName":"Amazon","viewCount":625,"favCount":1},{"id":7,"name":"AngularJS","vendorName":"Google","viewCount":5012,"favCount":1},{"id":13,"name":"Google Closure Library","vendorName":"Google","viewCount":390,"favCount":1}]}

or view the raw HTTP Request and Response headers with -raw, e.g:

$ x send https://techstacks.io FindTechnologies "{Ids:[1,2,6],VendorName:'Google',Take:5,Fields:'Id,Name,VendorName,FavCount,ViewCount'}" -raw

Output:

GET /json/reply/FindTechnologies?Ids=%5b1,2,6%5d&VendorName=Google&Take=5&Fields=Id,Name,VendorName,FavCount,ViewCount HTTP/1.1
Host: techstacks.io
Accept: application/json

HTTP/1.1 200 OK
Server: nginx/1.16.1
Date: Mon, 23 Aug 2021 06:46:42 GMT
Connection: keep-alive
Set-Cookie: ss-id=XUbgnfUG7gRi6ep9qpGw; path=/; secure; samesite=lax; httponly, ss-pid=lTGLyLc6QQsH2QFW5eak; expires=Fri, 23 Aug 2041 06:46:42 GMT; path=/; secure; samesite=lax; httponly
Vary: Accept
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type, Allow, Authorization
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD
Access-Control-Max-Age: 3600
X-Powered-By: ServiceStack/5.111 NetCore/Linux
Content-Type: application/json; charset=utf-8
Content-Length: 459

{"offset":0,"total":18,"results":[{"id":1,"name":"ServiceStack","vendorName":"ServiceStack","viewCount":4204,"favCount":5},{"id":2,"name":"PostgreSQL","vendorName":"PostgreSQL","viewCount":2291,"favCount":4},{"id":6,"name":"AWS RDS","vendorName":"Amazon","viewCount":625,"favCount":1},{"id":7,"name":"AngularJS","vendorName":"Google","viewCount":5012,"favCount":1},{"id":13,"name":"Google Closure Library","vendorName":"Google","viewCount":390,"favCount":1}]}

Which we expect to be very useful when debugging integration issues which we’ve historically recommended using Chrome Web Inspector, Fiddler or WireShark to be able to capture, now you can easily create a ServiceStack API Request and view the raw HTTP Headers with -raw.

A productive way to further re-iterate on the HTTP Request is to copy+paste the HTTP Request Header into JetBrains Rider’s HTTP Rest tool where you can run it and customize it using their text editor UI:

For maximum usefulness post command supports most of ServiceStack’s built-in Authentication options:

Options:
 -token <token>         Use JWT or API Key Bearer Token
 -basic <user:pass>     Use HTTP Basic Auth
 -authsecret <secret>   Use Admin Auth Secret
 -ss-id <session-id>    Use ss-id Session Id Cookie
 -cookies <file>        Store and Load Cookies from file

Please refer the Authentication section for relevant examples.

https://docs.servicestack.net/post-command#authentication

Capture HTTP Headers in Service Clients

As it’s also useful to be able to view HTTP Headers when using the .NET Service Clients we’ve also added support for capturing HTTP with the new CaptureHttp() API, e.g:

var client = new JsonServiceClient(BaseUrl);
client.CaptureHttp(print:true);

var authResponse = client.Send(new Authenticate { 
     provider = "credentials", UserName = "admin", Password = "test" });

Which will print out the raw HTTP Request & Response Headers and body to the Console, e.g:

POST /json/reply/Authenticate HTTP/1.1
Host: test.servicestack.net
Accept: application/json
User-Agent: ServiceStack .NET Client 5.121
Accept-Encoding: gzip,deflate
Content-Type: application/json

{"provider":"credentials","UserName":"admin","Password":"test"}

HTTP/1.1 200 OK
Server: nginx/1.18.0, (Ubuntu)
Date: Sat, 21 Aug 2021 09:51:34 GMT
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: ss-id=o7VAdXm7JKLy92XiQcQQ; path=/; samesite=strict; httponly, ss-pid=I2MdbrzWZILqNCOqGlyR; expires=Wed, 21 Aug 2041 09:51:34 GMT; path=/; samesite=strict; httponly, ss-opt=temp; expires=Wed, 21 Aug 2041 09:51:34 GMT; path=/; samesite=strict; httponly, X-UAId=2; expires=Wed, 21 Aug 2041 09:51:34 GMT; path=/; samesite=strict; httponly, ss-tok=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjNuLyJ9.eyJzdWIiOjIsImlhdCI6MTYyOTUzOTQ5NCwiZXhwIjoxNjMwNzQ5MDk0LCJlbWFpbCI6ImFkbWluQGdtYWlsLmNvbSIsImdpdmVuX25hbWUiOiJGaXJzdCBhZG1pbiIsImZhbWlseV9uYW1lIjoiTGFzdCBhZG1pbiIsIm5hbWUiOiJhZG1pbiBEaXNwbGF5TmFtZSIsInByZWZlcnJlZF91c2VybmFtZSI6ImFkbWluIiwicm9sZXMiOlsiQWRtaW4iXSwianRpIjoxMTR9.rHk-OdCwd8wR4AsT7exLRUr59-mzFs0FvKZUeZhvKMI; expires=Sat, 04 Sep 2021 09:51:34 GMT; path=/; samesite=strict; httponly, ss-reftok=eyJ0eXAiOiJKV1RSIiwiYWxnIjoiSFMyNTYiLCJraWQiOiIzbi8ifQ.eyJzdWIiOjIsImlhdCI6MTYyOTUzOTQ5NCwiZXhwIjoxNjYxMDc1NDk0LCJqdGkiOi02OX0.35MpYdz-QIkbVf98y_wNTA9PIYDy_EEQc3zfkpFvuQc; expires=Sun, 21 Aug 2022 09:51:34 GMT; path=/; samesite=strict; httponly
Vary: Accept
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type, Allow, Authorization, X-Args
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD
X-Powered-By: ServiceStack/5.111 NetCore/Linux
X-Cookies: ss-tok,ss-reftok
Content-Type: application/json; charset=utf-8

{"userId":"2","sessionId":"o7VAdXm7JKLy92XiQcQQ","userName":"admin","displayName":"admin DisplayName","bearerToken":"..."}

Alternatively you can log it to the debug logger with:

var client = new JsonServiceClient(BaseUrl);
client.CaptureHttp(log:true);

Or if preferred you can capture it in a StringBuilder to inspect later by disabling clearing it after each request:

var client = new JsonServiceClient(BaseUrl);
client.CaptureHttp(clear:false);

Which will begin capturing all HTTP requests made by that client in a StringBuilder you can access with:

client.HttpLog