Before we get into unpacking this release we’re happy to announce that we’ve now eclipsed 10 years since our
first commit!
When you’ve been working on the same piece of code or art work for a long enough time you can look through it as a time lens back to that first commit,
the developer you were, the visions, inspiration and motivations you had. Enough years pass that it eclipses a generation, remembering
the person you were then and comparing with who you are now, how we grew as developers, as people, with different skills, careers, motivations,
perspectives and life priorities.
As I take this moment to reflect back, it’s reassuring to see that whilst the environments and ecosystems around us have all changed,
ServiceStack’s code always stayed true to its time-tested original vision. Whose core focus, Services, still provides the most value and software reuse,
whose interface is still your systems most important and regardless of the technology used to implement them, its goals continue to be to
make them simple to implement and simple to consume from wherever they’re being called from - realizing their benefits.
It might be clear now but it wasn’t in the era ServiceStack was created in, where most software was being given the “Enterprise” label
and focused on surrounding itself with “Enterprise features”. In Java, Enterprise Java Beans were being marketed as the pinnacle of
Enterprise Software development whilst in .NET we were told we needed to use SOAP to in order to develop Web Services - which after experiencing
the friction they inflicted in large code-bases first-hand, became the catalyst for starting ServiceStack - a clean, friction-free Services
Framework for developing evolving message-based APIs, that even then already shipped with multiple JSON/XML serialization formats
and REST/HTTP and SOAP endpoints out-of-the-box, including generic SOAP clients to ease transitions from legacy code bases.
DTO-first Services on the outside
From the outset ServiceStack embraced and built upon the importance of the Data Transfer Object (DTO) and Remote Facade patterns for the development of its
robust coarse-grained, message-based Services, the importance of using Model-first logic-less DTOs for developing well-defined, evolvable, serialization-agnostic Services ensuring they can be consumed in any format, in any language and client - where they utilized the
Gateway pattern, a large part to why ServiceStack Services are so easily consumed from
all its reusable generic Typed Service Clients in different languages, all in the same way,
where only DTOs are needed to enable its end-to-end Typed APIs.
Even today, most generated client proxy solutions still generate their clients RPC proxy stubs coupled with their types, creating
additional friction whilst limiting its flexibility and reuse.
Clean architecture on the inside
Instead of WCF’s dependence on complicated configuration and tooling ServiceStack opted for a convention-based configuration-free approach that
in addition to adopting a simpler, cleaner more robust approach to develop Services on the outside, also encouraged a cleaner development model
on the inside where DTOs only dependency were to ServiceStack.Interfaces - a dependency and implementation-free Assembly containing abstractions of
all ServiceStack providers. This allowed the decoupling of your Host project containing all your App’s configuration and concrete dependencies with your Services
logic which would only bind to substitutable and testable interfaces so your AppHost could freely switch to use different concrete Caching,
Configuration or Logging providers without impacting your Services implementation which is decoupled from your ServiceModel DTOs, which itself has no concrete dependencies.
This same time-tested blue-print giving ServiceStack Projects a structured base to build on continues to be our recommended
Physical Project Structure that’s adopted from most of our project templates.
I’d like to take this milestone to thank our thousands of Customers we’ve had the pleasure to serve and see our Software used which has amassed
more than 27,000,000+ total downloads on NuGet and to see the uptick in adoption of some of our unique features like
Add ServiceStack Reference which has been used more than 88,000 times
to generate native Typed DTOs amongst its 8 supported languages.
It’s rewarding to see value being created with our software and the small part we’ve played in its success, we’re especially grateful to continue serving
our long term Customers who’ve supported us since ServiceStack became a permanent full-time effort in 2013 and I look forward to continue working on enhancing the
value and provide more first-class integrations around your ServiceStack Services including access to the latest modern development technologies
in the simplest and most productive way we can make it, including continuing to develop our own innovations where it can reduce friction or be able to
deliver a simpler and more enjoyable experience - many of which we’re happy to introduce to you today.
And with that we have another jam-packed release with exciting features across the board, if you haven’t got enough time to go through it all today,
feel free to jump directly to the features you’re interested in:
Another important announcement that occurred since our last release was Microsoft’s announcement that it would stop supporting
new versions of ASP.NET Core on the .NET Framework. Whilst we strongly disagreed
against this decision which would’ve put out a large class of the existing ecosystems from participating in the new ASP.NET Core
development model and many from staged migrations to .NET Core from commencing, we’re happy to see
ASP.NET Core 2.1 LTS will enjoy the same indefinite level of support
as the rest of the .NET Framework - which should come as great news to the 1/3 of our Customers who are still creating new
ASP.NET Core on FX Project Templates.
Whilst this announcement sends a clear message that new development on .NET Framework has effectively been put on product life support,
ASP.NET Core 2.1 LTS is still a great rock-solid platform to build on if you’re unable to jump directly to .NET Core immediately or
if you want to get off .NET Core’s major version release train and build upon a stable LTS platform.
ASP.NET Core - still our top recommendation for .NET Framework
If you need to stay on the .NET Framework, we’d still recommend using the newer ASP.NET Core 2.1 over classic ASP.NET System.Web projects
as it’s cleaner, lighter, more flexible and future proof. Unlike Microsoft web frameworks, ServiceStack is a single code-base which
supports running on multiple platforms so your ServiceStack Services can enjoy near perfect
source-code compatibility when and if you choose to move to .NET Core in future.
So whilst Microsoft is stopping new development of ASP.NET Core on .NET Framework, we’re not, our supported packages have standardized to
multi-target both .NET v4.5+ and .NET Standard 2.0 which is supported natively on ASP.NET Core 2.1.
ServiceStack’s multi-targeted Packages
We’ve opted early on to shun classic ASP.NET providers and use our own clean Session, Caching, Configuration, Logging
providers all of which automatically support .NET Standard 2.0 given they’re clean library implementations without .NET Framework-only dependencies.
The only features which we can’t offer .NET Standard 2.0 builds for are when they referenced external packages which don’t offer
.NET Standard 2.0 builds, the major examples being:
But otherwise all our own home-grown innovations like #Script (fka ServiceStack Templates) naturally support
.NET Framework and .NET Core and runs everywhere ServiceStack does including within classic ASP.NET MVC Controllers
which wasn’t a design goal but was a natural consequence of developing clean libraries without external dependencies or reliance on external tooling.
This is to say that ASP.NET Core 2.1 LTS is still a fantastic rock-solid platform to run your .NET Framework workloads when you need to
which will continue to receive enhancements and new features with each ServiceStack release courtesy of being derived from the same
shared code-base which will enable seamless migrations to .NET Core should you wish to in future.
Subpar experiences
It’s not always a friction-free experience as there have been frequent reports of runtime Assembly binding issues which are not always
correctly handled by NuGet package installs and may require manual binding redirects, in general upgrading to the latest .NET Framework
will mitigate these issues.
Also you’ll miss out on some niceties like the Microsoft.AspNetCore.App
meta-package reference, as a result we recommend starting from one of our ASP.NET Core Framework project Templates
which contains all the individual package references needed to get started which we’ve expanded in this release to include a
couple of exciting new project templates…
New Vue and React “lite” ASP.NET Core Templates
Developing Single Page Apps can often feel like a compromise, on the one hand premier JS frameworks like Vue and
React have offered unprecedented simplicity and elegance in developing rich and reactive Single Page Apps on the Web,
on the other hand you have to start from:
Regardless of the reasons touted for micro modules, they have many negative side-effects
where each of these dependencies opens a possible vector making your project susceptible to breaking changes, potential vulnerabilities or
potentially malicious code if any of the packages in your dependency tree becomes compromised by a bad actor. The resulting matrix
of dependencies often requires you to use complicated tools like Webpack to manage them, which itself can grow to accumulate bespoke
complex configuration to manage your projects builds which can quickly become obsolete with each new major Webpack version.
There are also packages that have shunned this trend like typescript - a wonderfully capable
Typed superset of JavaScript that assists in maintaining new class of large-scale code-bases - a clear counter that you can maintain large,
high quality code bases and build highly functional and capable libraries without micro modules.
Introducing new “lite” npm-free project templates
The question we keep asking ourselves is how can ServiceStack make modern Web Development simpler, the natural choice was to
provide pre-configured Webpack-powered SPA Project Templates - bringing the recommended SPA
development model for all popular SPA frameworks to .NET, which we’ve been doing successfully and seamlessly integrated with ServiceStack for years.
However the next leap in simplicity wont be coming from adding additional tooling to manage the complexity, it will be from
removing the underlying complexity entirely. Fortunately one of the targets all premier SPA frameworks offer are encapsulated
UMD packages so they can be referenced as a single include in online IDE’s like codepen.io but also
for simple Web Apps that want to gradually adopt these frameworks but want to avoid the complexity of maintaining an npm build system.
These UMD packages lets us return back to the simple era of web development where we can go back to referencing libraries using simple
script includes - which is the strategy embraced in ServiceStack’s new “lite” project templates.
Light on Complexity, Big on Features
Surprisingly whilst we’re able to rid ourselves of the complexity of maintaining an npm-based build system, we’re still able to
enjoy many of the features that make SPA development with Webpack a joy:
Integrated hot-reloading
Advanced JavaScript language features
Continue developing with same componentized development model as done when using Webpack
Future proofed to use optimal ES6 source code
TypeScript with runtime type-checking verification and auto-complete
Incremental compilation
TypeScript declarations are included for all default packages
Smart, effortless bundling and minification
Optimal unminified during development and minified for production
No reliance on external tooling necessary, but can use same bundling configuration in website _layout.html if pre-compilation is preferred
Essentially the “lite” templates goal are to provide the richest suite of functionality possible with the least amount of complexity.
TypeScript was adopted because it runs as a non-invasive global tool with no dependencies that enables us to take advantage of the
latest JavaScript language features to be able to develop in modern JavaScript without compromises, in the same source code as a fully-fledged
npm webpack build system, should you wish to upgrade to one in future.
Install
All ServiceStack Project Templates can now be created with our web (or app) .NET Core tool:
$ dotnet tool install -g web
If you previously had an existing web tool installed, update it to the latest version with:
Alternatively you can create an ASP.NET Core 2.1 LTS project on .NET Framework with:
$ web new react-lite-corefx ProjectName
Development workflow
All that’s needed for client development is to run TypeScript in “watch” mode:
$ tsc -w
Which monitors any changes to any .ts files and incrementally compiles their .js files on save. ServiceStack’s built-in
static files hot-reloading detects
changes to any .js files and automatically reloads the page.
For Server C# development, start your .NET Web App in a new Terminal window with:
$ dotnet watch run
Using watch run will monitor changes to C# source files and automatically re-builds and restarts the Server.
Update TypeScript DTOs
After changing your ServiceStack Services, you can re-generate their Typed TypeScript DTOs with:
$ web ts
Which will recursively update and re-generate all *dto.ts in the current and sub directories.
Integrated Bundling
The way to eliminate needing a build and module system comes down to including dependencies in dependent order which is
where ServiceStack’s new bundling APIs help with. We’ll walk through the vue-lite to see how this is easily done.
All the bundling logic for all .css and .js resources are contained within the _layout.html page below:
The bundler will include all target resources specified on the left of bundleCss using the behavior as specified in the argument options on the right:
minify - whether to minify the .css files before bundling
cache - whether to use the previous cached version if exists
disk - whether to save the output bundle to disk or in the In Memory FileSystem
bundle - whether to bundle all .css in a single file or emit include individual <link /> imports
out - virtual file path where to save the bundle (defaults to /css/bundle{.min}.css)
During development (in DebugMode) this will create an unminified bundle, ignoring any previous caches that’s saved to the In Memory Virtual File at /css/bundle.css.
Whereas in Release mode it will create a minified bundle, with all subsequent requests using the pre-bundled asset written at /css/bundle.min.css.
No tooling or pre-compilation is required prior to deployment as the bundler will automatically create one if it doesn’t already exist.
All virtual paths are from the wwwroot/WebRoot. Paths ending with a / indicate to include all .css files in that directory, which
is included in DirectoryInfo (alphabetical) order.
If for example you wanted to include your App’s default.css before bootstrap.css you can specify it first, where it will be included first
and ignored in subsequent references, e.g:
Where it will automatically reload the page if it detects any modifications to any .html, .js or .css files,
Configured with:
if(Config.DebugMode){Plugins.Add(newHotReloadFeature{DefaultPattern="*.html;*.js;*.css",VirtualFiles=VirtualFiles// Monitor ContentRoot to detect changes in /src});}
The page placeholder is where the page will be rendered inside the Layout template:
{{page}}
JavaScript Library Bundling
The layout creates 2 JavaScript bundles, the first containing all 3rd Party libraries used in the App which is written to /js/lib.bundle{.min}.js
using the same bundling options as the bundleCss above:
After importing the libraries we need to make the globals registered by the UMD dependencies available under the module name they are imported from.
When they don’t match they need to be explicitly registered in the ALIASES object:
Dependencies like vue-property-decorator.umd.js and servicestack-client.umd.js that already register themselves under their expected "vue-property-decorator" and "@servicestack/client" module names don’t need any manual mappings.
App Source Code Bundling
The last js bundle created is your App’s source code which also needs to be imported in dependent order, both vue-lite and
react-lite project templates share the same structure so their bundle configuration is identical where
/src/components contains each page defined as a separate component, the
/src/shared contains any shared functionality used by the different components whilst the base
/src folder contains your App’s entry point:
The content: prefix specifies that the virtual path is from the ContentRoot directory, in this case so your App source code is maintained outside of the wwwroot/WebRoot.
Possible values include:
web: - Web Root folder (default)
content: - Content Root folder
filesystem: - The FileSystem VFS provider in the Web Root’s cascading Virtual File Sources
memory: - The Memory VFS provider in the Web Root’s cascading Virtual File Sources
Finally the scripts argument is written (unencoded) after the library and App Source code bundles where it contains any additional scripts that
individual pages wants to include at the bottom of the page:
{{scripts|raw}}
Pre-compiled minified production bundles
Whilst not required you can copy the exact same bundling configuration in your _layout.html above into a separate
/wwwroot/_bundle.ss script:
Which will create the production bundles, minify all already non-minified bundles and write them to disk with the paths written visible in the
#Script_bundle.ss output:
The bundles created by running _bundle.ss generates more advanced compression courtesy of the web tool’s use of NUglify’s
smarter and more advanced JS, CSS and HTML minifers.
If you encounter any issues you can revert back to using ServiceStack’s built-in JSMin and CssMinifier implementations by adding these
script arguments at the top of your _bundle.css script:
Cache Breaker support is available by with the [hash] placeholder, which we only want to include in minified bundles.
In this case we need to perform a file pattern search to find and delete any existing generated bundles:
When using [hash] cache breakers the bundle APIs will use any existing generated bundles it finds, so you’ll need to
ensure that any older minified assets are removed (as done in the above script).
Available in Razor Helpers
The same Html.BundleJs(), Html.BundleCss() and Html.BundleHtml() bundling implementations as above have also been available in
ServiceStack Razor where it can be used like:
vue-lite comes pre-configured with a lot of the functionality needed in most Single Page Apps including client-side routing in
/shared/router.ts and
Sign In and
Registration pages, both of which are integrated
with ServiceStack’s declarative form validation and auto-binding.
Form Validation Example
The Sign Up Page shows a typical example of auto-form
validation with ServiceStack which can be developed using clean declarative markup:
@Component({ template:
`<div><h3>Register New User</h3><formref="form"@submit.prevent="submit":class="{ error:responseStatus, loading }"><divclass="form-group"><ErrorSummaryexcept="displayName,email,password,confirmPassword":responseStatus="responseStatus"/></div><divclass="form-group"><Inputname="displayName"v-model="displayName"placeholder="Display Name":responseStatus="responseStatus"/></div><divclass="form-group"><Inputname="email"v-model="email"placeholder="Email":responseStatus="responseStatus"/></div><divclass="form-group"><Inputtype="password"name="password"v-model="password"placeholder="Password":responseStatus="responseStatus"/></div><divclass="form-group"><Inputtype="password"name="confirmPassword"v-model="confirmPassword"placeholder="Password":responseStatus="responseStatus"/></div><divclass="form-group"><CheckBoxname="autoLogin"v-model="autoLogin":responseStatus="responseStatus">
Auto Login
</CheckBox></div><divclass="form-group"><buttonclass="btn btn-lg btn-primary"type="submit">Register</button></div><divclass="pt-3"><b>Quick Populate:</b><pclass="pt-1"><aclass="btn btn-outline-info btn-sm"href="javascript:void(0)"@click.prevent="newUser('new@user.com')">new@user.com</a></p></div></form></div>`
})
Which renders into the following Bootstrap Form UI:
All custom controls used are defined in /shared/controls.ts which
encapsulate the label and input controls and their validation error bindings within reusable Vue components.
Validation Error Binding
All validation errors are sourced from the Component’s this.responseStatus reactive property, populated by any Exception’s thrown when using the
ServiceStack’s TypeScript JsonServiceClient which in this case is used to
Register the user by sending the Register Request DTO generated in /shared/dtos.ts:
This is all it takes to render any server validation errors against their respective fields which we can test by submitting an empty form:
Vue Global State Management
Instead of immediately reaching for Vuex, we’ve kept the templates “lite” by leveraging existing
functionality built into the core libraries. So for global state management we’re using a global Vue instance as a pub/sub EventBus that
our decoupled components use to update global state and listen for events.
This is used by checkAuth to post an empty Authenticate DTO to ServiceStack to check if the user is still authenticated on the server,
which depending if they’re Authenticated will either returns basic session info or fails with a 401 error response, which the pub/sub event listeners
use to update global its state:
The react-lite template is functionality equivalent to vue-lite but created using
the latest React features. For client-side routing we use React Router’s declarative markup defined in
main.tsx.
All components are written as Functional Components and makes use of React’s new Hooks functionality
which enable functional components to retain local state. Just like vue-lite all high-level controls are encapsulated into reusable
functional components defined in /shared/controls.tsx which
ends up retaining similar markup as vue-lite despite their completely different implementations:
Despite React and Vue’s stylistic differences the ServiceStack integration remains the same where the populated Register Request DTO
in /shared/dtos.ts is used to register the User with any failures used to
populate the responseStatus local state where it’s reactively referenced in all Input components to render field validation errors against their targeted control:
React Global State Management
Likewise with global state management we’ve leveraged existing functionality instead of depending on an external state library like Redux or MobX.
Instead react-lite use React’s new useReducer hook within a global StateContext which is made available to all components using
React’s Context where they’re used to dispatch actions that mutate global state:
Unlike most other project templates which follow our Recommended Physical Project Structure, the “lite”
project templates are all within a single project as it’s more suitable for smaller projects and can be developed using lightweight IDE’s
like VS Code which doesn’t work well with multi-project solutions.
So what would’ve been separate projects are being maintained separate folders:
Where they still retain the same source code and namespaces and can be easily be moved out into a different project when wanting
to upgrade to a multi-project solution.
Updating “lite” project dependencies
We’ve also enabled a novel approach for updating your “lite” project 3rd Party dependencies where instead of everyone maintaining their
own bespoke configuration and a tool like libman for updating their local dependencies, vue-lite projects can just run:
$ web +vue-lite-lib
To update their vue-lite projects with the latest JS libraries and TypeScript definitions used in the default project template.
For react-lite projects, run:
$ web +react-lite-lib
We’ll cover how this works in more detail when we announce our web tool’s new capabilities below.
Empty MemoryVirtualFiles now registered in VirtualFileSources
To enable shadowing of the WebRoot cascading Virtual File Sources, an empty MemoryVirtualFiles has been added to
InsertVirtualFileSources by default where it gets inserted at the start of VirtualFileSources, i.e:
As we continue enhancing ServiceStack’s scripting support with exciting new features, it no longer made sense to call our dynamic scripting language
“Templates” which is just one of the many use-cases #Script enables.
#Script is typical of a popular dynamic template language you’d find in other platforms, using the ubiquitously familiar mix of
JavaScript Expressions which for increased wrist-friendly readability can be easily composed
together using the Unix | operator as embraced by Vue.js filters and
Angular’s Template Expressions
whist the Script Statement Blocks adopt the universally adopted Handlebars-like syntax that’s ideal for
rendering dynamic pages.
#Script is contained within the pure ServiceStack.Common library that as it doesn’t require any compilation or reliance on any
external build tools is embeddable within any .NET v4.5 or .NET Standard 2.0 App, even within Environments that don’t allow
Reflection.Emit thanks to the cascading implementations of Reflection Utils.
#Script is also completely customizable where all the script methods and blocks can easily be removed or shadowed and replaced to create your
own DSL language. Alternatively you can use its AST parsing APIs directly to create, parse and evaluate ASTs
from free-form JavaScript expressions.
Optimal for generating HTML and Live Scripting Environments
We’re staunch proponents for using typed languages like C# for developing compiler-checked server software but we prefer using dynamic
languages for creating UIs which are typically constantly changing, single purpose “end-user scripts” where we believe it’s more valuable to
have a flexible and highly iterative and productive workflow than be confronted with the friction and delays imposed by a static type system - that’s especially cumbersome
in text generation tasks like dynamic HTML pages. We see this as the main reason why innovative Reactive UI frameworks like React and Vue don’t work well translated in C#
where the friction and boilerplate imposed by conforming to static and generic typed structures inhibits the productivity and fast iteration that dynamic languages enjoy.
Unrestricted flexibility
The flexibility, extensibility, expendability of #Script ensures we can use it anywhere, e.g. the same UI logic and controls we use to render
dynamic HTML pages can also be re-used inside Services to render Emails and run in stand-alone scripts. It also becomes trivial to unit test
any partial fragments and functionality in isolation where the ScriptContext can easily be re-created and any functionality simulated.
As #Script is not shackled to external tooling or constrained by MVC Razor conventions it’s unrestricted with which features and capabilities
we can add to #Script - where it’s already being used to power a number of exciting scenarios.
Sharp Apps
In our last release we can see how we can use #Script to build Sharp Apps in real-time:
Sharp APIs
#Script is also the fastest way to create APIs in .NET, which can also be created in real-time without compilation where you can use page based
routing to define your API at /hello/_name/index.html (or /hello/_name.html)
that just returns a JS object literal:
{{{result:`Hello,${name}!`}|return}}
Which returns the same JSON wire-format as the equivalent ServiceStack Service:
Note: as Sharp APIs are untyped they don’t benefit from ServiceStack’s metadata features around its Typed Services
Sharp Scripts
In addition to being a versatile utility tool belt, our web (and app) .NET Core tools also serve as a #Script runner. We’ve seen a
glimpse of this with _bundle.ss script above which is run with web run {script}:
$ web run wwwroot/_bundle.ss
Sharp Scripts are run in the same context and have access to the same functionality and features as a
Sharp App including extensibility va custom plugins.
They can run stand-alone independent of an
app.settings config file, instead the app settings configuration
can be added in its page arguments to enable or configure any features.
Lets go through a couple of different possibilities we can do with scripts:
The above script generates a static HTML page can be invoked with any number of named arguments after the script name, in this case it
generates a report for Northwind Order #10643, saves it to 10643.html and opens it in the OS’s default browser:
$ web run script.html -id 10643 > 10643.html && start 10643.html
Which looks like:
textDump
Generating static .html pages can quickly produce reports that looks good enough to share with others,
but if you just want to see a snapshot info at a glance or be able to share in text-based mediums like email or chat
channels you can replace htmlDump with textDump where it will instead output GitHub flavored Markdown tables, e.g:
<!--
db sqlite
db.connection ~/../apps/northwind.sqlite
-->{{`SELECTo.Id,OrderDate,CustomerId,Freight,e.IdasEmployeeId,s.CompanyNameasShipVia,ShipAddress,ShipCity,ShipPostalCode,ShipCountryFROM"Order"oINNERJOINEmployeeeONo.EmployeeId=e.IdINNERJOINShippersONo.ShipVia=s.IdWHEREo.Id=@id`|dbSingle({id})|assignTo:order}}{{#withorder}}{{order|textDump({caption:'Order Details'})}}{{`SELECTp.ProductName,${sqlCurrency("od.UnitPrice")}UnitPrice,Quantity,DiscountFROMOrderDetailodINNERJOINProductpONod.ProductId=p.IdWHEREOrderId=@id`|dbSelect({id})|textDump({caption:"Line Items"})}}{{`SELECT${sqlCurrency("(od.UnitPrice * Quantity)")}ASOrderTotalsFROMOrderDetailodINNERJOINProductpONod.ProductId=p.IdWHEREOrderId=@idORDERBY1DESC`|dbSelect({id})|textDump({rowNumbers:false})}}{{else}}{{`ThereisnoOrderwithid:${id}`}}{{/with}}
As the output is human-readable we can view directly it without a browser:
$ web run script.ss -id 10643
Which will output:
| Order Details ||
|------------------|----------------|
| Id | 10643 |
| Order Date | 1997-08-25 |
| Customer Id | ALFKI |
| Freight | 29.46 |
| Employee Id | 6 |
| Ship Via | Speedy Express |
| Ship Address | Obere Str. 57 |
| Ship City | Berlin |
| Ship Postal Code | 12209 |
| Ship Country | Germany |Line Items
| # | Product Name | Unit Price | Quantity | Discount |
|---|-------------------|------------|----------|----------|
| 1 | Rössle Sauerkraut | $45.60 | 15 | 0.25 |
| 2 | Chartreuse verte | $18.00 | 21 | 0.25 |
| 3 | Spegesild | $12.00 | 2 | 0.25 || Order Totals |
|--------------|
| $684.00 |
| $378.00 |
| $24.00 |
And because they’re GitHub Flavored Markdown Tables they can be embedded directly in Markdown docs (like this) where it’s renders as:
Order Details
Id
10643
Order Date
1997-08-25
Customer Id
ALFKI
Freight
29.46
Employee Id
6
Ship Via
Speedy Express
Ship Address
Obere Str. 57
Ship City
Berlin
Ship Postal Code
12209
Ship Country
Germany
Line Items
#
Product Name
Unit Price
Quantity
Discount
1
Rössle Sauerkraut
$45.60
15
0.25
2
Chartreuse verte
$18.00
21
0.25
3
Spegesild
$12.00
2
0.25
Order Totals
$684.00
$378.00
$24.00
AWS Dashboards
The comprehensive built-in scripts coupled with ServiceStack’s agnostic
providers like the Virtual File System makes it easy to quickly query infrastructure resources
like all Tables and Row counts in managed AWS RDS Instances or Search for static Asset resources in S3 Buckets.
<!--
db postgres
db.connection $AWS_RDS_POSTGRES
files s3
files.config {AccessKey:$AWS_S3_ACCESS_KEY,SecretKey:$AWS_S3_SECRET_KEY,Region:us-east-1,Bucket:rockwind}
-->{{dbTableNamesWithRowCounts|textDump({caption:'Tables'})}}{{`SELECT"Id","CustomerId","EmployeeId","OrderDate"from"Order"ORDERBY"Id"DESC${sqlLimit(5)}`|dbSelect|textDump({caption:'Last 5 Orders',headerStyle:'None'})}}{{contentAllRootDirectories|map=>`${it.Name}/`|union(map(contentAllRootFiles,x=>x.Name))|textDump({caption:'Root Files and Folders'})}}{{find??'*.html'|assignTo:find}}{{find|contentFilesFind|map=>it.VirtualPath|take(15)|textDump({caption:`Filesmatching:${find}`})}}
You can use $NAME to move confidential information out of public scripts where it will be replaced with Environment
Variables. Then run the script as normal and optionally override the find pattern for files you want to search for:
$ web run script-aws.ss -find *.png
Where it displays a dashboard of activity from your AWS resources: containing all Tables with their Row Counts,
adhoc queries like your last 5 Orders, The Root files and Folders available in your S3 Bucket and any matching resources
from your find search pattern:
The nice thing about #Script late-binding and cloud agnostic providers is that with just different configuration we
can use the exact same script to query an Azure managed SQL Server Database and Azure Blob File Storage:
<!--
db sqlserver
db.connection $AZURE_SQL_CONNECTION_STRING
files azure
files.config {ConnectionString:$AZURE_BLOB_CONNECTION_STRING,ContainerName:rockwind}
-->{{dbTableNamesWithRowCounts|textDump({caption:'Tables'})}}{{`SELECT"Id","CustomerId","EmployeeId","OrderDate"from"Order"ORDERBY"Id"DESC${sqlLimit(5)}`|dbSelect|textDump({caption:'Last 5 Orders',headerStyle:'None'})}}{{contentAllRootDirectories|map=>`${it.Name}/`|union(map(contentAllRootFiles,x=>x.Name))|textDump({caption:'Root Files and Folders'})}}{{find??'*.html'|assignTo:find}}{{find|contentFilesFind|map=>it.VirtualPath|take(5)|textDump({caption:`Filesmatching:${find}`})}}
Live #Script with web watch
What’s even nicer than the fast feedback of running adhoc scripts? Is the instant feedback you get from being able to “watch” the same script!
To watch a script just replace run with watch:
$ web watch script-aws.ss -find *.png
The ability to run stand-alone adhoc scripts in an extensible dynamic scripting language feels like you’re
using a “developer enhanced” SQL Studio, where you can combine queries from multiple data sources, manipulate them with LINQ
and quickly pipe results to dump utils to combine them in the same output for instant visualization.
#Script scripts can also be easily shared, maintained in gists and run on all different Win/OSX/Linux OS’s that .NET Core runs on.
Live Transformations
Another area where “watched” scripts can shine is as a “companion scratch pad” assistant during development that you can quickly switch to
and instantly test out live code fragments, calculations and transformations, e.g. This ends up being a great way to test out markdown syntax
and Nuglify’s advanced compression using our new minifyjs and minifycssScript Blocks:
<!--
debug false
-->
Markdown:
{{#markdown}}
## Title
> quote
Paragraph with [a link](https://example.org).
{{/markdown}}
JS:
{{#minifyjs}}
function add(left, right) {
return left + right;
}
add(1, 2);
{{/minifyjs}}
CSS:
{{#minifycss}}
body {
background-color: #ffffff;
}
{{/minifycss}}
Then run with:
$ web watch livepad.ss
Which starts a live watched session that re-renders itself on save, initially with:
Markdown:
<h2 id="title">Title</h2>
<blockquote>
<p>quote</p>
</blockquote>
<p>Paragraph with <a href="https://example.org">a link</a>.</p>
JS:
function add(n,t){return n+t}add(1,2)
CSS:
body{background-color:#fff}
Live Session
Usage in .NET
To evaluate #Script in .NET you’ll first create the ScriptContext containing all functionality and features your Scripts have access to:
varcontext=newScriptContext{Args={...},// Global Arguments available to all Scripts, Pages, Partials, etcPlugins={...},// Encapsulated Features, e.g. Markdown, Protected or ServiceStack FeaturesScriptMethods={...},// Additional MethodsScriptBlocks={...},// Additional Script Blocks FilterTransformers={...},// Additional Stream TransformersScanTypes={...},// Auto register Methods, Blocks and Code Page TypesScanAssemblies={...},// Auto register all Methods, Blocks and Code Page Types in AssemblyPageFormats={...},// Additional Text Document Formats}.Init();
Then call EvaluateScript() to evaluate the script and capture it’s rendered output in a string:
stringoutput=context.EvaluateScript("The time is now: {{ now | dateFormat('HH:mm:ss') }}");
Evaluating Scripts with return values
#Script can render text as above or they can return values using the return method where it can be accessed using Evaluate():
But can also be used for more powerful conversions like converting an Object Dictionary into your preferred POCO:
varresult=context.Evaluate<Customer>("{{`select * from customer where id=@id` | dbSingle({id}) | return }}",newObjectDictionary{["id"]=1});
Optimized for .NET
To enable JS-like dynamism when binding to .NET methods, #Script automatically converts arguments for Types that don’t match.
One of the effects of this is that you can define a single method with double params:
Where it will convert all int arguments into double before executing your method.
For improved performance the Default scripts arithmetic and Math methods
avoid any numeric conversions themselves by using DynamicNumber which delegates it to use the optimal concrete arithmetic methods.
Auto Async I/O and Stream Transformations
#Script makes it easy to write composable, intent-based self-documenting code, e.g it’s clear that the expression below makes a database call
to fetch a URL from the qotd table, downloads the URL Contents, transforms its markdown contents and assigns the results to the quote argument:
How it does it becomes an implementation detail, e.g. with this naive implementation below it will make a Sync DB call then
download the entire URL contents before passing it to the markdown() method to convert it to HTML:
Where dbScalar is now an async API that returns a Task<object> which is automatically awaited before the async urlContents method
is called which makes an Async I/O HTTP Call to asynchronously write the response to the OutputStream before it’s passed to the markdownFilter Transformer which reads markdown from an async Input Stream
and returns a Stream of HTML, the rendered text output is then captured and stored in the quote string argument.
So whilst both implementations end up with the same result, they achieve it differently where no additional boilerplate is required
to enlist the more performant async streaming implementation below.
Breaking Changes
There were 2 major changes which can cause breaking changes in #Script:
ServiceStack.Script rebrand
Despite the re-branding to #Script we were able to retain most source-code compatibility where the previous “Old APIs” under ServiceStack.Templates
are now deprecated stubs
that inherit the new APIs under ServiceStack.Script. All deprecation messages contain the newer classes that you should move to.
Some classes couldn’t be duplicated, like if you were using PageResult in your Services. They now require adding:
ServiceStack Templates still uses and documents the old ServiceStack.Template APIs whilst the
new sharpscript.net is the new website for #Script which has been converted to use and document the new ServiceStack.Script APIs.
To verify minimal disruption to existing APIs, most were converted into unit tests in
BAK_CompatTemplateTests.cs.
Migration to new Script APIs
Migrating to the new APIs is fairly straight forward:
Change using ServiceStack.Templates; to using ServiceStack.Script;
Any classes with TemplatePage* has been renamed to SharpPage*
Any other class with a Template* prefix has been renamed to Script*
This change doesn’t affect any of your existing #Script source code whose existing syntax and available filters/methods remains unchanged.
New Terminology
The primary rationale for the rebranding was so we’re better able to label, describe and document all of #Script different features easier,
so when referring to Templates View Engine we’re now calling #Script Pages which is a better corollary to “Razor Pages”
which it provides an alternative to.
The collection of methods you inject in your scripts like TemplateRedisFilters and TemplateDbFilters are now referred to as “Scripts”
where they’ve been renamed to RedisScripts and DbScripts.
Request Params are no longer imported by default
A major change that will require changing existing scripts is that Request Parameters are no longer imported by default and will need to
explicitly accessed or imported.
Previously you could access the ?id=1 queryString param in your page with:
{{ id }}
This now needs to be explicitly accessed using the new query or shorter qs alias:
{{ qs.id }}
For HTTP Form Data Params use:
{{ form.id }}
importRequestParams
The least disruption to existing Pages would be to specify a white-list of arguments you want to import at the top of your page:
{{'id,name,age'|importRequestParams}}
Or if preferred, you can specify a collection of param names instead:
{{['id','name','age']|importRequestParams}}
Allow all Request Params in a page
There’s a local nuclear option that you can use to temporarily restore previous behavior in adhoc pages by calling importRequestParams
without any arguments:
{{importRequestParams}}
Which you can add at the top of adhoc pages to import all QueryString and FormData params as page arguments.
Allow all Request Params Globally
There’s also the unrecommended global nuclear option of reverting to the previous behaving and always importing all Request Params in all pages:
One message we continually try to re-iterate is the importance of Services (aka APIs) having a well-defined coarse-grained Services Contract
which serves as the interface into your system by which all external consumers bind to - making it the most important contract in your system.
Benefits of Services
This is the development model ServiceStack has always promoted and what most of its features are centered around, where your Services Contract is
defined by decoupled impl-free DTOs. If your Services retain this property then they’ll be able to encapsulate any of its capabilities of
infinite complexity and make it available remotely to all consumers with never any more complexity than the cost of a Service call:
This is where the true value of Services are derived, they’re the ultimate form of encapsulating complexity and offers the highest level
of software reuse. ServiceStack amplifies your Services capabilities by making them available in multiple Hosting Options,
serialization formats, MQ and SOAP endpoints to
enable more seamless integrations in a variety of different scenarios including native end-to-end Typed APIs for the most popular
Web, Mobile and Desktop Apps that reduce the effort and complexity required to call your Services
in all consumers - multiplicatively increasing the value provided.
API First Development Model
The practice .NET has always dictated was that you need to maintain separate controllers and logic for your HTML UIs and a different controller
for your HTTP APIs. Apart from forcing code duplication, doing this breaks your systems well-defined Service Contracts where any custom
logic in your MVC Controllers and Razor pages becomes another entry point into your system where no longer are all your system capabilities available
to all clients, some are only available when using a browser to navigate MVC pages.
Whereas in ServiceStack there are only Services, which are written with pure logic that’s unopinionated as to what clients are calling it, with
clean Request DTOs received as Inputs that typically return clean Response DTOs as outputs. HTML is then just another serialization format,
providing a View of your Services or serving as a bundled UI that works on top of your existing Services, in all cases calling the same
well tested and defined Services that all other clients use.
Validation from all the things
To better demonstrate the benefits of this approach and to show there’s no loss of flexibility, we’ve created the new
Validation .NET Core App which uses the same pure unopinionated ServiceStack Services to support
8 different HTML UI strategies including server HTML Rendered and Ajax Client forms, multiple View Engines, multiple layouts - all utilizing
the same Services and declarative Fluent Validation.
It should be noted that these are just examples of different HTML UIs, with no additional effort, all ServiceStack Services automatically
provide native integrations into all popular Mobile and Desktop Apps with Add ServiceStack Reference.
About
The Validation App covers a typical App example you’d find in most Apps, including Login and Registration Forms to Sign In and
Register new Users who are then able to access the same protected Services to maintain their own private contact lists.
It’s a compact example that tries to cover a lot of use-cases typical in a real-world App, including maintaining a separate Data and DTO Model
and using C# idioms like Enum’s for defining a finite list of options which are re-used to populate its HTML UI.
The UI for the same App is re-implemented in 8 popular Web Development approaches, each integrated with ServiceStack’s validation.
As of this writing there 4 different server HTML generated strategies that use HTML Form Posts to call back-end Services:
/vuetify - Vue App using Vuetify’s Material Design Controls using ServiceClient Requests
/client-ts - TypeScript UI using Ajax Forms and ServiceClient Requests
/client-jquery - JavaScript UI using jQuery Ajax Requests
/client-razor - Client jQuery Ajax Requests rendered by Razor pages
The source code for all different strategies are encapsulated within their folders above except for the Razor examples which need to
maintain their shared resources in the /Views folder
(representative of friction and restrictions when working with Razor).
Server Implementation
This is the shared backend Server implementation that all UIs are using:
All Auth Configuration is encapsulated within a “no-touch” IConfigureAppHost plugin that’s run once on Startup:
All Services and Validators used in this App. Extension methods are used to DRY reusable code and a Custom
Auto Mapping handles conversion between the Contact Data Model and Contact`` DTO:
Despite their respective differences they share the same concepts where all validation errors are populated from the Service’s ResponseStatus
Error Response. The UI implementations takes care of binding all matching field errors next to their respective controls whilst the
validationSummary or errorResponseExcept methods takes a list of field names that they should not display as they’ll already be
displayed next to their targeted control.
We’ll cover just the Login and Contacts Pages since they’re sufficiently different, to see what this looks like in practice:
Login Page
The Login Page contains a standard Bootstrap Username/Password form with labels, placeholders and help text, which initially looks like:
What it looks like after submitting an empty form with Server Exception Errors rendered against their respective fields:
Server UIs
All Server Examples submits a HTML Form Post and renders full page responses:
Unfortunately Validation in Bootstrap doesn’t lend itself to easy server rendering as it requires co-ordination with label, input and error feedback
elements so #Script Pages wraps this in a formInput control from BootstrapScripts to render both Label and Input elements together.
For those preferring Razor, these same controls are available as @Html Helpers as seen in Server Razor which ends up having identical
behavior and markup, albeit rendered using a different View Engine.
Server TypeScript shows a more fine-grained version where we show how to bind validation errors to your own custom HTML markup.
This would normally end up being a lot more tedious to do so we’ve extended it with our own declarative data-invalid attribute to hold the
fields error message which drastically reduces the manual binding effort required. Calling the bootstrap() method will scan the form for populated
data-invalid attributes where it’s used to render the appropriate error message adjacent to the control and toggle the appropriate error classes.
The Server jQuery version uses the exact same markup as Server TypeScript but requires a dependency on jQuery and uses the
$(document).bootstrap() jQuery plugin from ServiceStack’s built-in ss-utils.js.
Continue and ErrorView
In order to enable full-page reloads in ServiceStack’s built-in Services like its /auth and /register Services we need to submit 2 additional
hidden input fields: errorView to tell it which page it should render on failed requests and continue to tell it where to redirect to after
successful requests.
Client UIs
In contrast to full page reloads all Client UIs submit Ajax forms and bind their JSON Error Response to the UI for a more fluid and flicker-free UX:
Vuetify is a Vue App which uses the popular Vuetify Material Design UI which is in contrast to all other UIs which use Bootstrap.
It also uses the JsonServiceClient to send a JSON Authenticate Request whereas all other UIs sends HTML Form x-www-form-urlencoded Key/Value Pairs.
Client TypeScript only needs to render the initial Bootstrap Form Markup as bootstrapForm() takes care of submitting the Ajax Request and binding
any validation errors to the form. The data-validation-summary placeholder is used to render any other error summary messages except for the userName
or password fields.
Client jQuery uses the exact same markup but uses $('form').bootstrapForm() jQuery plugin to handle the form Ajax request and any error binding.
Client Razor adopts the same jQuery implementation but is rendered using MVC Razor instead of #Script Pages.
Contacts Page
The Contacts Page is representative of a more complex page that utilizes a variety of different form controls where the same page is also responsible
for rendering the list of existing contacts:
Here’s an example of what a partially submitted invalid form looks like:
Both the Contacts UIs and Contacts Services are protected resources which uses a partial to protect its pages.
Normally redirectIfNotAuthenticated wouldn’t require a URL, but one is needed here so it knows the right login page it should redirect to.
#Script Pages
In #Script Pages our wrist-friendly server controls are back as we start to see more of its features. The arguments of the left of the formInput
are for HTML attributes you want rendered on the input control whilst the arguments on the right (or 2nd argument) are to enlist the controls
other “high-level features” like values which is used to populate a list of radio and checkboxes or formSelect options. The inline
argument tells the control to render multiple controls in-line whilst you can use help to render some help text as an aside.
We also see the introduction of the sendToGateway method used to send the GetContacts Request DTO to call its Service using the
Service Gateway, the Response of which is used to render the list of contacts on the Server.
Another difference is that there are multiple <form> elements on this page to handle deleting a contact by submitting an empty form post to
/contacts//delete.
#Script Pages doesn’t need to specify its own ErrorView or Continue Request params as its the default view used for ContactServices:
[DefaultView("/server/contacts")]// Render custom HTML View for HTML RequestspublicclassContactServices:Service{...}
This is typically all that’s needed, as most real-world Apps would rarely have more than 1 HTML View per Service.
Server TypeScript
With Server TypeScript you’re starting to see the additional effort required when you need to use your own custom markup to render form controls.
It differs with #Script Pages in that instead of rendering the list of contacts on the server, it renders the GetContacts Response DTO
as JSON which is interpreted in the browser as a native JS Object literal which the render() method uses to render the list of contacts in the browser.
Deleting a contact is also handled differently where it uses the JsonServiceClient to send the DeleteContact Request DTO from the generated dtos.ts.
After the request completes it uses GetContacts to fetch an updated list of Contacts which it re-renders.
Server jQuery
Server jQuery adopts the same approach as Server TypeScript but renders it using jQuery and uses custom routes constructed on the client
with jQuery’s Ajax APIs to call the ContactServices.
Server Razor
Server Razor is very similar to #Script Pages but implemented using Razor. In many cases the built-in script methods in #Script Pages have
Razor equivalents, either in the base ViewPage<T> class like RedirectIfNotAuthenticated() or as a @Html helper.
Vuetify ends up being larger than other implementations as it also handles Edit Contacts functionality which is a separate page in other UIs.
It also includes additional functionality like client-side validation enabled in each control using its :rules attribute. One thing
that remains consistent is the way to call ServiceStack Services and handle errors by assigning it to this.responseStatus which the reactive
errorResponse method uses to bind to each control.
The remaining client implementations show that whilst the server controls require the least code, if you need custom markup it’s much easier
to render the initial markup once, then use bootstrapForm() to bind any validation errors and handle the ajax form submissions. It’s especially
valuable when you need to update a form where the same markup can be populated by just assigning the model property as done in the
Edit Contact Pages:
The amount of code can be even further reduced when using an SPA framework that allows easy componentization as seen in the
Vue Form Validation and React Form Validation examples.
“No touch” Host Configuration
There’s also a couple of new ServiceStack features that World Validation introduces, the first is that all Auth Configuration logic is encapsulated
in a single Configure.Auth.cs:
You can use this to refactor out different cohesive parts your Host configuration over multiple files and decouple them from your concrete AppHost which
ServiceStack automatically runs all IPreConfigureAppHost, IConfigureAppHost and IPostConfigureAppHost interfaces on Startup it
can find in either your AppHost Assembly or Service Assemblies specified in your AppHost constructor.
This opens up a number of re-use benefits where you’ll be able to use the same AppHost configuration if your Services are being hosted
in different Hosting Options, it makes it easy to maintain a standardized configuration
across many of your ServiceStack projects, e.g. you can easily replace Configure.Auth.cs in all your projects to ensure they’re running
the same Auth Configuration without impacting any of the projects other bespoke host configuration.
It also allows you to maintain any necessary Startup configuration that your Services implementation needs alongside the Services themselves.
E.g. This is used to register the Data.Contact to DTO ContactAuto Mapping:
// Register Custom Auto Mapping for converting Contact Data Model to Contact DTOpublicclassContactsHostConfig:IConfigureAppHost{publicvoidConfigure(IAppHostappHost)=>AutoMapping.RegisterConverter((Data.Contactfrom)=>from.ConvertTo<Contact>(skipConverters:true));}
There are 3 different Startup interfaces you can use depending on when you want your configuration to run.
Use IPreConfigureAppHost for Startup logic you want to run before the AppHost starts initialization, this is
run before AppHost.Config is initialized or Services are registered so has limited configurability but is useful
if you want to register additional Service Assemblies with ServiceStack, e.g:
We’ve added the number 1 feature request that prevented many Customers from using ServiceStack’s built-in
Auto Mapping instead of the more feature-complete AutoMapper.
Our stance was that you should use a C# Extension Method for any additional Custom Conversions that didn’t follow the intuitive mapping convention, e.g:
Which would be explicitly called when you want to convert between a Data Model and View Model:
vardto=viewModel.ToDto();
Using C# methods ensures conversion is explicit, discoverable, debuggable, fast and flexible with access to the full C# language at your disposal
whose conversion logic can be further DRY’ed behind reusable extension methods.
The problem with his is having to call this extension method manually everywhere you want this conversion to occur.
Register Converters
No More! You can now register a custom Converter mapping using the new AutoMapping.RegisterConverter() APIs, e.g:
Due to its heavy reliance in #Script and other parts in ServiceStack, the built-in Auto Mapping is a sophisticated implementation
that covers a large number of use-cases and corner cases when they can be intuitively mapped.
Another feature introduced in the Validation App is the new support for Page Based Routing in ASP.NET Core Razor
which lets you use a _ prefix to declare a variable placeholder for dynamic routes defined solely by directory and file names.
With this feature we can use a _id directory name to declare an id variable place holder:
A constant eyesore that hurts my aesthetic eye when surfing the web is how you can immediately tell that a Website is written in ASP.NET
by its /{Controller}/{Action} routing convention or .aspx suffix. This forces URL abnormalities where instead of choosing
the ideal identifier for your public resource, the path tends to adopt internal method and class names that typically makes more sense
to its developers than to external users. These dictated conventions also results in the ?queryString becoming a data bag of params
that should otherwise be hidden or included as part of its public URI identifier.
Permalinks important for SEO, usability and refactorability
In general it’s not a good idea to let a technology to dictate what your public routes end up being. Ideally your external routes
should be regarded as permalinks and decoupled from their internal implementations as you don’t want internal refactors to cause
link rot, break existing inbound navigation or lose any SEO weight they’ve accumulated.
If you adopt the ideal URL from the start, you’ll never have a reason to change it and the decoupling frees you from being able
to refactor it’s mapped implementation or even replacing the underlying technology completely as the ideal routes are already at what
they should be that’s free from any technology bias.
Pretty URLs or Clean URLs also provide important usability and accessibility benefits to
non technical users where their prominent location in browsers is a valuable opportunity to add meaningful context on where they are in your Website.
Pre-defined Routes are optimal for machines
In ServiceStack all Services are automatically available using the pre-defined routes which is optimal
for automated tooling and machinery as they can be predicted without requiring any server meta information.
Optimize Custom Routes for humans
Use Custom Routes to also make your Services available at the optimal Clean URLs for humans. For Content Pages
you can take advantage of Page Based Routing in both #Script Pages and now in Razor to specify the ideal route for your page which
in addition to requiring less effort to define (as they’re implicitly defined) they’re also less effort to implement as no Controller or Service
are needed. They also benefit from being immediately inferrible by looking at the intuitively mapped directory and file names alone which works
equally well in reverse where the page for a route will be exactly where you think it will be.
For some real-world inspiration look to github.com who are masters at it. You can tell a lot of thought went into
meticulously choosing the ideal routes they want for all of their sites functionality. This has added tremendous value to GitHub’s usability
whose intuitive routes have made deep navigation possible where you can jump directly to the page you want without always having to navigate
from their home page as needed in most websites with framework-generated routes who are more susceptible to negatively impacting user engagement
in home page redesigns that move around existing links and navigation. GitHub’s logically grouped routes also gets a natural assist
from Autocomplete in browsers who are better able to complete previously visited GitHub URLs.
web tool
Our web (and app) .NET Core tools have graduated to become a versatile invaluable companion for all ServiceStack developers.
It builds on our last v5.4 release where it served as a Sharp App
delivery platform where they can be run as a .NET Core Windows Desktop App with app or as a cross-platform Web App launcher
using web and we’ve already how it’s now a #Script runner with web run and into a
Live #Script playground with web watch.
They’ve now also gained all existing features from our @servicestack/cli npm tools
so you’ll no longer need npm to create ServiceStack projects or Add/Update ServiceStack References.
To access available features, install with:
$ dotnet tool install --global web
Or if you had a previous version installed, update with:
$ dotnet tool update -g web
Then run web without any arguments to view Usage:
$ web
Usage:
web new List available Project Templates
web new <template> <name> Create New Project From Template
web <lang> Update all ServiceStack References in directory (recursive)
web <file> Update existing ServiceStack Reference (e.g. dtos.cs)
web <lang> <url> <file> Add ServiceStack Reference and save to file name
web csharp <url> Add C# ServiceStack Reference (Alias 'cs')
web typescript <url> Add TypeScript ServiceStack Reference (Alias 'ts')
web swift <url> Add Swift ServiceStack Reference (Alias 'sw')
web java <url> Add Java ServiceStack Reference (Alias 'ja')
web kotlin <url> Add Kotlin ServiceStack Reference (Alias 'kt')
web dart <url> Add Dart ServiceStack Reference (Alias 'da')
web fsharp <url> Add F# ServiceStack Reference (Alias 'fs')
web vbnet <url> Add VB.NET ServiceStack Reference (Alias 'vb')
web tsd <url> Add TypeScript Definition ServiceStack Reference
web + Show available gists
web +<name> Write gist files locally, e.g:
web +init Create empty .NET Core 2.2 ServiceStack App
web + #<tag> Search available gists
web gist <gist-id> Write all Gist text files to current directory
web run <name>.ss Run #Script within context of AppHost (or <name>.html)
web watch <name>.ss Watch #Script within context of AppHost (or <name>.html)
web run Run Sharp App in App folder using local app.settings
web run path/app.settings Run Sharp App at folder containing specified app.settings
web list List available Sharp Apps (Alias 'l')
web gallery Open Sharp App Gallery in a Browser (Alias 'g')
web install <name> Install Sharp App (Alias 'i')
web publish Package Sharp App to /publish ready for deployment (.NET Core Required)
web publish-exe Package self-contained .exe Sharp App to /publish (.NET Core Embedded)
web shortcut Create Shortcut for Sharp App
web shortcut <name>.dll Create Shortcut for .NET Core App
dotnet tool update -g web Update to latest version
Options:
-h, --help, ? Print this message
-v, --version Print this version
-d, --debug Run in Debug mode for Development
-r, --release Run in Release mode for Production
-s, --source Change GitHub Source for App Directory
-f, --force Quiet mode, always approve, never prompt
--clean Delete downloaded caches
--verbose Display verbose logging
Add/Update ServiceStack References
This shows us we can Add a ServiceStack Reference with web <lang> <baseurl> which will let us create a TypeScript Reference
to the new World Validation App using its ts file extension alias:
$ web ts http://validation.web-app.io
Saved to: dtos.ts
Or create a C# ServiceStack Reference with:
$ web cs http://validation.web-app.io
Saved to: dtos.cs
To update run web <lang> which will recursively update all existing ServiceStack References:
$ web ts
Updated: dtos.ts
web new - .NET’s missing project template system
It’s not often that a tool causes enough friction that it ends up requiring less effort to develop a replacement than
it is to continue using the tool. But this has been our experience with maintaining our VS.NET Templates in the
ServiceStackVS VS.NET Extension which has been the biggest time sink of all our
3rd Party Integrations where the iteration time to check in a change, wait for CI build, uninstall/re-install the VS.NET extension
and create and test new projects is measured in hours not minutes. To top off the poor development experience we’ve now appeared to have
reached the limits of the number of Project Templates we can bundle in our 5MB ServiceStackVS.vsix VS.NET Extension as a
number of Customers have reported seeing VS.NET warning messages that ServiceStackVS is taking too long to load.
Given all the scenarios ServiceStack can be used in, we needed a quicker way to create, update and test our growing 47 starting project templates.
In the age of simple command-line dev tools like git and .NET Core’s light weight text/human friendly projects, maintaining and creating
new .NET project templates still feels archaic & legacy requiring packaging projects as binary blobs in NuGet packages which become stale
the moment they’re created.
GitHub powered Project Templates
Especially for SPA projects which need to be frequently updated, the existing .NET Project Templates system is a stale solution that doesn’t offer
much benefit over maintaining individual GitHub projects, which is exactly what the dotnet-new npm tool and now web new .NET Core are designed around.
Inside dotnet-new and web new is an easier way to create and share any kind of project templates which are easier for developers
to create, test, maintain and install. So if you’re looking for a simpler way to be able to create and maintain your own value-added project templates
with additional bespoke customizations, functionality, dependencies and configuration, using web new is a great way to maintain and share them.
Using GitHub for maintaining project templates yields us a lot of natural benefits:
Uses the same familiar development workflow to create and update Project Templates
Git commit history provides a public audit trail of changes
Publish new versions of project templates by creating a new GitHub release
Compare changes between Project Templates using GitHub’s compare changes viewer
Browse and Restore Previous Project Releases
End users can raise issues with individual project templates and send PR contributions
Always up to date
Importantly end users will always be able to view the latest list of project templates and create projects using the latest available version,
even if using older versions of the tools as they query GitHub’s public APIs to list all currently available projects that for installation
will use the latest published release (or master if there are no published releases), which if available, downloads, caches and
creates new projects from the latest published .zip release.
Just regular Projects
Best of all creating and testing projects are now much easier since project templates are just working projects following a simple naming convention
that when a new project is created with:
$ web new <template> ProjectName
Replaces all occurrences in all text files, file and directory names, where:
MyApp is replaced with ProjectName
my-app is replaced with project-name
My App is replaced with Project Name
The tool installer then inspects the project contents and depending on what it finds will:
Restore the .NET .sln if it exists
Install npm packages if package.json exists
Install libman packages if libman.json exists
That after installation is complete, results in newly created projects being all setup and ready to run.
Available project templates
One missing detail is how it finds which GitHub repo should be installed from the <template> name.
This can be configured with the APP_SOURCE_TEMPLATES Environment variable to configure the web tool to use your own GitHub organizations instead, e.g:
web new will then use the first GitHub Repo that matches the <template> name from all your GitHub Sources, so this
does require that all repos have unique names across all your configured GitHub Sources.
.NET Core C# Templates:
1. angular-lite-spa .NET Core 2.1 Angular 4 Material Design Lite Webpack App
2. angular-spa .NET Core 2.1 Angular 8 CLI Bootstrap App
3. aurelia-spa .NET Core 2.1 Aurelia CLI Bootstrap App
4. bare-app .NET Core 2.1 Bare Sharp Apps
5. mvc .NET Core 2.1 MVC Website
6. mvcauth .NET Core 2.2 MVC Website integrated with ServiceStack Auth
7. mvcidentity .NET Core 2.2 MVC Website integrated with ServiceStack using MVC Identity Auth
8. mvcidentityserver .NET Core 2.1 MVC Website integrated with ServiceStack using IdentityServer4 Auth
9. parcel .NET Core 2.1 Parcel TypeScript App
10. parcel-app .NET Core 2.1 Parcel Sharp Apps
11. razor .NET Core 2.1 Website with ServiceStack.Razor
12. react-lite .NET Core 2.1 simple + lite (npm-free) React SPA using TypeScript inc bundling + hot reloading
13. react-spa .NET Core 2.1 React Create App CLI Bootstrap App
14. rockwind-app .NET Core 2.1 Rockwind Sharp Apps
15. selfhost .NET Core 2.1 self-hosting Console App
16. script .NET Core 2.1 #Script Pages Bootstrap Website
17. vue-lite .NET Core 2.1 simple + lite (npm-free) Vue SPA using TypeScript inc bundling + hot reloading
18. vue-nuxt .NET Core 2.1 Nuxt.js SPA App with Bootstrap
19. vue-spa .NET Core 2.1 Vue CLI Bootstrap App
20. vuetify-nuxt .NET Core 2.1 Nuxt.js SPA App with Material Vuetify
21. vuetify-spa .NET Core 2.1 Vue CLI App with Material Vuetify
22. web .NET Core 2.1 Empty Website
.NET Framework C# Templates:
1. angular-lite-spa-netfx .NET Framework Angular 4 Material Design Lite Webpack App
2. angular-spa-netfx .NET Framework Angular 8 Bootstrap cli.angular.io App
3. aurelia-spa-netfx .NET Framework Aurelia Bootstrap Webpack App
4. mvc-netfx .NET Framework MVC Website
5. razor-netfx .NET Framework Website with ServiceStack.Razor
6. react-desktop-apps-netfx .NET Framework React Desktop Apps
7. react-spa-netfx .NET Framework React Bootstrap Webpack App
8. selfhost-netfx .NET Framework self-hosting HttpListener Console App
9. script-netfx .NET Framework #Script Pages Bootstrap WebApp
10. vue-nuxt-netfx .NET Framework Vue Nuxt.js SPA Web App
11. vue-spa-netfx .NET Framework Vue Bootstrap Webpack App
12. vuetify-nuxt-netfx .NET Framework Vuetify Material Nuxt.js SPA Web App
13. vuetify-spa-netfx .NET Framework Vuetify Material Webpack App
14. web-netfx .NET Framework Empty Website
15. winservice-netfx .NET Framework Windows Service
ASP.NET Core Framework Templates:
1. mvc-corefx .NET Framework ASP.NET Core MVC Website
2. razor-corefx .NET Framework ASP.NET Core Website with ServiceStack.Razor
3. react-lite-corefx .NET Framework ASP.NET Core lite (npm-free) React SPA using TypeScript inc bundling + hot reloading
4. selfhost-corefx .NET Framework ASP.NET Core self-hosting Console App
5. script-corefx .NET Framework ASP.NET Core #Script Pages Bootstrap Website
6. vue-lite-corefx .NET Framework ASP.NET Core lite (npm-free) Vue SPA using TypeScript inc bundling + hot reloading
7. web-corefx .NET Framework ASP.NET Core Website
web + - customize mix/match projects from gists!
Whilst we believe web new is a super simple way to create and maintain project templates, we’ve also created an even
simpler and lighter way to create projects - from gists!
We can use web + (read as “apply gist”) to create light-weight customized projects by applying multiple gists on top of each other.
One of the major benefits of this approach is that it’s not only limited at project creation time as it’s also a great way to easily add
“layered functionality” to existing projects and was the catalyst for the new “no touch” IConfigureAppHost interfaces which allows
for easy extension and replacement of isolated AppHost configuration.
We saw an example of this earlier with how we can use this to easily update dependencies in “lite” projects
which is just applying the vue-lite-lib and react-lite-lib to your existing “lite” projects:
$ web +vue-lite-lib
Usage
Similar to web other features, we get the full user experience where we can list, search and apply gists from the commands below:
Usage:
web + Show available gists
web +<name> Write gist files locally, e.g:
web + #<tag> Search available gists
web gist <gist-id> Write all Gist text files to current directory
Where we can view all available gists that we can apply to our projects with:
$ web +
Which as of this writing lists:
1. init Empty .NET Core 2.2 ServiceStack App to: . by @ServiceStack [project]
2. init-lts Empty .NET Core 2.1 LTS ServiceStack App to: . by @ServiceStack [project]
3. init-corefx Empty ASP.NET Core 2.1 LTS on .NET Framework to: . by @ServiceStack [project]
4. init-sharp-app Empty ServiceStack Sharp App to: . by @ServiceStack [project]
5. bootstrap-sharp Bootstrap + #Script Pages Starter Template to: $HOST by @ServiceStack [ui,sharp]
6. sqlserver Use OrmLite with SQL Server to: $HOST by @ServiceStack [db]
7. sqlite Use OrmLite with SQLite to: $HOST by @ServiceStack [db]
8. postgres Use OrmLite with PostgreSQL to: $HOST by @ServiceStack [db]
9. mysql Use OrmLite with MySql to: $HOST by @ServiceStack [db]
10. auth-db AuthFeature with OrmLite AuthRepository, CacheClient (requires ui,db) to: $HOST by @ServiceStack [auth]
11. auth-memory AuthFeature with Memory AuthRepository, CacheClient (requires ui) to: $HOST by @ServiceStack [auth]
12. validation-contacts Contacts Validation Example to: $HOST by @ServiceStack [example,sharp]
13. vue-lite-lib Update vue-lite projects libraries to: $HOST by @ServiceStack [lib,vue]
14. react-lite-lib Update react-lite projects libraries to: $HOST by @ServiceStack [lib,react]
15. nginx Nginx reverse proxy config for .NET Core Apps to: /etc/nginx/sites-available/ by @ServiceStack [config]
16. supervisor Supervisor config for managed execution of .NET Core Apps to: /etc/supervisor/conf.d/ by @ServiceStack [config]
17. docker Dockerfile example for .NET Core Web Apps to: . by @ServiceStack [config]
Usage: web +<name>
web +<name> <UseName>
Search: web + #<tag> Available tags: auth, config, db, example, lib, project, react, sharp, ui, vue
The way we populate this list is by extending the multi-purpose functionality of Markdown and using it as an “Executable Document”
where the human-friendly apply.md document below is also
reused as the datasource to populate the above list:
supervisor {to:'/etc/supervisor/conf.d/'} config Supervisor config for managed execution of .NET Core Apps
docker {to:'.'} config Dockerfile example for .NET Core Web Apps
Including your Gists
To include your gist in this directory and make it available to all web tool users please post a link to your gist with your preferred alias in the comments below. Your chosen alias and gist description will be shown in the web + output listing.
This self-documenting list lets you browse all available gists and their contents the same way as the web tool does.
That just like web new can be configured to use your own apply.md Gist document with:
APP_SOURCE_GISTS=<gist id>
Available Gists
As we expect to see this list of available gists expand greatly in future we’ve also included support for grouping related gists by <tag>,
e.g. you can view available starting projects with:
$ web + #project
Results matching tag [project]:
1. init Empty .NET Core 2.2 ServiceStack App to: . by @ServiceStack [project]
2. init-lts Empty .NET Core 2.1 LTS ServiceStack App to: . by @ServiceStack [project]
3. init-corefx Empty ASP.NET Core 2.1 LTS on .NET Framework to: . by @ServiceStack [project]
4. init-sharp-app Empty ServiceStack Sharp App to: . by @ServiceStack [project]
Usage: web +<name>
web +<name> <UseName>
Search: web + #<tag> Available tags: auth, config, db, example, lib, project, react, sharp, ui, vue
Which can be chained together to search for all project and sharp gists we can use for #Script Pages projects:
$ web + #project,sharp
Results matching tags [project,sharp]:
1. init Empty .NET Core 2.2 ServiceStack App to: . by @ServiceStack [project]
2. init-lts Empty .NET Core 2.1 LTS ServiceStack App to: . by @ServiceStack [project]
3. init-corefx Empty ASP.NET Core 2.1 LTS on .NET Framework to: . by @ServiceStack [project]
4. init-sharp-app Empty ServiceStack Sharp App to: . by @ServiceStack [project]
5. bootstrap-sharp Bootstrap + #Script Pages Starter Template to: $HOST by @ServiceStack [ui,sharp]
6. validation-contacts Contacts Validation Example to: $HOST by @ServiceStack [example,sharp]
Usage: web +<name>
web +<name> <UseName>
Search: web + #<tag> Available tags: auth, config, db, example, lib, project, react, sharp, ui, vue
Creating customized projects
From this list we can see that we can create an Empty .NET Core 2.2 ServiceStack App by starting in a new App Folder:
$ md ProjectName && cd ProjectName
Then applying the init labelled gist which will be saved to the '.' current directory:
$ web +init
Write files from 'init' https://gist.github.com/gistlyn/58030e271595520d87873c5df5e4c2eb to:
C:\projects\Example\ProjectName.csproj
C:\projects\Example\Program.cs
C:\projects\Example\Properties\launchSettings.json
C:\projects\Example\ServiceInterface\MyServices.cs
C:\projects\Example\ServiceModel\Hello.cs
C:\projects\Example\Startup.cs
C:\projects\Example\appsettings.Development.json
C:\projects\Example\appsettings.json
Proceed? (n/Y):
Where its output will let you inspect and verify the gist it’s writing and all the files that it will write to before accepting, by typing y or Enter.
To instead start with the latest .NET Core LTS release, run:
$ web +init-lts
After we’ve created our empty .NET Core project we can configure it to use PostgreSQL with:
$ web +postgres
Or we can give it a Bootstrap #Script Pages UI with:
$ web +bootstrap-sharp
What’s even better is that gists can be chained, so we can create a .NET Core 2.2 Bootstrap #Script Pages App using PostgreSQL with:
$ web +init+bootstrap-sharp+postgres
A Bootstrap #Script Pages App that includes a complete Contacts Validation example with:
$ web +init+bootstrap-sharp+validation-contacts
The same as above, but its Auth replaced to persist in a PostgreSQL backend:
$ web +init+bootstrap-sharp+validation-contacts+postgres+auth-db
If we decided later we wanted to switch to use SQL Server instead we can just layer it over the top of our existing App:
$ web +sqlserver
This isn’t just limited to gist projects, you can also apply gists when creating new projects:
$ web new sharp+postgres+auth-db
Which will create a script project configured to use PostgreSQL Auth.
This works despite the sharp project being a multi-project solution
thanks to the to: $HOST modifier which says to apply the gists files to the HOST project.
Apply Gist Modifiers
To enable a versatile and fine-grained solution you can use the modifiers below to control how gists are applied:
The modifiers next to each gist specify where the gist files should be written to:
{to:'.'} - Write to current directory (default)
{to:'$HOST'} - Write to host project (1st folder containing either appsettings.json,Web.config,App.config,Startup.cs)
{to:'wwwroot/'} - Write to first sub directories named wwwroot
{to:'package.json'} - Write to first directory containing package.json
{to:'/etc/nginx/sites-available/'} - Write to absolute folder
{to:'$HOME/.my-app/'} - Write to $HOME in unix or %USERPROFILE% on windows
{to:'${EnumName}/.my-app/'} - Write to Environment.SpecialFolder.{EnumName}, e.g:
{to:'$UserProfile/.my-app/'} - Write to Environment.SpecialFolder.UserProfile
File Name features
Use \ in gist file names to write files to sub directories, e.g:
wwwroot\js\script.js - Writes gist file to wwwroot/js/script.js
Use ? at end of filename to indicate optional file that should not be overridden, e.g:
wwwroot\login.html? - Only writes to wwwroot\login.html if it doesn’t already exist.
Replacement rules
Just like web new any gist file name or contents with different “MyApp” text styles will be replaced with the Project Name in that style, e.g:
MyApp will be replaced with ProjectName
my-app will be replaced with project-name
My App will be replaced with Project Name
Adding packages
To include nuget package dependencies, create a file in your gist called _init with the list of dotnet or nuget commands:
dotnet add package ServiceStack.OrmLite.Sqlite
Open for Gists!
Whilst we intend to use this feature extensively to be able to deliver “pre-set layered functionality” to ServiceStack Users, we’re
happy to maintain a curated list of gists that can help any .NET Core project as we’ve done with the config gists:
$ web + #config
Results matching tag [config]:
1. nginx by @ServiceStack Nginx reverse proxy config for .NET Core Apps to: /etc/nginx/sites-available/ [config]
2. supervisor by @ServiceStack Supervisor config for managed execution of .NET Core Apps to: /etc/supervisor/conf.d/ [config]
3. docker by @ServiceStack Dockerfile example for .NET Core Web Apps to: . [config]
To add your gist to the public list add a comment to apply.md with
a link to your gist and the modifiers you want it to use.
Apply adhoc Gists
Alternatively you can share and apply any gists by gist id or URL, e.g:
$ web gist 58030e271595520d87873c5df5e4c2eb
$ web gist https://gist.github.com/58030e271595520d87873c5df5e4c2eb
SourceLink Enabled Packages
To maximize the debuggability of ServiceStack packages all ServiceStack projects have been overhauled and converted to utilize MSBuild generated NuGet packages
where all packages are now embedding pdb symbols and have configured support for SourceLink to
improve the debugging experience of ServiceStack Apps as source files can be downloaded on-the-fly from GitHub as you debug.
Scott Hanselman has written a nice post on Source Link
and how it can be enabled inside VS.NET by turning on Enable source link support:
When enabled it should let you debug into the ServiceStack framework implementation, downloading the correct source files version from GitHub as and when needed.
All ServiceStack GitHub projects now use CI NuGet feed
In addition to switching to MSBuild generated packages all projects have also switched to using CI NuGet package feeds for external dependencies instead
of copying .dll’s in /lib folders. As a consequence you’ll no longer have to build external ServiceStack GitHub projects or use GitHub published releases,
as now the master repo of all GitHub projects can be built from a clean checkout at anytime.
The pre-release packages are still published using the same version number so if you get a build error from having a cached stale package
you’ll need to clear your local packages cache to download the latest build packages from the CI NuGet packages feed.
Authentication
For many Customers the improved Authentication support will be the most important part of this release which saw
a major focus going into enhancing Authentication integration with ASP.NET Core’s Claims based Authentication.
To best way to describe the differences between existing Identity/IdentityServer Auth Providers is that they function on
converting token inputs into ServiceStack Authenticated Sessions whereas the new