I am trying to incorporate a slack notification using a Slack bot app API into my C# application. The code below is working fine but the format used for the attachments field makes it very difficult to edit and maintain... There must be an easier way to populate that json array?
I've tried multiple ways to write it but I can't get it to work properly other than with this unwieldy syntax.
var data = new NameValueCollection
{
["token"] = "token", // Removed my actual token from here obviously
["channel"] = "channel", // Same with the channel
["as_user"] = "true",
["text"] = "test message 2",
["attachments"] = "[{\"fallback\":\"dummy\", \"text\":\"this is an attachment\", \"color\":\"#F35A00\", \"title\" : \"Title\", \"title_link\": \"http://www.google.com\"}]"
};
var client = new WebClient();
var response = client.UploadValues("https://slack.com/api/chat.postMessage", "POST", data);
The "unwieldy" syntax is hand-crafted JSON and a much better approach would be to construct the attachments as C# objects and then convert them into JSON as the API requires.
My example is using the external library Json.NET for the JSON conversion.
Example for C# object:
// a slack message attachment
public class SlackAttachment
{
public string fallback { get; set; }
public string text { get; set; }
public string image_url { get; set; }
public string color { get; set; }
}
Example for creating a new attachments array:
var attachments = new SlackAttachment[]
{
new SlackAttachment
{
fallback = "this did not work",
text = "This is attachment 1",
color = "good"
},
new SlackAttachment
{
fallback = "this did not work",
text = "This is attachment 2",
color = "danger"
}
};
Finally, converting the attachments array to JSON for the API:
var attachmentsJson = JsonConvert.SerializeObject(attachments);
See also this answer for a complete example.
Related
I'm new in c# and want too call the bank web service,in bank document write this:
call the BatchBillPaymentRequest method with ClientBatchBillPaymentRequestData object for input argumant :
BatchBillPaymentRequest(ClientBatchBillPaymentRequestData data)
for that purpose write this class:
class ClientBatchBillPaymentRequestData
{
public string CallbackUrl { get; set; }
public string LoginAccount { get; set; }
public int OrderId { get; set; }
public string BillId { get; set; }
public string PayId { get; set; }
public string AdditionalData { get; set; }
}
and write this code:
payment.BillService behzad=new BillService();
ClientBatchBillPaymentRequestData datas = new ClientBatchBillPaymentRequestData();
datas.BillId = "1233";
datas.CallbackUrl = "google.com";
datas.LoginAccount = "213214";
datas.OrderId = 123;
datas.PayId = "2131243";
datas.AdditionalData = "23213";
behzad.BatchBillPaymentRequest(datas);
but in this line:
behzad.BatchBillPaymentRequest(datas);
get this error:
Severity Code Description Project File Line Suppression State
Error CS1503 Argument 1: cannot convert from 'ConsoleApplication1.ClientBatchBillPaymentRequestData' to 'ConsoleApplication1.payment.ClientBatchBillPaymentRequestData' ConsoleApplication1 L:\TEMP\ConsoleApplication1\ConsoleApplication1\Program.cs 23 Active
what happen?how can i solve that problem?thanks.
VS add this class too project:
public ClientBatchBillPaymentResponseData BatchBillPaymentRequest(ClientBatchBillPaymentRequestData requestData) {
object[] results = this.Invoke("BatchBillPaymentRequest", new object[] {
requestData});
return ((ClientBatchBillPaymentResponseData)(results[0]));
}
As per Adriano Repetti's comment - when you added the web service reference Visual Studio created a class for you to use - so change this code from this
payment.BillService behzad=new BillService();
ClientBatchBillPaymentRequestData datas = new ClientBatchBillPaymentRequestData();
datas.BillId = "1233";
datas.CallbackUrl = "google.com";
datas.LoginAccount = "213214";
datas.OrderId = 123;
datas.PayId = "2131243";
datas.AdditionalData = "23213";
behzad.BatchBillPaymentRequest(datas);
to this
payment.BillService behzad=new BillService();
var datas = new ConsoleApplication1.payment.ClientBatchBillPaymentRequestData();
datas.BillId = "1233";
datas.CallbackUrl = "google.com";
datas.LoginAccount = "213214";
datas.OrderId = 123;
datas.PayId = "2131243";
datas.AdditionalData = "23213";
behzad.BatchBillPaymentRequest(datas);
You can streamline this a little by doing this instead too
payment.BillService behzad=new BillService();
var datas = new ConsoleApplication1.payment.ClientBatchBillPaymentRequestData{
BillId = "1233",
CallbackUrl = "google.com",
LoginAccount = "213214",
OrderId = 123,
PayId = "2131243",
AdditionalData = "23213"
}
behzad.BatchBillPaymentRequest(datas);
You should also remove the ClientBatchBillPaymentRequestData class you created as its surplus to requirements.
For each of the Attributes in your object instantiation, highlight it and press CTRL-Space to get autocomplete to correctly case your parameter name.
If you don't know the structure of your Webservice call you can use Wizdler for Chrome https://chrome.google.com/webstore/detail/wizdler/oebpmncolmhiapingjaagmapififiakb?hl=en to find out what the packets should be .
Once you install it, enter the WSDL URL in chrome, click the wizdler button to the right of the address bar and select the method you want to call, this will let you see the parameters (including their names and casing).
I am working on building an app that accepts orgunitid and creates a new section associated with orgunitid. I'm using c#.
Here is my code.
string orgUnitId = textBoxOrgUnitId.Text;
string sectionCreateRoute = "/d2l/api/lp/1.0/" + orgUnitId + "/sections/";
var client = new RestClient(host);
var valenceAuthenticator = new D2L.Extensibility.AuthSdk.Restsharp.ValenceAuthenticator(userContext);
var requestCreateSection = new RestRequest(sectionCreateRoute, Method.POST);
valenceAuthenticator.Authenticate(client, requestCreateSection);
And, the JSON data I should provide will look like this.
{
"Name": "Test Section",
"Code": "" ,
"Description": { "Content": "Test", "Type" : "HTML" }
}
How can I create a new section with this JSON data.
Thanks,
Phillip
I've tried this code, but it still does not create a section.
string orgUnitId = textBoxOrgUnitId.Text;
string sectionCreateRoute = "/d2l/api/lp/1.0/" + orgUnitId + "/sections/";
var client = new RestClient(host);
var valenceAuthenticator = new D2L.Extensibility.AuthSdk.Restsharp.ValenceAuthenticator(userContext);
var requestCreateSection = new RestRequest(sectionCreateRoute, Method.POST);
requestCreateSection.AddJsonBody(new
{
Name = "Section Test",
Code = "156156",
Description = new { Content = "Test", Type = "Html" }
});
valenceAuthenticator.Authenticate(client, requestCreateSection);
Because you're using ResSharp I would recommend calling the following method;
public IRestRequest AddJsonBody(object obj)
{
this.RequestFormat = DataFormat.Json;
return this.AddBody(obj, "");
}
However in order to use this you need a C# object to represent that data. The class definitions would look something like this;
public class NewSectionPostBody
{
public string Name;
public string Code;
public SectionContentType Description;
}
public class SectionContentType
{
public string Content;
public string Type;
}
With these and your existing code I can do the following;
var requestCreateSection = new RestRequest(sectionCreateRoute, Method.POST);
// use object initializer to make instance for body inline
requestCreateSection.AddJsonBody(new NewSectionPostBody{
Name="Test Section",
Code=String.Empty,
Description= new SectionContentType { Content="Test", Type="HTLM" }
});
valenceAuthenticator.Authenticate(client, requestCreateSection);
RestSharp handles the object to json string serialization so you can basically just pass any object into that method and the resulting json will be used as the post body.
One last thing; if this is a one off request you don't even need to define the types I used for the body. You can just use the same inline initilization constructs to make an anonymous type if you were to just remove the classnames ie;
requestCreateSection.AddJsonBody(new {
Name="Test Section",
Code=String.Empty,
Description= new { Content="Test", Type="HTLM" }
});
^^ Rather than instantiating user defined class types I use anonymous types. If you're not able to reuse the types which make up the post body, this is a more efficient way of setting up the request.
I have a problem with my web api application. I get an internal error 500 when i try to post(save) a new user in my db.
The function bellow is the one that i use to make the client call.
public void InsertNewUser(RegisterModel pNewUser, string pEmail)
{
// Build rest uri
string lREST_Uri_Browse = string.Format(#"api/accountapi/saveuserdata"
// User data
/*pModelSerialized*/);
// Complete URI
string lREST_Uri = Helpers_API.endPoint + lREST_Uri_Browse;
var client = new HttpClient();
client.BaseAddress = new Uri(Helpers_API.endPoint);
var newUser = new Models.Models_API.Users
{
Email = pNewUser.Email,
FName = pNewUser.FName,
LName = pNewUser.LName,
Inserted = DateTime.Now,
ActiveAcc = true,
AccType = pNewUser.AccType,
BCompanyID = pNewUser.CompanyID,
PID = pNewUser.PID,
Password = pNewUser.Password,
Token = GetToken(pEmail),
ThirdParty = 0,
Gender = pNewUser.Gender,
BirthDate = pNewUser.BirthDate
};
// Add an Accept header for JSON format.
//client.DefaultRequestHeaders.Accept.Add(
// new MediaTypeWithQualityHeaderValue("application/json"));
// Create the JSON formatter.
MediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();
// Use the JSON formatter to create the content of the request body.
HttpContent content = new ObjectContent<Models.Models_API.Users>(newUser, jsonFormatter);
var result = client.PostAsync(lREST_Uri_Browse, content).Result;
}
This is the model
public class Users
{
public int BrokerID { get; set; }
public DateTime Inserted { get; set; }
public string Email { get; set; }
public string FName { get; set; }
public string LName { get; set; }
public bool ActiveAcc { get; set; }
public int BCompanyID { get; set; }
public int PID { get; set; }
public int AccType { get; set; }
public string Password { get; set; }
public string Token { get; set; }
public int Gender { get; set; }
public DateTime BirthDate { get; set; }
public int ThirdParty { get; set; }
}
And bellow is the POST in APIController:
public HttpResponseMessage SaveUserData(Users pNewUser)
{
bool createUser = false;
// First check for provided email in DB
Users existingUser = asigCtx.Users.Where(u => u.Email == pNewUser.Email).SingleOrDefault();
if (existingUser == null)
createUser = true;
else if (existingUser.ActiveAcc)
createUser = true;
if (createUser)
{
using (asigCtx = new AsigPrimeContext())
{
Users user = new Users()
{
Email = pNewUser.Email,
FName = pNewUser.FName,
LName = pNewUser.LName,
Inserted = DateTime.Now,
ActiveAcc = true,
AccType = pNewUser.AccType,
BCompanyID = pNewUser.BCompanyID,
PID = pNewUser.PID,
Password = pNewUser.Password,
Token = pNewUser.Token,
ThirdParty = 0,
Gender = pNewUser.Gender,
BirthDate = pNewUser.BirthDate,
};
asigCtx.Users.Add(user);
asigCtx.SaveChanges();
}
}
var response = Request.CreateResponse<Users>(HttpStatusCode.Created, pNewUser);
return response;
}
Can anyone give me piece of advice with this code because i'm new in this and i just want to do it wright. TNX
You have an error in your code. A 500 error indicates that your code contains an unhandled exception that killed its worker process.
Change your web.config file so that your application outputs the full error message.
A few things I would check/try to get to the bottom of the issue:
Is the code above exactly the same as in your application or have you changed anything (even if only to make it simpler)?
Is the Users object used in SaveUserData controller method definitely from the same assembly as the one that you are posting from the InsertNewUser method?
Is the Users object complete on the listing (e.g. does it have any constructors)?
Have you tried executing the request to the endpoint through fiddler? This way you take any potential bugs in the client call out of the equation to see if the controller method on its own works.
I've noticed this line:
string lREST_Uri_Browse = string.Format(#"api/accountapi/saveuserdata"
// User data
/*pModelSerialized*/);
Are you formatting the url and passing any params to it? If so, what are the params and what does your WebApi route look like?
That should be enough for a start - let me know how you get on.
BTW: Two things that strike me in your code (unrelated to the question):
It's very confusing to have a class called 'Users' representing a single user. If it's you're code I'd advise to change that to singular.
the properties on the Users object are using abbreviations - I don't think it's that expensive to spell them out and I can guarantee you that anyone new to this code will be grateful if you put a full name rather than a mysterious BCompanyID, or less mysterious but still hard to read (and write for that matter) FName
I had the same problem a few weeks ago. After doing a few tests, I've realized that my WebApi application was using DataContractJsonSerializer by default, instead of Newtonsoft. To fix it, you can change the default serializer on your server to NewtonSoft or to use DatacontractJsonSerializer to serialize your object and pass it to HttpWebRequest.
I would use jQuery to post some Json to the same URL ($.post(#"api/accountapi/saveuserdata", { Email: "e#example.com", FName = "Vlad" ... })), thus eliminating InsertNewUser from the equation. If you still get a null pNewUser parameter in SaveNewuser, then I would look at your API's route configuration to make sure the server expects SaveUserData(Users). If you get a non-null pNewUser from jQuery, then I would look closer at the output from InsertNewUser. If I had to bet, I would put my money on the routing configuration.
it seemed that i had an error with the LINQ code
Users existingUser = asigCtx.Users.Where(u => u.Email == pNewUser.Email).SingleOrDefault();
After i solved this the request was ok.
Tnx for the help :) Appreciate.
I have a custom list earthquakes which contains a list of earthquakes. How do I parse this in JavaScript in order to add it to innerHtml and display on the screen. The problem is that I cannot get this to display on the screen in a div. When it parses I get no result because my javascript is wrong and if I try just printing the result i get [Object object]
So the flow goes input from textbox -> web service -> list to javascript
earthquakes class:
public class earthquakes
{
public string eqid { get; set; }
public double magnitude { get; set; }
public double lng { get; set; }
public string source { get; set; }
public DateTime date { get; set; }
public int depth { get; set; }
public double lat { get; set; }
}
dataEarthquakes class
public class dataPostalCodes
{
public List<postalCodes> postalCodes { get; set; }
}
WebService:
public static dataEarthQuakes getEarthquakes(dataPostalCodes postalCodes)
{
double lat = postalCodes.postalCodes[0].lat;
double lng = postalCodes.postalCodes[0].lng;
Uri address = new Uri(String.Format(FindEarthquakes, lat, 0, lng, 0));
WebClient client = new WebClient();
string jsonResponse = string.Empty;
jsonResponse = client.DownloadString(address.AbsoluteUri);
var results = JsonConvert.DeserializeObject<dataEarthQuakes>(jsonResponse);
return results;
}
Javascript:
function OnLookupComplete(e) {
var result = e;
var weatherData = new Sys.StringBuilder();
var line;
for (var property in result.dataPostalCodes) {
line = String.format("<b>{0}:</b> {1}<br/>",
property, result.dataPostalCodes[property]);
weatherData.append(line);
}
$get('divResult').innerHTML = weatherData.toString();
}
Json string:
{"earthquakes":[{"eqid":"2010utc5","magnitude":7.7,"lng":97.1315,"src":"us","datetime":"2010-04-06 20:15:02","depth":31,"lat":2.3602}, {"eqid":"2009kdb2","magnitude":7.6,"lng":92.9226,"src":"us","datetime":"2009-08-10 17:55:39","depth":33.1,"lat":14.0129},{"eqid":"2010zbca","magnitude":7.6,"lng":123.533,"src":"us","datetime":"2010-07-23 20:51:11","depth":576.3,"lat":6.4939},{"eqid":"2010xkbv","magnitude":7.5,"lng":91.9379,"src":"us","datetime":"2010-06-12 17:26:50","depth":35,"lat":7.7477},{"eqid":"c0000rxc","magnitude":7.4,"lng":143.7392,"src":"us","datetime":"2010-12-21 16:19:41","depth":14.9,"lat":26.8656},{"eqid":"2010zbcd","magnitude":7.4,"lng":123.2677,"src":"us","datetime":"2010-07-23 21:15:08","depth":616.7,"lat":6.7489},{"eqid":"2010wbaq","magnitude":7.4,"lng":96.0805,"src":"us","datetime":"2010-05-09 03:59:44","depth":61.4,"lat":3.7284},{"eqid":"2007hvbq","magnitude":7.4,"lng":142.6846,"src":"us","datetime":"2007-09-28 11:38:58","depth":261.3,"lat":21.98},{"eqid":"2010zbbz","magnitude":7.3,"lng":123.4788,"src":"us","datetime":"2010-07-23 20:08:11","depth":604.5,"lat":6.7079},{"eqid":"2007xvam","magnitude":7.3,"lng":126.292,"src":"us","datetime":"2007-01-21 10:27:42","depth":10,"lat":1.2071}]}
As no.good.at.coding said in the comment, if your weatherData object contains the correct data, then it might be as simple as:
$('#divResult').html(weatherData.toString());
Another option may be to call parseJSON on your json object and then use jquery's each function to iterate through the results:
var results = $.parseJSON(e);
$(results).each(function (i, val) {
$('#divResult').append('<p>' + val.eqid + '<p>'); // can add markup here for magnitude and other properties
});
If you aren't sure what your objects are in javascript, firebug is a great tool for debugging (or you could use the Developer Tools that are built in to Chrome).
You didn't state an exact problem, but hopefully this will help get you on the right track.
Here's what I might do:
$.get('my-webservice-url',
function(data) {
OnLookupComplete(data['earthquakes']);
},
'json');
function OnLookupComplete(e) {
var weatherData = new Sys.StringBuilder();
for(var i=0;i<e.length;i++) {
var line;
for (var property in e[i].dataPostalCodes) {
line = String.format("<b>{0}:</b> {1}<br/>",
property, e[i].dataPostalCodes[property]);
weatherData.append(line);
}
}
$('#divResult').html(weatherData.toString());
}
The idea here is that you make your call to your web service and indicate to jQuery that the response expected is JSON (this is useful in case you aren't setting the content-type header correctly in the response from the server).
Once the GET request completes, jQuery will call your callback function (the anonymous function you can see in the call to $.get(). From your JSON example, I see that you expect an object earthquakes which is an array of objects of earthquake details.
The function then calls OnLookupComplete() with each the earthquakes array.
OnLookupComplete() then iterates over each earthquake, builds the right string and appends it to the StringBuilder. Finally, once all earthquakes have been dealt with, it appends the complete set of formatted lines to the div with the id divResult.
I use an Api that is returning not everytime the same response based in the place demanded. Some places have more details, some contents have more attributes that others. The resulting serialized object is not the same everytime resulting in deserializing error when not match. The object of this project is not match the entire content response, but only one piece of this content : the Centroid.
{
"place":{
"woeid":12345,
"placeTypeName":"State",
"placeTypeName attrs":{
"code":8
},
"name":"My Region",
"country":"",
"country attrs":{
"type":"Country",
"code":"XX"
},
"admin1":"My Region",
"admin1 attrs":{
"type":"Region",
"code":""
},
"admin2":"",
"admin3":"",
"locality1":"",
"locality2":"",
"postal":"",
"centroid":{
"latitude":30.12345,
"longitude":40.761292
},
"boundingBox":{
"southWest":{
"latitude":32.2799,
"longitude":50.715958
},
"northEast":{
"latitude":29.024891,
"longitude":12.1234
}
},
"areaRank":10,
"popRank":0,
"uri":"http:\/\/where.yahooapis.com",
"lang":"en-US"
}
}
Can someone point the best method to deserialize a piece of content instead the complete response (centroid not at same place), or to deserialize a changing response schema.
I use ServiceStack C# serializer but all propositions are welcome. Thanks.
There's actually a few ways you can parse this using ServiceStack's JsonSerializer as can be seen in this example of parsing one of GitHub's JSON API.
I would take the JsonObject approach as you get to end up with the C# class of your choice, although it does require more than the 1-liner that you're used to with ServiceStack's JsonSerializer. Anyway here's the resulting code:
Func<JsonObject, Centroid> toCentroid = map =>
new Centroid(map.Get<decimal>("latitude"), map.Get<decimal>("longitude"));
var place = JsonObject.Parse(JsonCentroid)
.Object("place")
.ConvertTo(x => new Place
{
WoeId = x.Get<int>("woeid"),
PlaceTypeName = x.Get(""),
PlaceTypeNameAttrs = x.Object("placeTypeName attrs"),
Name = x.Get("Name"),
Country = x.Get("Country"),
CountryAttrs = x.Object("country attrs"),
Admin1 = x.Get("admin1"),
Admin1Attrs = x.Object("admin1 attrs"),
Admin2 = x.Get("admin2"),
Admin3 = x.Get("admin3"),
Locality1 = x.Get("locality1"),
Locality2 = x.Get("locality2"),
Postal = x.Get("postal"),
Centroid = x.Object("centroid")
.ConvertTo(toCentroid),
BoundingBox = x.Object("boundingBox")
.ConvertTo(y => new BoundingBox
{
SouthWest = y.Object("southWest").ConvertTo(toCentroid),
NorthEast = y.Object("northEast").ConvertTo(toCentroid)
}),
AreaRank = x.Get<int>("areaRank"),
PopRank = x.Get<int>("popRank"),
Uri = x.Get("uri"),
Lang = x.Get("lang"),
});
Here is the full source code of this example.
You could use the DataContractJsonSerializer that's part of the standard .NET framework. You define just those attributes you're interested in. The other ones will be ignored.
class CentroidReader
{
public static Centroid ReadControid(Stream stream)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Response));
Response response = serializer.ReadObject(stream) as Response;
return response.Place.Centroid;
}
}
[DataContract]
class Response
{
[DataMember(Name = "place")]
public Place Place { get; set; }
}
[DataContract]
class Place
{
[DataMember(Name = "centroid")]
public Centroid Centroid { get; set; }
}
[DataContract]
class Centroid
{
[DataMember(Name = "latitude")]
public double? Latitude { get; set; }
[DataMember(Name = "longitude")]
public double? Longitude { get; set; }
}