How do I reference a JSON body from HTTP Post in a Sharp API?

I’m attempting to build an INSERT database Sharp API page. I’m trying to follow along with the blog post example of executing the INSERT statement, but I can’t figure out how to reference a JSON request body from within the script.

Let’s say I POSTED something like:

{
	"name":"name1",
	"value":"value1"
}

How would I reference each of those fields? I tried @name, @value, but that didn’t seem to work.

In this case the there’s no default script methods for accessing the Request Body InputStream (I’ve added some in this commit), so you’ll need to add your own Script Methods in your Plugin.dll where they’ll automatically get registered when the plugin is loaded, e.g. you can access it with something like:

public class MyScripts : ScriptMethods
{
    internal IRequest req(ScriptScopeContext scope) => 
        scope.GetValue(ScriptConstants.Request) as IRequest;

    public async Task<object> requestBodyAsJson(ScriptScopeContext scope) => 
        JSON.parse(await req(scope).GetRawBodyAsync());
}

Which you should be able to access by assigning it to a variable which will let you use the resulting Object Dictionary, e.g:

{{ requestBodyAsJson |> to => dto }}
{{ dto.name }} / {{ dto.value }}

As you’re going to be developing Sharp Apps you’ll need to familiarize yourself with the Scripts API Reference listing all the built-in Script Methods available.

Alternative Solution use HTML Form POST

Alternative solutions is to use the standard HTTP POST variables as submitted with all HTML Form Posts, you can find the form* script methods by searching the API Reference for form, where the POST’ed Form key/value pairs is available from form, formDictionary, formQuery(field) and formQueryValues(field) APIs. The formQuery* APIs looks for the field in both QueryString & Form.

Use Scripting APIs to inspect objects

Finally the Scripting .NET APIs lets you use reflection to access an objects methods/fields/properties which you could use to inspect the httpRequest object, but it’s typically easier & more reusable to use C# to access them in your own Script Methods.

Learn about existing Script Methods

To learn about what each Script Method does & learn about how to go about creating your own have a look at the source code for the default scripts in ServiceStack.Common/Script/Methods with the scripts around HTTP Request APIs in DefaultScripts.Web.cs and the higher-level ServiceStack Related APIs in ServiceStackScripts.cs.

Great info! Let me digest this and see if I can come up with something. I’ll let you know.

I added the MyScripts class to the plugin library but this doesn’t seem to be working:

{{ requestBodyAsJson |> to => dto }}
{{
         `SELECT *, 1 as TestField
              FROM test_model WHERE name = '{dto.value}'`
           | dbSelect()
           | return
}}

It’s not returning the record which returns when I hard code it like:

{{
         `SELECT *, 1 as TestField
              FROM test_model WHERE name = 'value1'`
           | dbSelect()
           | return
}}

Did you dump the dto {{ dto |> dump}} or have a look at the SQL string that was generated? Because your string interpolation is wrong (uses JS template literals), something that should’ve been clear after some inspection.

Also to protect yourself from SQL Injection you should be using parameterized values instead, e.g:

 `SELECT *, 1 as TestField FROM test_model WHERE name = @value`
       |> dbSelect(dto)
       |> return

Please also start using the |> pipeline operator syntax that #Script is moving to.

Doesn’t seem to be doing anything. I’m suspecting the requestBodyAsJson in my custom MyScripts isn’t getting triggered. What am I missing? I know the plugin dll itself is working because it contains that ChangeDbFeature we worked on that other day and that IS working.

have a look at the SQL string that was generated

How would I do that?

Also, did I need to update my ServiceStack references? You mentioned you added this to a commit but I’m not sure if that meant it was actually released yet?

This is what my Plugin looks like. Maybe I’m missing some code to wire up the MyScripts?

using ServiceStack;
using ServiceStack.Script;
using ServiceStack.Web;
using System.Threading.Tasks;

namespace MyScriptPlugin2
{
    public class ChangeDbFeature : IPlugin
    {
        public void Register(IAppHost appHost)
        {
            appHost.PreRequestFilters.Add((req, res) => {
                var db = req.QueryString["db"];
                if (db == null) return;
                req.Items[Keywords.DbInfo] = new ConnectionInfo
                {
                    ConnectionString = $"Server=localhost;User Id=postgres;Password=admin;Database={db};Pooling=true;MinPoolSize=0;MaxPoolSize=200",
                    //ProviderName = "postgres"
                };               
            });
        }
    }

    public class MyScripts : ScriptMethods
    {
        internal IRequest req(ScriptScopeContext scope) =>
            scope.GetValue(ScriptConstants.Request) as IRequest;

        public async Task<object> requestBodyAsJson(ScriptScopeContext scope) =>
            JSON.parse(await req(scope).GetRawBodyAsync());
    }
}

