Sharp APIs
In addition to being productive high-level .NET scripting language for generating dynamic HTML pages, #Script can also be used to rapidly develop Web APIs which can take advantage of the new support for Dynamic Page Based Routes to rapidly develop data-driven JSON APIs and make them available under the ideal "pretty" URLs whilst utilizing the same Live Development workflow that doesn't need to define any C# Types or execute any builds - as all development can happen in real-time whilst the App is running, enabling the fastest way to develop Web APIs in .NET!
Dynamic Sharp APIs
The only difference between a Sharp Page that generates HTML or a Sharp Page that returns an API Response is that Sharp APIs return a value using the return method.
For comparison, to create a Hello World C# ServiceStack Service you would typically create a Request DTO, Response DTO and a Service implementation:
[Route("/hello/{Name}")]
public class Hello : IReturn<HelloResponse>
{
public string Name { get; set; }
}
public class HelloResponse
{
public string Result { get; set; }
}
public class HelloService : Service
{
public object Any(Hello request) => new HelloResponse { Result = $"Hello, {request.Name}!" };
}
/hello API Page
Usage: /hello/{name}
An API which returns the same wire response as above can be implemented in API Pages by creating a page at /hello/_name/index.html that includes the 1-liner:
Which supports the same content negotiation as a ServiceStack Service where calling it in a browser will generate a human-friendly HTML Page:
Or calling it with a JSON HTTP client containing Accept: application/json
HTTP Header or with a ?format=json
query string will
render the API response in the JSON Format:
Alternatively you can force a JSON Response by specifying the Content Type in the return arguments:
More API examples showing the versatility of this feature is contained in the new blog.web-app.io which only uses Templates and Dynamic API Pages to implement all of its functionality.
/preview API Page
Usage: /preview?content={templates}
The /preview.html API page uses this to force a plain-text response with:
The preview API above is what provides the new Blog Web App's Live Preview feature where it will render any #Script provided in the content Query String or HTTP Post Form Data, e.g:
Which renders the plain text response:
0
1
4
9
16
25
36
49
64
81
/_user/api Page
Usage: /{user}/api
The /_user/api.html API page shows an example of how easy it is to
create data-driven APIs where you can literally return the response of a parameterized SQL query using the dbSelect
filter and returning
the results:
The user argument is populated as a result of dynamic route from the _user
directory name which will let you view all
@ServiceStack posts with:
Which also benefits from ServiceStack's multiple built-in formats where the same API can be returned in:
- /ServiceStack/api?format=json
- /ServiceStack/api?format=csv
- /ServiceStack/api?format=xml
- /ServiceStack/api?format=jsv
/posts/_slug/api Page
Usage: /posts/{slug}/api
The /posts/_slug/api.html page shows an example of using the
httpResult
filter to return a custom HTTP Response where if the post with the specified slug does not exist it will return a
404 Post was not found
HTTP Response:
The httpResult
filter returns a ServiceStack HttpResult
which allows for the following customizations:
httpResult({
status: 404,
status: 'NotFound' // can also use .NET HttpStatusCode enum name
statusDescription: 'Post was not found',
response: post,
format: 'json',
contentType: 'application/json',
'X-Powered-By': '#Script',
})
Any other unknown arguments like 'X-Powered-By' are returned as HTTP Response Headers.
Returning the httpResult
above behaves similarly to customizing a HTTP response using return arguments:
Using the explicit httpResult
filter is useful for returning a custom HTTP Response without a Response Body, e.g. the New Post page
uses httpFilter
to
redirect back to the Users posts page
after they've successfully created a new Post:
Dedicated Sharp APIs
Dedicated Sharp APIs lets you specify a path where your "Sharp API" are located when registering SharpPagesFeature:
Plugins.Add(new SharpPagesFeature
{
ApiPath = "/api"
})
All pages within the /api folder are also treated like "Sharp API" for creating Web APIs where instead of writing their response to the Output Stream, their return value is serialized in the requested Content-Type using the return method:
{{ response |> return }}
{{ response |> return({ ... }) }}
{{ httpResult({ ... }) |> return }}
The route for the dedicated API page starts the same as the filename and one advantage over Dynamic Sharp APIs above is that a single Page can handle multiple requests with different routes, e.g:
/api/customers // PathArgs = [] /api/customers/1 // PathArgs = ['1'] /api/customers/by-name/Name // PathArgs = ['by-name','Name']
API Page Examples
To demonstrate Sharp APIs in action we've added Web APIs equivalents for Rockwind's customers and products HTML pages with the implementation below:
/api/customers
The entire implementation of the customers API is below:
These are some of the API's that are made available with the above implementation:
/customers API | |
All Customers |
Accept HTTP Header also supported
|
---|---|
Alfreds Futterkiste Details | |
As List | |
Customers in Germany | |
Customers in London | |
Combination Query | /api/customers?city=London&country=UK&limit=3 |
/api/products
The Products API is an example of a more complex API where data is sourced from multiple tables:
Some API examples using the above implementation:
/products API | |
All Products | |
---|---|
Chai Product Details | |
As List | |
Beverage Products | |
Bigfoot Breweries Products | |
Products containing Tofu |
Untyped APIs
As these APIs don't have a Typed Schema they don't benefit from any of ServiceStack's metadata Services, i.e. they're not listed in Metadata pages, included in Open API or have Typed APIs generated using Add ServiceStack Reference.