Send proper api call using RestSharp to getresponse.com api - asp.net-mvc

I am trying to add a contact email address to a existing campaign list named- "liakat". But I am getting a response from getresponse.com saying httpStatus 400, which you can check on the attached pitures. I have added a temporary api key, so you can test it yourself too. I am following this document from getresponse.com. Anyone who can fix this issue?
Controller:
string email = "test#gmail.com";
// Test Campaign name - "pikaa"
// "pikaa" named campaign ID->ee104303
// our api key-> 948dsdfsdfs1a017a07f3c6
var client = new RestClient("https://api.getresponse.com/v3/contacts");
var request = new RestRequest(Method.POST);
request.RequestFormat = DataFormat.Json;
var root = new RootObject();
root.campaign = new Campaign();
root.name = "Mr Men";
root.email = "test#gmail.com";
root.campaign.campaignId = "IDss6604303";
var jsBody = new JavaScriptSerializer().Serialize(root);
request.AddHeader("X-Auth-Token", "api-key 948sdfsdfsdfsdfsdfs6");
request.AddParameter("application/x-www-form-urlencoded", "email=" + email + "&campaign=ID85104303", ParameterType.RequestBody);
var response = client.Execute(request);
Model:
public class Campaign
{
public string campaignId { get; set; }
}
public class RootObject
{
public string name { get; set; }
public string email { get; set; }
public Campaign campaign { get; set; }
}

There is no "connection" between the Request request and the JSON Object jsBody (or root). What you're sending is a query string.
Something like
email=test#gmail.com&campaign=ID85104303
This is defined by the line:
request.AddParameter("application/x-www-form-urlencoded", "email=" + email + "&campaign=ID85104303", ParameterType.RequestBody);
If you want to send your JSON, you need to change the line to:
request.AddParameter("application/json; charset=utf-8", jsBody, ParameterType.RequestBody);
I can't tell if this will already work, but so you're at least sending the right body and you should get a successful response or at least a better error message.

Related

Adding invited (guest) user to teams seems to not work properly

Hi (ref issue)
After setting up the tenant to allow invitation of user from another domain, we are able to invite external users (in set domain) to teams. This works fine when doing it manually, in the GUI.
However, when trying to add an invited user threw the windows graph API, something is not working properly.
Our procedure to invite a user to a team is as follows:
Note we are using application privileges
Invite the user to the tenant (with or without welcome mail)
https://learn.microsoft.com/en-us/graph/api/invitation-post?view=graph-rest-1.0
Add the invited user to the team
https://learn.microsoft.com/en-us/graph/api/group-post-members?view=graph-rest-1.0
Both these calls complete successfully and does not return any error messages. In all the admin GUI’s (AAD, Teams, Exchange) the user is invited and is added to the group.
But the user in question does not receive a welcome mail that he/she has been added to the team. And if the user (given we send a welcome mail in step 1) tries to access http://teams.microsoft.com the user gets notified that he/she does not have permissions and/or does not see the team.
Any tips?
API Permissions
EDIT:
After some investigation, by monitoring the network traffic. It's seems that the missing call, to get properly invited to the team is:
POST https://api.teams.skype.com/emea/beta/teams/($teamurl)/bulkUpdateRoledMembers?allowBotsInChannel=true
where you send in a list of userid (8:orgid:{userid}) and the groupid. (teamurl seems to be the channel id)
{"users":[{"mri":"8:orgid:00000000-5946-0000-87d2-b16b6fdf7a72","role":2}],"groupId":"00000000-2e8b-4d18-0000-394c6a4846d0"}
I have tried to call this from application & delegation, but get 'Unauthorized'. Also I could not find any API permission that granted access to 'api.teams.skype.com'.
I finally figured out how to get an access token to invoke bulkUpdateRoledMembers. It only works if I request an access token for it directly, so no Application Permissions and no On-Behalf-Of Flow.
private static async Task<string> GetAccessTokenForTeams(string tenantId)
{
var client = new PublicClientApplication(
clientId: "d3590ed6-52b3-4102-aeff-aad2292ab01c",
authority: $"https://login.microsoftonline.com/{tenantId}/",
userTokenCache: null);
try
{
var result = await client.AcquireTokenInteractive(new[] { "https://api.spaces.skype.com/user_impersonation" }, null).ExecuteAsync();
return result.AccessToken;
}
catch (Exception e)
{
Debug.WriteLine(e);
throw;
}
}
It turns out you also need a Skypetoken, which you can get very easily with the just acquired access token.
private static async Task<string> GetSkypeToken(string accessToken)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add(HttpRequestHeader.Authorization.ToString(), "Bearer " + accessToken);
using (var response = await client.PostAsync("https://api.teams.skype.com/beta/auth/skypetoken", null))
{
var contentString = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var skypeTokenResponse = JsonConvert.DeserializeObject<SkypeTokenResponse>(contentString);
return skypeTokenResponse.Tokens.SkypeToken;
}
else
{
throw new Exception(response.StatusCode.ToString() + ": " + contentString);
}
}
}
}
private class SkypeTokenResponse
{
public Token Tokens { get; set; }
public class Token
{
public string SkypeToken { get; set; }
public string ExpiresIn { get; set; }
}
}
Then you can finally invoke bulkUpdateRoledMembers by passing both tokens along.
private static async Task<object> bulkUpdateRoledMembers(string accessToken, string skypeToken, string teamUrl, string groupId, string userId)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add(HttpRequestHeader.Authorization.ToString(), "Bearer " + accessToken);
client.DefaultRequestHeaders.Add("X-Skypetoken", skypeToken);
var bodyString = JsonConvert.SerializeObject(new
{
users = new List<object>
{
new
{
mri = "8:orgid:" + userId,
role = 2
}
},
groupId = groupId
});
var body = new StringContent(bodyString, Encoding.UTF8, "application/json");
using (var response = await client.PutAsync($"https://teams.microsoft.com/api/mt/emea/beta/teams/{teamUrl}/bulkUpdateRoledMembers?allowBotsInChannel=true", body))
{
var contentString = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var jsonresult = JObject.Parse(contentString);
return jsonresult;
}
else
{
throw new Exception(response.StatusCode.ToString() + ": " + contentString);
}
}
}
}

