I am currently developing an app for Windows Phone 8 in C# that makes use of Quizlet.com's API, an online flashcard website.
One of the API calls documented in their API reference to edit a flashcard set requires you to send an array of terms and definitions through a PUT method.
Presumably do this you would have to use HttpWebRequest rather than WebClient, as the latter is too simple and doesn't support PUT requests to my knowledge. I cannot, however, seem to understand how you could send an entire array as part of a PUT request.
Does anyone have any ideas as to how one would do this? I'm sorry I don't have source code to attach, but it's probably not necessary as this is a more general question.
Thanks in advance!
This is the code I'm currently using that is not working:
RestClient Edit = new RestClient("https://api.quizlet.com");
RestRequest EditRequest = new RestRequest();
EditRequest.AddParameter("term_ids[]", ID);
EditRequest.AddParameter("terms[]", Terms);
EditRequest.AddParameter("definitions[]", Definitions);
EditRequest.AddParameter("title", item.Title);
EditRequest.AddHeader("Authorization", "Bearer " + CurrentLogin.AccessToken);
EditRequest.AddHeader("Host", "api.quizlet.com");
EditRequest.Resource = "2.0/sets/" + item.Id;
EditRequest.Method= Method.PUT;
Edit.ExecuteAsync(EditRequest, Response =>
{
FinalizeUpdate(Response);
});
I declare my arrays as such:
int[] ID;
string[] Terms;
string[] Definitions;
And I add data to my arrays as such (TermsList is an ObservableCollection):
foreach(Term i in TermsList)
{
ID[Counter] = i.Id;
Terms[Counter] = i.Name;
Definitions[Counter] = i.Definition;
Counter++;
}
Below is the class definition for Term:
public class Term
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("term")]
public string Name { get; set; }
[JsonProperty("definition")]
public string Definition { get; set; }
}
The API is RESTful, so I would recommend using an external library if possible, such as RestSharp.
From what I can see in online examples of other APIs, the way to send an array is simply to send the same parameter again with a different value.
i.e., using RestSharp's API you would do :
client.AddParameter("imageData[]", data1);
client.AddParameter("imageData[]", data2);
Just in case anyone else runs into this problem, this is code that finally got the PUT method to work:
RestClient Edit = new RestClient("https://api.quizlet.com");
RestRequest EditRequest = new RestRequest();
foreach (var i in ID)
{
EditRequest.AddParameter("term_ids[]", i);
}
foreach (var i in Terms)
{
EditRequest.AddParameter("terms[]", i);
}
foreach(var i in Definitions)
{
EditRequest.AddParameter("definitions[]", i);
}
EditRequest.AddParameter("title", item.Title);
EditRequest.AddHeader("Authorization", "Bearer " + CurrentLogin.AccessToken);
EditRequest.AddHeader("Host", "api.quizlet.com");
EditRequest.Resource = "2.0/sets/" + item.Id;
EditRequest.Method= Method.PUT;
Edit.ExecuteAsync(EditRequest, Response =>
{
FinalizeUpdate(Response);
});
I hope this helps!
Related
I cannot POST data in Json Format using UnityWebRequest in Unity. It gives error
Error: HTTP/1.1 500 Internal Server Error
I am using Webservice made in ASP.NET Core and hosted locally on IIS Express.
Here is my C# Code in Unity
public class AddUsers : MonoBehaviour
{
IEnumerator addOrUpdateUser()
{
User user = new User()
{
Id = "0001",
Name = "John",
}
UnityWebRequest req = UnityWebRequest.Post("http://localhost:58755/User/AddNewUser", JsonConvert.SerializeObject(user));
req.SetRequestHeader("Content-Type", "application/json");
req.certificateHandler = new BypassCertificate();
yield return req.SendWebRequest();
if (req.isNetworkError || req.isHttpError || req.isError)
print("Error: " + req.error);
print(req.downloadHandler.text);
}
}
[Serializable]
public class UserDetails
{
public string Id { get; set; }
public string Name { get; set; }
}
Here is my ASP.NET Core Code using Entity Framework Core
[HttpPost]
string AddNewUser([FromBody]User user)
{
Context.LogoQuizUsers.Add(user); // I am getting System.NullReferenceException here
Context.SaveChanges();
return "Inserted Id: " + user.Id;
}
Post data as raw - body just as you would send using Postman or any similar interface.
Set Request's UploadHandler as UploadHandlerRaw. Add and Change your statement
UnityWebRequest req = UnityWebRequest.Post("http://localhost:58755/User/AddNewUser", "POST");
req.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(user))) as UploadHandler;
Hence the final code will be
IEnumerator addOrUpdateUser()
{
//...
UnityWebRequest req = UnityWebRequest.Post("http://localhost:58755/User/AddNewUser", "POST");
req.SetRequestHeader("Content-Type", "application/json");
req.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(user))) as UploadHandler;
req.certificateHandler = new BypassCertificate();
yield return req.SendWebRequest();
if (req.isNetworkError || req.isHttpError || req.isError)
print("Error: " + req.error);
print(req.downloadHandler.text);
//...
}
Rest of the code is correct.
Look at this topic
Inside the Unity editor:
Go to Window -> Package Manager.
In the topbar of the new window, search the "Packages : XXXXXX" drop-down and select "Unity Registry".
Search the "WebGl publisher" and install / import it.
IIIFFF the package doesn't appear:
In the same Package manager windows, click the "+" button -> Add from git.
Add the following:
com.unity.connect.share
This will automatically add the WebGL Publisher.
It helped me to resolve the problem
I found a blog post that shows how to "shim" familiar things like HttpResponseMessage back into ASP.NET Core MVC, but I want to know what's the new native way to do the same thing as the following code in a REST Post method in a Controller:
// POST audit/values
[HttpPost]
public System.Net.Http.HttpResponseMessage Post([FromBody]string value)
{
var NewEntity = _repository.InsertFromString(value);
var msg = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Created);
msg.Headers.Location = new Uri(Request.RequestUri + NewEntity.ID.ToString());
return msg;
}
In an ASP.NET Core MVC project, I can't seem to get Request.RequestUri.
I tried inspecting Request, and I was able to make a function like this:
private string UriStr(HttpRequest Request)
{
return Request.Scheme + "://" + Request.Host + Request.Path; // Request.Path has leading /
}
So I could write UriStr(Request) instead. But I'm not sure that's right. I feel like I'm hacking my way around, and not using this correctly.
A related question for earlier non-Core ASP.NET MVC versions asks how to get the base url of the site.
Personally, I use :
new Uri(request.GetDisplayUrl())
GetDisplayUrl fully un-escaped form (except for the QueryString)
GetEncodedUrl - fully escaped form suitable for use in HTTP headers
These are extension method from the following namespace : Microsoft.AspNetCore.Http.Extensions
A cleaner way would be to use a UriBuilder:
private static Uri GetUri(HttpRequest request)
{
var builder = new UriBuilder();
builder.Scheme = request.Scheme;
builder.Host = request.Host.Value;
builder.Path = request.Path;
builder.Query = request.QueryString.ToUriComponent();
return builder.Uri;
}
(not tested, the code might require a few adjustments)
Here's a working code. This is based off #Thomas Levesque answer which didn't work well when the request is from a custom port.
public static class HttpRequestExtensions
{
public static Uri ToUri(this HttpRequest request)
{
var hostComponents = request.Host.ToUriComponent().Split(':');
var builder = new UriBuilder
{
Scheme = request.Scheme,
Host = hostComponents[0],
Path = request.Path,
Query = request.QueryString.ToUriComponent()
};
if (hostComponents.Length == 2)
{
builder.Port = Convert.ToInt32(hostComponents[1]);
}
return builder.Uri;
}
}
I have been stuck on this for a couple hours now and not even google can help anymore. I am trying to send a file from the client to the backend using xmlhttprequest. I cannot get the filename, type, or content on the C# side. I would appreciate help on doing this. A lot of code I came across had methods that I can only guess are not supported in ASP.Net 5 and MVC 6 (such as HttpContext.Current and HttpPostedFile)
Here is my client side JavaScript request. This sends the query strings which bind to my model no problem so that is easily accessible, but getting the file is what I am having trouble with.
var form = new FormData();
form.append("file", file);
var queryParams = "id=" + (id == null ? -1 : id);
queryParams += "&name=" + name;
queryParams += "&value=" + val;
xhrAttach(REST_DATA + "/attach?" + queryParams, form, function (item) {
console.log('attached: ', item);
alert(item.responseText);
row.setAttribute('data-id', item.id);
removeProgressIndicator(row);
setRowContent(item, row);
}, function (err) {
console.log(err);
//stop showing loading message
stopLoadingMessage();
document.getElementById('errorDiv').innerHTML = err;
});
function xhrAttach(url, data, callback, errback)
{
var xhr = new createXHR();
xhr.open("POST", url, true);
//xhr.setRequestHeader("Content-type", "multipart/form-data");
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
callback(parseJson(xhr.responseText));
}else{
errback("Error: "+xhr.responseText);
}
}
};
xhr.timeout = 1000000;
xhr.ontimeout = errback;
xhr.send(data);
}
Here is my Controller dealing with the request. attachment is a model and the query string binds to it no problem. I could not find out how to add a File parameter to the model, or if that would even matter. Things I have tried are under this code.
// POST: /api/db/attach
[Route("/api/[controller]/attach")]
[HttpPost]
public async Task<dynamic> attach(Attachment attachment)
{
//get the file somehow
}
i have tried many things, but cannot remember exactly what, here is one thing I did try though, which did not work.
var file = Request.Form["file"];
here is the attachment model in case it helps
namespace MyModel.Models
{
public class Attachment
{
public long id { get; set; }
public string name { get; set; }
public string value { get; set; }
}
}
Don't use query parameters or FormData if you're going to use a model on the MVC side. Just don't. And to me, it's better to just get the file into a base64 string first, than to try sending the File object, itself. I've posted about how to do that, here: Convert input=file to byte array
Then, declare and format a JSON object:
var dataObj = {
file = fileByteArray[0],
id = (id == null ? -1 : id),
name = name,
value = val
};
That fileByteArray[0] is referencing the object from my link. My answer there assumes you were just going to keep loading file base64 strings into that global array object. You can either keep it as an array, like I had, and loop through them one by one, replacing that [0] with [i], for example, as the indexer in a for loop, or just use a var fileByteArray = "" with that other code, make it so you don't push additional files but always just overwrite that variable, & just use that.
And a word of caution on that last parameter - don't use val if you use jQuery - it's a keyword. I only have it above because it's what you were passing to the URL parameter values.
Get rid of the queryParams in this line:
xhrAttach(REST_DATA + "/attach?" + queryParams, form, function (item) {
Change it to:
xhrAttach(REST_DATA + "/attach", form, function (item) {
Set the Content-Type, right where it's commented out, to:
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
Change what you are sending - it's no longer going to be FormData, it's the JSON object, and it needs to be stringified:
xhr.send(JSON.stringify(dataObj));
Fix your model to now include the file base64 string:
public class Attachment
{
public string file { get; set; }
public long id { get; set; }
public string name { get; set; }
public string value { get; set; }
}
Fix your POST method. 2 issues:
You can't use [HttpPost] if your class is inheriting ApiController, which you probably should be for this. It must be [System.Web.Http.HttpPost], and yes, it has to be completely spelled out, or it will assume it's [System.Web.Mvc.HttpPost] and not assign the route - you'd get a 404 - Not Found error when you try to do your POST. If you're inheriting from Controller, disregard this.
You will need a [FromBody] tag on your model if you are inheriting from ApiController:
public async Task<dynamic> attach([FromBody]Attachment attachment) { ... }
Then you get the file like this:
string base64FileString = attachment.file;
If you want to store it in a byte[] in the database, you can convert it:
byte[] bytes = System.Convert.FromBase64String(base64FileString);
And btw, I think your response handling is wrong. I would not do this:
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
callback(parseJson(xhr.responseText));
}else{
errback("Error: "+xhr.responseText);
}
}
};
This is how I would do it:
xhr.onreadystatechange = function(response){
if(xhr.readyState == 4 && xhr.status == 200){
callback(parseJson(response.target.responseText));
} else {
alert("Error: " + response.target.responseText);
}
};
Assuming that the response.target.responseText is getting the error sent back from the server-side in a way you can display. If not, sending it to a function that could parse it out would be the right choice. I don't think that xhr.responseText was correct.
I would suggest trying the following:
public async Task<dynamic> attach([FromURI]Attachment attachment, [FromBody] FormDataCollection formData)
And then the FormDataCollection should have the form data for retrieval.
Add a public get/set property called File to your Attachment model and the uploaded file should be bound to this property.
A model similar to yours:
https://github.com/aspnet/Mvc/blob/9f9dcbe6ec2e34d8a0dfae283cb5e40d8b94fdb7/test/WebSites/ModelBindingWebSite/Models/Book.cs#L8
public class Book
{
public string Name { get; set; }
public IFormFile File { get; set; }
}
Following controller has examples of different ways of model binding an uploaded file.
https://github.com/aspnet/Mvc/blob/9f9dcbe6ec2e34d8a0dfae283cb5e40d8b94fdb7/test/WebSites/ModelBindingWebSite/Controllers/FileUploadController.cs#L81
public KeyValuePair<string, FileDetails> UploadModelWithFile(Book book)
{
var file = book.File;
var reader = new StreamReader(file.OpenReadStream());
var fileContent = reader.ReadToEnd();
var parsedContentDisposition = ContentDispositionHeaderValue.Parse(file.ContentDisposition);
var fileDetails = new FileDetails
{
Filename = parsedContentDisposition.FileName,
Content = fileContent
};
return new KeyValuePair<string, FileDetails>(book.Name, fileDetails);
}
if this does not work, then I suspect your request is not in correct format.
I am trying to use the Office API to sync contacts from a few different sources. I have been having a problem trying to make a POST request with my JSON object to create a new contact. I have been looking at the MSDN pages but I feel like I should clarify I’m relatively new to C#, this is my first time trying to use REST protocols, and async methods in C#.
I have my code below, I tried to create a class that will add a new contact with a hard coded JSON string. I have tried a few various ways of trying to complete this request. Every request I have attempted gives me a 401 or 400 Error. I left a couple lines that I felt were closest to the solution but if those are not on the right track I have no problem trying something else. There is also a function that I believe could be useful but I couldn’t really find documentation on how to use it:
await client.Me.Contacts.AddContactAsync();
Again I said I am pretty new to this so if there is a way to create an IContact item from the JSON and use the above method or to just pass the JSON directly either would be extremely useful. Even links to documentation that could be useful I would love to see. I’m a pretty stuck on this problem I’ve never posted a question before but I’m stumped on this.
Below is the documentation for the Contacts API maybe it will make more sense to you guys than me.
http://msdn.microsoft.com/en-us/library/office/dn792115(v=office.15).aspx
If anybody can figure out how to make a post request from that JSON it will be much appreciated.
using Microsoft.Office365.Exchange;
using Microsoft.Office365.OAuth;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
using System.Windows;
using System.Net.Http;
using System.Net.Http.Headers;
namespace ContactSynchronization
{
class OfficeAPIWrite
{
private static string odata = "#odata.type";
private static string type = "#Microsoft.Exchange.Services.OData.Model.Contact";
const string ServiceResourceId = "https://outlook.office365.com";
static readonly Uri ServiceEndpointUri = new Uri("https://outlook.office365.com/ews/odata/Me/Contacts");
static string _lastLoggedInUser;
static DiscoveryContext _discoveryContext;
public static async Task OfficeWrite()
{
try
{
var client = await EnsureClientCreated();
string json = new JavaScriptSerializer().Serialize(new
{
odata = type,
GivenName = "Mara",
Surname = "Whitley",
EmailAddress1 = "mara#fabrikam.com",
BusinessPhone1 = "425-555-1313",
Birthday = "1974-07-22T07:00:00Z"
});
try
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, ServiceEndpointUri);
request.Content = new StringContent(json);
request.Headers.Add("Accept", "application/json;odata=minimalmetadata");
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
catch (System.Net.WebException e)
{
MessageBox.Show(e.ToString());
}
}
catch (Microsoft.Office365.OAuth.AuthenticationFailedException)
{
MessageBox.Show("Authentication Failed Exception was thrown");
}
}
public static async Task<ExchangeClient> EnsureClientCreated()
{
if (_discoveryContext == null)
{
_discoveryContext = await DiscoveryContext.CreateAsync();
}
var dcr = await _discoveryContext.DiscoverResourceAsync(ServiceResourceId);
_lastLoggedInUser = dcr.UserId;
return new ExchangeClient(ServiceEndpointUri, async () =>
{
return (await _discoveryContext.AuthenticationContext.AcquireTokenSilentAsync(ServiceResourceId, _discoveryContext.AppIdentity.ClientId, new Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier(dcr.UserId, Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifierType.UniqueId))).AccessToken;
});
}
public static async Task SignOut()
{
if (string.IsNullOrEmpty(_lastLoggedInUser))
{
return;
}
if (_discoveryContext == null)
{
_discoveryContext = await DiscoveryContext.CreateAsync();
}
await _discoveryContext.LogoutAsync(_lastLoggedInUser);
}
}
}
Well I guess I figured out a work around. This uses a ContactObject that I created and newtonsoft's JSON serializer. I was hoping to see an example of the microsoft ExchangeClient in action, the only reason I am posting this is to help others that might have similar issues posting to the office API, the below code will run successfully. I'm still looking though if anybody can show me the correct way to use the ExchangeClient functions.
// your request must include these, and a given name,
// everything else is optional
private const string odata = "#odata.type";
private const string type = "#Microsoft.Exchange.Services.OData.Model.Contact";
public static async Task CreateContact(ContactObject officeContact, string userEmail, string userPassword)
{
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, new Uri("https://outlook.office365.com/ews/odata/Me/Contacts"));
// Add the Authorization header with the basic login credentials.
var auth = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(userEmail + ":" + userPassword));
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Authorization", auth);
var createResponse = new JObject();
createResponse[odata] = type; // this needs to be here for this to work
if (!String.IsNullOrEmpty(officeContact.officeDisplayName)) createResponse["DisplayName"] = officeContact.officeDisplayName;
if (!String.IsNullOrEmpty(officeContact.officeGivenName)) createResponse["GivenName"] = officeContact.officeGivenName;
if (!String.IsNullOrEmpty(officeContact.officeMiddleName)) createResponse["MiddleName"] = officeContact.officeMiddleName;
if (!String.IsNullOrEmpty(officeContact.officeNickName)) createResponse["NickName"] = officeContact.officeNickName;
if (!String.IsNullOrEmpty(officeContact.officeSurname)) createResponse["Surname"] = officeContact.officeSurname;
if (!String.IsNullOrEmpty(officeContact.officeEmailAddress1)) createResponse["EmailAddress1"] = officeContact.officeEmailAddress1;
if (!String.IsNullOrEmpty(officeContact.officeEmailAddress2)) createResponse["EmailAddress2"] = officeContact.officeEmailAddress2;
if (!String.IsNullOrEmpty(officeContact.officeEmailAddress3)) createResponse["EmailAddress3"] = officeContact.officeEmailAddress3;
if (!String.IsNullOrEmpty(officeContact.officeHomePhone1)) createResponse["HomePhone1"] = officeContact.officeHomePhone1;
if (!String.IsNullOrEmpty(officeContact.officeHomePhone2)) createResponse["HomePhone2"] = officeContact.officeHomePhone2;
if (!String.IsNullOrEmpty(officeContact.officeBusinessPhone1)) createResponse["BusinessPhone1"] = officeContact.officeBusinessPhone1;
if (!String.IsNullOrEmpty(officeContact.officeBusinessPhone2)) createResponse["BusinessPhone2"] = officeContact.officeBusinessPhone2;
if (!String.IsNullOrEmpty(officeContact.officeMobilePhone1)) createResponse["MobilePhone1"] = officeContact.officeMobilePhone1;
if (!String.IsNullOrEmpty(officeContact.officeOtherPhone)) createResponse["OtherPhone"] = officeContact.officeOtherPhone;
if (!String.IsNullOrEmpty(officeContact.officeId)) createResponse["Id"] = officeContact.officeId;
if (!String.IsNullOrEmpty(officeContact.officeCompanyName)) createResponse["CompanyName"] = officeContact.officeCompanyName;
request.Content = new StringContent(JsonConvert.SerializeObject(createResponse));
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.SendAsync(request);
try
{
response.EnsureSuccessStatusCode();
}
catch (System.Net.WebException)
{
MessageBox.Show("BAD REQUEST");
}
}
Is there a way to search a different website from within your own website?
for example, I build a ASP.net page with a search box that's linked for example to stackoverflow's search box, when I type in any search query I'll get the results in my website.
There are a few ways to go about doing something like this:
I'll assume that the intent is to make it work with stack overflow
1. Use StackOverflows Built in API (Client Side)
You can find some details about it here. Included in there are details about how to do a search. You can do this with a client side library like jQuery.
var URL = "http://api.stackoverflow.com/1.1/";
var _url = URL + 'users?filter=locrizak';
$.ajax({
dataType: 'jsonp',
jsonp: 'jsonp', // <--- add this
url: _url,
success: function(val) {
console.log('success');
},
error: function(val) {
console.log('error');
console.log(arguments);
}
});
2. Use StackOverflows Built in API (Server Side)
static void SearchStackOverflow(string y)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://api.stackoverflow.com/1.1/search?intitle=" + Uri.EscapeDataString(y));
httpWebRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
httpWebRequest.Method = "GET";
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
string responseText;
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
responseText = streamReader.ReadToEnd();
}
var result = (SearchResult)new JavaScriptSerializer().Deserialize(responseText, typeof(SearchResult));
.... do something with result ...
}
class SearchResult
{
public List<Question> questions { get; set; }
}
class Question
{
public string title { get; set; }
public int answer_count { get; set; }
}
3. Use a Screen Scraper (Not Ideal When an API is available)
This can be done with a tool like Selenium or Watin. Here is a guide to help you get started with Selenium.