Need a C# WebApi example of a Smartsheet callback controller method - c#

I am working on a prototype for using Smartsheets webhooks to notify an ASP.Net/WebApi endpoint when changes occur. While I understand the concepts, I have not been able to find a specific example of the structure that Smartsheets will be passing to the callback endpoint.
I suspect that it is a Json string and that I should be able to define a string parameter in the controller method to accept it like this:
public HttpStatusCodeResult Put([FromBody]string payload)
{ ... }
but I am not clear on what the parameter should be named (or even if it matters what it is named).
Can someone provide:
An example of a Smartsheets webhook callback controller method
Clarification on what the payload parameter type and name should be

I finally figured this one out. I used the Json response structure to create a similar class in C#, then used that structure as the incoming parameter type:
public class Payload
{
public int PayloadId { get; set; }
public Guid Nonce { get; set; }
public string Timestamp { get; set; }
public string WebhookId { get; set; }
public string Scope { get; set; }
public string ScopeObjectId { get; set; }
public virtual List<PayloadEvent> Events { get; set; }
}
public class PayloadEvent
{
public int PayloadEventId { get; set; }
public string ObjectType { get; set; }
public string EventType { get; set; }
[JsonProperty(PropertyName = "Id")]
public string EventId { get; set; }
public string UserId { get; set; }
public string Timestamp { get; set; }
}
Controller code:
[HttpPost]
public void Post([FromBody] Payload value)
{
...
}
It is also possible to just use dynamic as the parameter type, but that doesn't take advantage of any compiler checks downstream so I opted for more structure by defining the classes.

I can help with the structure of the Webhook callback. Once you create and verify the webhook, the events will be posted to your callback endpoint in following format:
{
"nonce": "4b2ed20d-6f00-4b0c-8fac-082182aa9aac",
"timestamp": "2015-10-27T17:04:23.795+0000",
"webhookId": 4503604829677444,
"scope": "sheet",
"scopeObjectId": 4509506114742148,
"events": [
{
"objectType": "sheet",
"eventType": "updated",
"id": 4509506114742148,
"userId": 9007194052434043,
"timestamp": "2015-10-27T17:03:15.000+0000"
},
{
"objectType": "row",
"eventType": "created",
"id": 7129509179746180,
"userId": 9007194052434043,
"timestamp": "2015-10-27T17:03:15.000+0000"
}
]
}
The events array will be in the body of the POST.

Related

.NET 5.0 options pattern using JSON array