Getting deserialized body without binding

I'm starting with Nancy, and I've run into a frustrating issue.
I have a model that has an ID (amongst other properties).
public class MyModel
{
public string Id { get; set; }
// other properties
}
In my module, I defined a PUT method
Put["/{id}", true] = async (parameters, token) =>
{
var model = this.Bind<MyModel>();
string id = parameters["id"];
if (model.Id != id)
return new Response
{
ReasonPhrase = $"[error message about IDs not matching]",
StatusCode = HttpStatusCode.BadRequest
};
await _myModelService.Update(model);
return Nancy.Response.NoBody;
};
The issue I'm experiencing is that at the this.Bind<MyModel>() call, Nancy overwrites the ID in the body with the ID in the route, meaning I can't test my "unmatched" scenario. For example:
PUT /orders/someObjectId
{
"Id" : "aDifferentObjectId"
}
binds to a MyModel with Id as "someObjectId".
I've also tried blacklisting the property:
this.Bind<MyModel>(m => m.Id)
this.Bind<MyModel>("id")
this.Bind<MyModel>("Id")
this.Bind<MyModel>("id", "Id")
However, this results in the bind operation clearing the ID property in the model.
How can I get exactly what's in the body?
Nevermind. I was doing something wrong, and now it's working without the blacklist. I have no explanation.

POST data to a WebAPI controller

