Doing some restapi in VB.Net and stuck on something - vb.net

I doubt anyone has specific experience related to this particular task, but maybe you can spot my problem. I'm trying to make a call to lithium (forum software) to place a vote in their poll, and their docs show this:
Example URL:
http://community.lithium.com/community-name/restapi/vc/polls/id/15/votes/place
Query Arguments:
poll.choice (required): - the choice to place the vote for. The choice is specified by a string of the form id/choice_id where choice_id is the id of the poll choice
Http Method:
POST
So my code looks something like this:
Dim _Response As New XmlDocument
Dim RestApiRoot As String = "http://example.com/community-name/restapi/vc/polls/id/6/votes/place"
APIRequest = WebRequest.Create(RestApiRoot)
APIRequest.Method = "POST"
APIRequest.Headers.Add("poll.choice", HttpContext.Current.Server.UrlEncode("id/" & _choiceID.ToString))
APIResponse = APIRequest.GetResponse()
APIReader = New StreamReader(APIResponse.GetResponseStream())
_Response.LoadXml(APIReader.ReadToEnd())
APIResponse.Close()
I'm not able to successfully register a vote and they say it's because the poll.choice param is not appearing in the header, but if I step through debugging, I see it in the Header Keys/Items just fine.
Anyone have any clue what I might be doing wrong?

I do exactly this with RestSharp, an open source REST framework. It works great with the Lithium REST API.
You're code will look something like this using RestSharp:
You'll create a class to look like the response from the Lithium API, in this case "Response". It will look like this (sorry, you'll have to translate this to VB.NET):
public class LithiumResponse
{
public string status { get; set; }
public string value { get; set; }
public string message { get; set; }
}
Now RestSharp will use that to capture the result like this:
// create the request
var request = new RestRequest();
request.Verb = Method.POST;
request.BaseUrl = "http://example.com/community-name";
// specify the action
request.Action = "restapi/vc/polls/id/6/votes/place";
// add the parameters
request.AddParameter("poll.choice", "id/" + _choiceID.ToString());
// now create a RestClient to execute the request,
// telling it to put the results in your "reponse" class
var client = new RestClient();
var lithiumresponse = client.Execute<LithiumResponse>(request);
// now you can check the status property of your class to
// see if it was successful
if (lithiumresponse.status == "success")
// you successfully placed a vote
I use RestSharp for a lot of interaction with the Lithium API and it makes it brain-dead simple. Pretty awesome library.

Related

Mapping response objects(C#)

I am consuming an API mehod and it returns response as of type Product and below is the response class structure.
Public class Product
{
public int Id;
public string Name;
public IList<Product> MasterProduct { get; set; }
}
The API result include the product attributes along with IList. Since this API cannot be consumed directly though our windows client we have a wrapper web API which consume this API, for this in the local API we have defined similar Product class. The issue I am facing is when trying to map the attibues of external API with local. Below is what I am trying to do.
response = Response.Result.Select(x => new Product
{
Id=x.Id,
Name=x.Name
MasterProduct = x.MasterProduct.Cast<MasterProduct>().ToList()//tried below
}).ToList();
but it fails with error as - Unable to cast object of type 'Api.Models.Product' to type 'App.DataContracts.Product'
The Masterproduct consist of hierarchal data .I am wondering if the approach I am taking is right or it has to be done through some method. Any suggestion or help would be appreciated.
Upon searching the web I came across some code where serpare method is being called to parse using Microsoft.Its.Data, but this was for single object where as in my case I have a List(Hierarchical).
Appreciate if someone can point to some linke/sampel to achive the same.
Trying serialization/deserialization would do. Below is the code
Perhaps trying serialization/deserialization would do.
if (response.Result != null)
{
var serializedResponse = JsonConvert.SerializeObject(Response.Result, Formatting.Indented);
response = JsonConvert.DeserializeObject<List<Product>>(serializedResponse);
}).ToList();
return response;

RazorEngine Error trying to send email