This isn’t helpful, what exactly is it printing out, what does {{ dto |> typeName }} return? Did you try debugging it in your Test App with your plugin, is your requestBodyAsJson method getting called, are you able to use any of your other custom script methods?

The x tool and MyGet have been updated, dotnet tool -g update x will tell you if it’s updated to a newer version, you can find out what script methods by creating a simple script:

$ echo "scriptMethods |> map => it.signature |> joinln |> raw" > methods.sc

Then running it:

$ x run methods.sc

Which will dump a list of built-in script method signatures.

There’s also an interactive lisp interpreter you can use to query the methods that are available, the start of this youtube video shows some examples, e.g:

$ x lisp
> (globln "request*" (map .Signature scriptMethods))

If it’s updated in the x tool it’s likely also updated on MyGet, update the MyGet pre-release packages to check in your test app, if the new API exists on sharpscript.net it’s verified that it’s on MyGet.

Output the SQL, instead of running it?

{{ var sql = `SELECT * FROM test_model WHERE name = ${dto.value}` }}
{{ sql }}

null

What does this return?

{{ requestBodyAsString |> to => json }}
json '{{ json }}'

Uses new API in x tool, need to update to 0.0.24

How are you posting the JSON to the page? can you provide the raw HTTP Request Header.

I updated the the x tool.

It’s returning:

json ‘’

I’m posting the JSON to the page via Postman raw JSON body.

Raw body from Visual Studio debug in the Plugin:

?req.GetRawBody()
"{\n\t\"name\":\"name1\",\n\t\"value\":\"value1\"\n}"

If GetRawBody() works then you can use that instead, e.g:

public class MyScripts : ScriptMethods
{
    internal IRequest req(ScriptScopeContext scope) =>
        scope.GetValue(ScriptConstants.Request) as IRequest;

    public string rawBodyAsString(ScriptScopeContext scope) =>
        req(scope).GetRawBody();

    public object rawBodyAsJson(ScriptScopeContext scope) =>
        JSON.parse(rawBodyAsString(scope));
}

Still not returning anything. That ?req.GetRawBody() I dumped out earlier was from the ChangeDbFeature code block, not the MyScripts code block. I’m suspecting the MyScripts code block is not running. It also seems that the commit and subsequent update you made to the API with requestBodyAsJson isn’t working.

ok the request body needed buffering, it will be empty after the body has been accessed which I’ve added in this commit.

The x tool has also been updated to 0.0.25 with this change, NuGet takes a while (up to 20mins) to index new packages before dotnet tool update -g x will update to the new package.

This is what I’m using to test it, test.html:

{{ requestBodyAsString |> to => strBody }}
{{ requestBodyAsJson |> to => jsonBody }}
{{ rawBodyAsString |> to => rawStrBody }}
{{ rawBodyAsJson |> to => rawJsonBody }}

requestBodyAsString: '{{strBody |> raw}}'
requestBodyAsJson: '{{jsonBody |> dump |> raw}}'
rawBodyAsString: '{{rawStrBody |> raw}}'
rawBodyAsJson: '{{rawJsonBody |> dump |> raw}}'

Which I’ll test in F12 Web Developer Console, e.g:

await (await fetch("/test", { 
    method:"POST", 
    body:`{"A":1}`, 
    headers: { 'content-type': 'application/json' } })
).text()

Which now returns:

requestBodyAsString: '{"A":1}'
requestBodyAsJson: '{
	A: 1
}'
rawBodyAsString: '{"A":1}'
rawBodyAsJson: '{
	A: 1
}'

Can you test that you can use any custom script method, e.g:

public class MyScripts : ScriptMethods
{
    public string hi(string name) => $"Hi {name}";
}

Then:

{{ 'world' |> hi }}

Wasn’t able to run via F12 because it was complaining about CORS, but running this from Postman I get:

requestBodyAsString: ''
requestBodyAsJson: ''
rawBodyAsString: ''
rawBodyAsJson: ''

I ran dotnet tool update -g x and it updated to version 0.0.25 so it should contain the latest code.

This doesn’t appear to be working either. I put the following just to check I’m getting something:

start
{{ 'world' |> hi }}
end

and I get:

start

end

This is the exact steps I’m doing to test this starting from scratch:

$ md test
$ cd test
$ echo "debug = true" |> app.settings
$ echo "json '{{ rawBodyAsString |> raw }}'" > test.html
$ x

Use Chrome to go to https://localhost:5001/test

Press F12 to open Web Inspector, paste in above snippet:

await (await fetch("/test", { 
    method:"POST", 
    body:`{"A":1}`, 
    headers: { 'content-type': 'application/json' } })
).text()

Which returns:

image

You’re not getting the same result?

Seems the difference may be running the site with x vs web. Works with x, not with web. What’s the difference? I need to run it with web because I will be running a full .net core web app such as with your x new script template.

What, you’ve been running web this whole time?

web is the old .NET Core 2.1 dotnet tool x is what you should be using.