I am building a C# .NET 5.0 application to talk to an external API for which we have multiple accounts that we can post data to. I am trying to store these details in appsettings.json file and use the options pattern to retrieve the settings.
I have the following structure in my app settings.json
"SomeApi": {
"Url": "https://api.someapi.net/v3/",
"Secret": "mysecretvlaue",
"ClientId": "myclientid",
"Accounts": [
{
"Name": "Bob",
"Username": "HisUsername",
"Password": "HisPassword",
"PostingLocation": "HisLocation"
},
{
"Name": "Fred",
"Username": "HisUsername",
"Password": "HisPassword",
"PostingLocation": "HisLocation"
}
]
},
I have a class set up like this:
public class SomeApiOptions
{
public const string SomeApi = "SomeApi";
public string Url { get; set; }
public string Secret { get; set; }
public string ClientId { get; set; }
public List<Account> Accounts { get; set; }
}
public class Account
{
string Name { get; set; }
string Username { get; set; }
string Password { get; set; }
string PostingLocation { get; set; }
}
My startup.cs has this:
services.Configure<SomeApiOptions>(Configuration.GetSection(SomeApiOptions.SomeApi));
Finally, I inject IOptions to the class where I need to use the settings like so:
Public MyClass (IOptionsSnapshot<SomeApiOptions> options)
The issue I am having is the Accounts never get populated with their respective values.
Url, Secret etc. are populated and the number of elements items in the Accounts list is correct, but all of the properties have null values.
I have looked at similar questions and tried restructuring my JSON a few ways but still end up with null values.
Any help greatly appreciated.
I see a couple of problems here.
First, none of the properties in the Account class are public so you need to change that:
Second, the Location property doesn't match the JSON which is PostingLocation. Your class should be:
public class Account
{
public string Name { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string PostingLocation { get; set; }
}

How to model a target class for receiving a JSON html response in .NET5

I'm trying to read a list of objects from a public WEB Api that provides a JSON file with an array of objects, I'm using Blazor and the Net 5 platform.
The de-serialization fails with this error:
System.Text.Json.JsonException: The JSON value could not be converted to Meme[].
I suspect I'm modeling the "receiving" object incorrectly, should I change my code or use other libraries for this code to succeed?
The Api can be found at this endpoint, I tried reading the response in these two ways:
var response = await Http.GetFromJsonAsync<Meme[]>("https://api.imgflip.com/get_memes");
and
var httpResponse = await Http.GetAsync("https://api.imgflip.com/get_memes");
var response = await httpResponse.Content.ReadFromJsonAsync<Meme[]>();
the Meme class is declared as such:
public string Id { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int BoxCount { get; set; }
and the response should contain this content:
"success": true,
"data": {
"memes": [
{
"id": "181913649",
"name": "Drake Hotline Bling",
"url": "https://i.imgflip.com/30b1gx.jpg",
"width": 1200,
"height": 1200,
"box_count": 2
},
{
...
},
... ]
}
These are the libraries I'm including:
using System.Net.Http;
using System.Net.Http.Json;
The response includes more than your Memes itself. The Meme array is within the object data and memes. Modell the entire response and you will be able to deserialize it. So you will need the following:
public class Response
{
public bool success { get; set; }
public Data data { get; set; }
}
public class Data
{
public Meme[] memes { get; set; }
}
public class Meme
{
public string id { get; set; }
public string name { get; set; }
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
public int box_count { get; set; }
}
// Now you can use that like this:
var response = await httpResponse.Content.ReadFromJsonAsync<Response>();
Note that there is a handy tool in VS that generated that for me. You can paste JSON as classes under Edit > Paste Special > Paste JSON as Classes. You can still use "normal" camel casing but you may have to instruct the serializer to do not match property names case-sensitive.

The fields that are given to the settings appear in the subclasses when serializing the object

I have classes like this.
public class Activity
{
public Guid Id { get; set }
public string Name { get; set; }
public Firm RelatedFirm { get; set; }
public string Email { get; set; }
public string Notes { get; set; }
}
public class Firm
{
public Guid Id { get; set }
public string Title { get; set; }
public string Email { get; set; }
public string Notes { get; set; }
}
After capturing the activity list, I'm sending them to a function to serialize. Within this function, serialize operation is performed according to certain parameters. One of these parameters is the fields in which I want the response to return. I want to return the Id, Name, RelatedFirm, Email and Notes fields for the activity and I want only the Id field to return to the Firm. The fields that I want to return to within the activity turn to me if they are in the firm.
This is my response;
{
"Id": "9294bc10-d8e1-4590-9703-75b773110d1c",
"Email": "q#q.com",
"RelatedFirm": {
"Id": "ebbe560b-f75d-4daf-9500-89a10487e51f",
"Email": "x#x.com",
"Notes": "87654323ıuyt43"
},
"Notes": null
}
This is also the answer I want to come;
{
"Id": "9294bc10-d8e1-4590-9703-75b773110d1c",
"Email": "q#q.com",
"RelatedFirm": {
"Id": "ebbe560b-f75d-4daf-9500-89a10487e51f"
},
"Notes": null
}
Is there any way I can stop this?

Parsing JSON data in C# received from HTTP POST with content-type application/x-www-form-urlencoded

I have a C# endpoint that is receiving and parsing an HTTP Post when content type is JSON. This works perfectly thanks to some help from stackoverflow. I mistakenly thought this was what I needed, but have since learned the content type being posted is application/x-www-form-urlencoded.
Using my existing API controller, I get Nulls for all values.
What do I need to do differently to receive the payload in this format?
Current class that works when JSON:
namespace WebServiceTest1.Models
{
public class Rootobject
{
public DateTime received { get; set; }
public string authtype { get; set; }
public string[] tags { get; set; }
public Routingresults routingResults { get; set; }
public string device_name { get; set; }
public int errorcode { get; set; }
public string source { get; set; }
public string timestamp { get; set; }
public string record_id { get; set; }
public string data { get; set; }
public int device_id { get; set; }
}
public class Routingresults
{
public int matchedRules { get; set; }
public object[] errors { get; set; }
}
}
Current Controller that works when JSON:
namespace WebServiceTest1.Controllers
{
public class Konekt1Controller : ApiController
{
public IHttpActionResult Post(Rootobject dto)
{
//Do something here.
return Ok();
}
}
}
Thanks for any help.
**Update, here is the what is posted:
FORM/POST PARAMETERS
payload: {"received": "2016-05-13T20:43:17.845873", "authtype": "otp", "tags": ["SOCKETAPI", "SIMPLESTRING", "_DEVICE_37555_"], "device_name": "test (08921)", "errorcode": 0, "source": "1111111111111", "timestamp": "301", "data": "abc123Base64 encoded", "device_id": 37555}
key:
properties: {"url": "http://requestb.in/15jclna1", "payload_is_json": true, "user_id": 2518, "hook_payload": "ALL"}
userid: 2518
I got this figured out. The way the message is coming in, it is all stored in one string. So Rather than the individual get;set; values in my original post I had to get all the values in string payload in my class object and then parse it out in the controller.
public string payload { get; set; }

JSON deserialize variable number of properties

I have a JSON class structure like this from a third party API (only the problem part shown):
"template": {
"name": "MovieTemplate",
"ruleName": "Movie Template",
"zones": {
"Products": {
"type": "Record",
"name": "Products",
"content": "www.imagescloudsite.com/blahblah.gif"
"records": [ … ]
},
"URL": {
"type":"DVD",
"name":"Bundle"
"content": "www.imagescloudsite.com/blahblah.gif"
}
}
}
The "zones" property can contain many properties "Products","URL","Superman","Descartes",etc...
But, I do not know which ones and how many will be there, because these are added by our content guys in a special control panel. Newtonsoft Deserializer complains because I have a model like this and it clearly does not capture the zone name like 'Products' and 'URL':
public class Zone
{
public string Type { get; set; }
public string Name { get; set; }
public string Content { get; set; }
}
public class Template
{
public string Name { get; set; }
public string RuleName { get; set; }
public List<Zone> Zones { get; set; }
}
Any ideas on how I can capture the zone names using NewtonSoft?
Thanks.
Turn your Zone property to a dictionary since you don't know the keys in before hand, but do know their content structure.
Like so
public class Template
{
public string Name { get; set; }
public string RuleName { get; set; }
public Dictionary<string,Zone> Zones { get; set; }
}
What if you changed the Template class to the following:
public class Template
{
public string Name { get; set; }
public string RuleName { get; set; }
public Dictionary<string, Zone> Zones { get; set; }
}
You could then access the name via the key of the entry.
Use of dynamic would be a good bet
dynamic d = Newtonsoft.Json.Linq.JObject.Parse("{number:1000, str:'string', array: [1,2,3,4,5,6]}");

Categories