I have an MVC 4 application that sends out multiple emails. For example, I have an email template for submitting an order, a template for cancelling an order, etc...
I have an Email Service with multiple methods. My controller calls the Send method which looks like this:
public virtual void Send(List<string> recipients, string subject, string template, object data)
{
...
string html = GetContent(template, data);
...
}
The Send method calls GetContent, which is the method causing the problem:
private string GetContent(string template, object data)
{
string path = Path.Combine(BaseTemplatePath, string.Format("{0}{1}", template, ".html.cshtml"));
string content = File.ReadAllText(path);
return Engine.Razor.RunCompile(content, "htmlTemplate", null, data);
}
I am receiving the error:
The same key was already used for another template!
In my GetContent method should I add a new parameter for the TemplateKey and use that variable instead of always using htmlTemplate? Then the new order email template could have newOrderKey and CancelOrderKey for the email template being used to cancel an order?
Explanation
This happens because you use the same template key ("htmlTemplate") for multiple different templates.
Note that the way you currently have implemented GetContent you will run into multiple problems:
Even if you use a unique key, for example the template variable, you will trigger the exception when the templates are edited on disk.
Performance: You are reading the template file every time even when the template is already cached.
Solution:
Implement the ITemplateManager interface to manage your templates:
public class MyTemplateManager : ITemplateManager
{
private readonly string baseTemplatePath;
public MyTemplateManager(string basePath) {
baseTemplatePath = basePath;
}
public ITemplateSource Resolve(ITemplateKey key)
{
string template = key.Name;
string path = Path.Combine(baseTemplatePath, string.Format("{0}{1}", template, ".html.cshtml"));
string content = File.ReadAllText(path);
return new LoadedTemplateSource(content, path);
}
public ITemplateKey GetKey(string name, ResolveType resolveType, ITemplateKey context)
{
return new NameOnlyTemplateKey(name, resolveType, context);
}
public void AddDynamic(ITemplateKey key, ITemplateSource source)
{
throw new NotImplementedException("dynamic templates are not supported!");
}
}
Setup on startup:
var config = new TemplateServiceConfiguration();
config.Debug = true;
config.TemplateManager = new MyTemplateManager(BaseTemplatePath);
Engine.Razor = RazorEngineService.Create(config);
And use it:
// You don't really need this method anymore.
private string GetContent(string template, object data)
{
return Engine.Razor.RunCompile(template, null, data);
}
RazorEngine will now fix all the problems mentioned above internally. Notice how it is perfectly fine to use the name of the template as key, if in your scenario the name is all you need to identify a template (otherwise you cannot use NameOnlyTemplateKey and need to provide your own implementation).
Hope this helps.
(Disclaimer: Contributor of RazorEngine)

Sitecore Glass mapper GetItem<TypeName>(guid) always return null

I saw a related question:
Sitecore Glass Mapper always null
But unfortunately it does not give a solution for my case.
Here goes a code snippet:
var db = Factory.GetDatabase("master");
var context = new SitecoreContext();
// the ID of Needed item
var g = new Guid("{F21C04FE-8826-41AB-9F3C-F7BDF5B35C76}");
// just to test if it's possible to fetch item using db.GetItem
var i = db.GetItem(new ID(g), Language.Current, Sitecore.Data.Version.Latest);
// Grab item
var t = context.GetItem<Article>(g);
In the code above:
i is not null
t is null
Article is the simple class like:
[SitecoreType(TemplateId = "{4C4EC1DA-EB77-4001-A7F9-E4C2F61A9BE9}")]
public class Article
{
[SitecoreField(FieldName = "Title")]
public string Title { get; set; }
}
There are only one language installed in Sitecore - en, it has been specified in the web.config in the items as well.
Also I have added GlassMapperSc.Start(); to Application_Start in the Global.asax.cs and added my assembly to the list of included assemblies via var attributes = new AttributeConfigurationLoader(new[] { "Assembly.Name" }); and I succeeded to find my class in the SitecoreContext mappings.
It does not looks like a language issue, as stated in the link provided in the very beginning. And I'm struggling with it already for a pretty long time, but no luck...
Thank You!
I just noticed that you are using master db for the Sitecore DB and SitecoreContext for Glass.
The SitecoreContext class will use the database that is defined by the Sitecore.Context.Database property at runtime. This probably means that it is using the web database.
Can you check that you have published the item to the web database or instead using:
var context = new SitecoreService("master");

how to use built-in content type negotiation and just get access to the decision