I am looking to send information from a form submitted on a webpage to be processed by the WebAPI of another website. The form is basically a series of fields as follows:
Account Type (radio button 2 options)
Name
Email
Phone Number
Tariff (dropdown 4 options)
On the Web API controller end I have created a class called QuickSwitch which is coded as follows:
public class QuickSwitch
{
[StringLength(20)]
public string AccountType { get; set; }
[StringLength(50)]
public string FullName { get; set; }
[StringLength(100)]
public string Email { get; set; }
[StringLength(15)]
public string PhoneNumber { get; set; }
public string Tariff { get; set; }
}
On the WebAPI controller I have the following method:
[HttpPost]
public HttpResponseMessage PostMXBData([FromBody] QuickSwitch qs)
{
#region Customer Details
var accountType = "";
var name = "";
var email = "";
var phoneNumber = "";
var mobileNumber = "";
var houseNumber = "";
var address = "";
var town = "";
var postcode = "";
var county = "";
var keypadAccountNumber = "";
var keypadTariff = "";
var billpayCustomerNumber = "";
var mprnNumber = "";
var billpayTariff = "";
#endregion
}
How can I POST the data on the webpage so that it links the fields from the form to the QuickSwitch class on the WebAPI controller end. I was then hoping to be able to sign variables as:
var accountType = qs.AccountType;
You can use a simple Ajax post with JQuery.
var data = {
fullName: 'john',
email: 'a#c.com',
// etc.etc.
};
$.ajax({
type: "POST",
url: 'http://localhost/api/mycontroller/PostMXBData',
data: data, // your QuickSwitch object
success: successCallbackFunction
});
function successCallbackFunction(result){
// ok!
}
There is one big flaw with your approach on posting data using a simple form to a WebAPI endpoint: what happens after the data is posted?
If you simply create a form and POST data to your controller then your web browser will show you the response from the server, and if it is a Web API endpoint it will show you not a page but (usually) a JSON/XML response (or an empty response if you simply return a 200 status code in your controller). This is not what you want (I believe).
You have two options then:
POST your form to a page in the same website, and then make a server-side request to the Web API endpoint (this depends on the technology used for your web page e.g. MVC, ASP.NET Web Pages, etc.);
Use AJAX and send your data to the Web API endpoint in background.
For the second option, assuming you are using jQuery:
$('#mySubmitButton').click(function (e) {
e.preventDefault();
var postData = {
accountType: $('#accountTypeInput').val(),
//etc...
}
$.ajax({
type: "POST",
dataType: "json",
url: "http://myhost/api/MyApi",
data: postData ,
success: function (data) {
alert(data);
},
error: function (error) {
alert('error!');
}
});
});
In the success callback you may want to redirect the user to a form submitted page, or simply show a success message in the page (it's up to you).

Troubleshoot MVC model binding failure - argument is null in controller

I am trying to POST an object from a WebJob to an MVC 4 controller. I am using Entity Framework. In the controller, I cannot get the object to bind properly (the argument is null). I have looked at many tutorials and it seems like my code should work.
Model (does this need to be in a specific namespace for EF to find it?):
public class CreateListingObject
{
public Listing listing;
public List<GalleryImage> images;
public CreateListingObject()
{
listing = new Listing();
images = new List<GalleryImage>();
}
}
public struct GalleryImage
{
public string picURL;
public string caption;
}
POST:
public void PostListing(CreateListingObject o)
{
Console.WriteLine("Posting listing: {0}", o.listing.Title);
HttpClient _httpClient = new HttpClient();
Uri uri = new Uri(_serviceUri, "/Automaton/CreateTestListing");
string json = BizbotHelper.SerializeJson(o);
HttpResponseMessage response = BizbotHelper.SendRequest(_httpClient, HttpMethod.Post, uri, json);
string r = response.Content.ReadAsStringAsync().Result;
response.EnsureSuccessStatusCode();
}
SendRequest (thank you Azure search samples):
public static HttpResponseMessage SendRequest(HttpClient client, HttpMethod method, Uri uri, string json = null)
{
UriBuilder builder = new UriBuilder(uri);
//string separator = string.IsNullOrWhiteSpace(builder.Query) ? string.Empty : "&";
//builder.Query = builder.Query.TrimStart('?') + separator + ApiVersionString;
var request = new HttpRequestMessage(method, builder.Uri);
if (json != null)
{
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
}
return client.SendAsync(request).Result;
}
Controller Action fragment (o is an empty object here):
[HttpPost]
public ActionResult CreateTestListing(CreateListingObject o)
{
Listing li = o.listing;
I have confirmed that if I post a simple object using the same code, everything works as expected.
Instead of sending a CreateListingObject in PostListing, I send this instead:
var test = new
{
data = "hi mom"
};
And change my action to, then the argument gets bound and I get valid data:
[HttpPost]
public ActionResult CreateTestListing(string data)
{
I have also checked the serialization of my CreateListingObject in the WebJob, and it is fully populated as I expect. This leads me to suspect that I am falling afoul of the default ModelBinder.

Web Api Post error -> Value cannot be null. Parameter name: uriString

I am relatively new to Web Api and I am having trouble POSTing a Person object. If I run in debug, I see that my uriString never gets set and I don't understand why. Because of this, I get "400 Bad Request" errors in Fiddler for all attempted Posts.
I have tried replicating what others have done when it comes to the Post action. Every example I've found uses a repository to add the person to the database. I do not have repositories however, but instead am using the NHibernate Save method to carry out this functionality. Here are the domain class, mapping by code file, WebApiConfig, and the PersonController.
public class Person
{
public Person() { }
[Required]
public virtual string Initials { get; set; }
public virtual string FirstName { get; set; }
public virtual char MiddleInitial { get; set; }
public virtual string LastName { get; set; }
}
public class PersonMap : ClassMapping<Person>
{
public PersonMap()
{
Table("PERSON");
Lazy(false);
Id(x => x.Initials, map => map.Column("INITIALS"));
Property(x => x.FirstName, map => map.Column("FIRST_NAME"));
Property(x => x.MiddleInitial, map => map.Column("MID_INITIAL"));
Property(x => x.LastName, map => map.Column("LAST_NAME"));
}
}
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.Services.Replace(typeof(IHttpActionSelector), new HybridActionSelector());
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}/{action}/{actionid}/{subaction}/{subactionid}",
defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional,
actionid = RouteParameter.Optional, subaction = RouteParameter.Optional, subactionid = RouteParameter.Optional }
);
config.BindParameter( typeof( IPrincipal ), new ApiPrincipalModelBinder() );
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
//config.EnableQuerySupport();
// To disable tracing in your application, please comment out or remove the following line of code
// For more information, refer to: http://www.asp.net/web-api
config.EnableSystemDiagnosticsTracing();
}
}
public class PersonsController : ApiController
{
private readonly ISessionFactory _sessionFactory;
public PersonsController (ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
}
// POST api/persons
[HttpPost]
public HttpResponseMessage Post(Person person)
{
var session = _sessionFactory.GetCurrentSession();
using (var tx = session.BeginTransaction())
{
try
{
if (!ModelState.IsValid)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
var result = session.Save(person);
var response = Request.CreateResponse<Person>(HttpStatusCode.Created, person);
string uriString = Url.Route("DefaultApi", new { id = person.Initials });
response.Headers.Location = new Uri(uriString);
tx.Commit();
return response;
}
catch (Exception)
{
tx.Rollback();
}
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
}
}
Fiddler information:
POST //localhost:60826/api/employees HTTP/1.1
Request Headers:
User-Agent: Fiddler
Content-Type: application/json
Host: localhost:xxxxx
Content-Length: 71
Request Body:
{
"Initials":"MMJ",
"LastName":"Jordan",
"FirstName":"Michael"
}
This line never sets the uriString to the correct value. string uriString = Url.Route("DefaultApi", new { id = person.Initials });
I've also tried using Url.Link instead of Url.Route. I've tried adding the controller = "Persons" inside the 'new' block, but that had no effect. Why isn't uriString being set? I'll listen to any thoughts at this point.
EDIT
I have tried
string uriString = Url.Link("DefaultApi", new { controller = "Persons", id = person.Initials, action="", actionid="", subaction="", subactionid="" });
as well as using a separate routeconfig
config.Routes.MapHttpRoute(
name: "PostApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional
} );
with
string uriString = Url.Link("PostApi", new { controller = "Persons", id = person.Initials});
and have had no luck.
SOLUTION
I was able to get this Post to work by using the line of code below. I'm not entirely sure if this is the correct way to do it, so if anybody knows differently, please share. Otherwise, I will happily use this approach.
response.Headers.Location = new Uri(this.Request.RequestUri.AbsoluteUri + "/" + person.Initials);
Problem seems to be here:
string uriString = Url.Route("DefaultApi", new { id = person.Initials });
You are only passing id while you need to be passing other parameters such as controller, etc.
You may construct URL this way:
string uriString = Url.Action("ActionName", "ControllerName", new { Id = person.Initials });

Resources