I wanted to take advantage of built-in content negotiator and just get access to decision what formatter is going to be used. I don't want to use Request.Headers.Accept and check for whether it is json or xml content type because there lot of things are involved in that decision. Is there a way I can check at controller level or override any class that tells me what formatter going to be used OR what request content type is?
thanks in advance.
You can run conneg manually:
var conneg = Configuration.Services.GetContentNegotiator();
var connegResult = conneg.Negotiate(
typeof(YOUR_TYPE), Request, Configuration.Formatters
);
And use the output whichever way you want:
//the valid media type
var mediaType = connegResult.MediaType;
//do stuff
//the relevant formatter
var formatter = connegResult.Formatter;
//do stuff
If you want to see what is going on then install a TraceWriter and you will see what the conneg does.
A TraceWriter looks something like:
public class TraceWriter : ITraceWriter {
public bool IsEnabled(string category, TraceLevel level) {
return true;
}
public void Trace(HttpRequestMessage request, string category, TraceLevel level, Action<TraceRecord> traceAction) {
var rec = new TraceRecord(request, category, level);
traceAction(rec);
Log(rec);
}
private void Log(TraceRecord record) {
Console.WriteLine(record.Message);
}
}
and is installed like this,
config.Services.Replace(typeof(ITraceWriter), new TraceWriter());
If you want to manually invoke conneg then you can use,
config.Services.GetContentNegotiator().Negotiate(...)
Tugberk has a blog on this. Have a look.

ReadAsDataContract exception while reading namespace

I'm trying to consume twitter's REST api mentioned at this link using WCF REST starter kit mentioned at this link.
I'm using the same objects in DataContract as mentioned in the article - statusList and status.
[assembly: ContractNamespace("", ClrNamespace = "TwitterShell")]
[CollectionDataContract(Name = "statuses", ItemName = "status")]
public class statusList : List<status> { }
public class user
{
public string id;
public string name;
public string screen_name;
}
public class status
{
public string id;
public string text;
public user user;
}
I'm reading the XML contents using ReadAsDataContract() method.
HttpClient http = new HttpClient("http://twitter.com/statuses/");
http.TransportSettings.Credentials =
new NetworkCredential("{username}", "{password}");
HttpResponseMessage resp = http.Get("friends_timeline.xml");
resp.EnsureStatusIsSuccessful();
statusList sList = resp.Content.ReadAsDataContract<statusList>();
And I get the following exception. I have not defined the following namespace at all.
Error in line 1 position 24. Expecting element 'statuses' from namespace 'http://schemas.datacontract.org/2004/07/sitename'.. Encountered 'Element' with name 'statuses', namespace ''.
Please help. Thanks.
Just don't do it. You are in for a world of pain if you try using Datacontracts and operation contracts to access non-wcf services.
Ok, so I guess that was a bit unfair leaving you high and dry without an alternative, so try this:
var response = client.Get("http://twitter.com/statuses/friends_timeline.xml");
var statuses = response.Content.ReadAsXElement();
var statusQuery = from st in statuses.Elements("status")
select new status {
id = st.Element("id").Value,
text = st.Element("text").Value,
user = (from us in st.Elements("user")
select new user {
id = us.Element("id").Value,
name = us.Element("name").Value,
screen_name = us.Element("screen_name").Value
}).FirstOrDefault()
};
var statuses = statusQuery.ToList();
Using Linq to XML to create objects from the XML document allows you to avoid the magic of serializers and completely control the names and datatypes of your client side objects. It would be really easy to wrap this as a new HttpContent extension method so that you could simply do:
var statuses = response.Content.ReadAsTwitterStatuses();
I came across your post searching for answers to the same problem and I was able to find a solution for what you are looking for if you want to forgo the LINQ to XML approach.
1) Make sure you annotate your Status class with the following decoration
[DataContract (Namespace = "")]
By specifying the above annotation, you are overriding the namespace from the default namespace of your class. This should fix your namespace problem.
2) To address the issues of nulls (which I also experienced), order of your fields are very important. When your objects are deserialized, it is done in alphabetically order. You can order your fields to match the order of the incoming XML using the Order property on the DataMember annotation.
e.g.
[DataMember (Order = 1)]
public string text
etc ...