I have been reading around building an action method in my Web Api to allow the posting of images, which would be saved on the server and related to a Contact object via a field imageURL.
I have found a few examples online of how to set this up, and am wanting to test them. I currently have:
public Task<HttpResponseMessage> PostFile()
{
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = System.Web.HttpContext.Current.Server.MapPath("~/Content/images");
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(o =>
{
string file1 = provider.BodyPartFileNames.First().Value;
//Use file name to update ImageURL of contact
return new HttpResponseMessage()
{
Content = new StringContent("File uploaded.")
};
}
);
return task;
}
from How To Accept a File POST, and whilst I understand the flow I want to test it properly.
What would an example of a client call be to this API method, where I can include the contactid as a header?
I need to understand this properly to test this code sample so I can start understanding this more, but I'm really new to this area. I have kind of a chicken and egg scenario where I understand little about posting and receiving images and need a start point.
Thanks.
You can use a small MVC or ASP application (you only need one page) where you can use some JS plugins to post files to your controller.
Plugins: jQueryFileUpload or DropzoneJS. The last one is more easy to integrate. Both plugins has support for sending additional data.
Related
I've tried different ways to connect the Microsoft sign in function which open a webpage so you can use things like sign in with MFA. I manage to get this to work in Postman and now im trying it in C# particularly in .NET MVC 5.
HomeController:
public ActionResult TestAuth()
{
HttpClient client = new HttpClient();
var bodyParams = new Dictionary<string, string>();
bodyParams.Add("client_id", "{my_client_id}");
bodyParams.Add("client_secret", "{my_client_secret}");
bodyParams.Add("scope", "openid");
bodyParams.Add("redirect_uri", "https://localhost");
bodyParams.Add("grant_type", "authorization_code");
var response = client.PostAsync("https://login.microsoftonline.com/{my_tenant_id}/oauth2/v2.0/authorize", new FormUrlEncodedContent(bodyParams)).Result;
return View("TestAuth", new { response.Content.ReadAsStringAsync().Result });
}
View TestAuth.cshtml:
#model dynamic
#Html.Raw(Model)
If i sign in with my email on that domain, or any text at all really, i get this message. I cannot see why this issue occurs it gives me zero information what to do next more than just trying until you make it basically :). I've looked at tons of different Microsoft documentations, Stack posts, forums etc but with no success.
The postman call example:
Is it possible I'm doing something wrong in the request in the c# code or am i missing something important like configurations in Azure AD etc?
I'm up for anything that will work that i can sign into a Microsoft account that use MFA, then i can use their login to fetch data from Microsoft Graph based on their permissions basically.
P.S. I also can fetch data with the access token generated from postman so it's working as expected. I only need to "convert the postman call to c#" to make it work esentially. Any help is appreciated :)
You’re trying to do an oauth2 request from the controller. The request you’re sending is incorrect.
Microsoft made a great sample on how to use the Microsoft identity platform in a dotnet application https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/1-WebApp-OIDC
In a nutshell you redirect the user to the endpoint (so not a get/post from the controller, but actually a redirect 302 response to the token url).
The user then has to login and is redirected to the webapplication.
Your webapplication will get an authorization code that is has to exchange for an access token by a post request.
Postman does this for you, but in order to do it in dotnet core, just follow the sample.
I didn't find a soultion to this specific problem what i did find was another guide which led me to this github project https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect
Which had similar code in the Startup.cs file but actually had some examples like SendMail and ReadMail etc which was fetched from ms graph api. This gave me some idea of how this project was structured compared to mine. So one thing that was missing was this part I couldnt figure out:
IConfidentialClientApplication app = await MsalAppBuilder.BuildConfidentialClientApplication();
var account = await app.GetAccountAsync(ClaimsPrincipal.Current.GetAccountId());
So the Msal app builder which is a custom made thingy i needed to get the current user etc which i needed. This works fine and after that i can start doing requests to the graph api like adding scopes etc and make http request.
Example see groups:
[Authorize]
[HttpGet]
public async Task<ActionResult> GetGroups()
{
IConfidentialClientApplication app = await MsalAppBuilder.BuildConfidentialClientApplication();
var account = await app.GetAccountAsync(ClaimsPrincipal.Current.GetAccountId());
string[] scopes = { "GroupMember.Read.All", "Group.Read.All", "Group.ReadWrite.All", "Directory.Read.All", "Directory.AccessAsUser.All", "Directory.ReadWrite.All" };
AuthenticationResult result = null;
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/groups");
try
{
//Get acccess token before sending request
result = await app.AcquireTokenSilent(scopes, account).ExecuteAsync().ConfigureAwait(false);
if (result != null)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
//Request to get groups
HttpResponseMessage response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
ViewBag.Groups= response.Content.ReadAsStringAsync().Result;
return View("MyView");
}
}
}
catch (Exception ex)
{
Response.Write($"Error occured:{System.Environment.NewLine}{ex}");
}
return View();
}
I have a .net console application that I want to use to pull Instagram posts via the Instasharp wrapper using a hashtag search.
I use C# .net web forms extensively and am not very familiar with MVC nor how to use the await keyword. The code sample below seems to run, but never provides any output.
This line:
var tagInfo = await tagApi.Get("soccer");
Returns me to the calling method with no indication of retrieved data.
Can anyone provide insights as to what I am doing wrong here?
public static async void GetInstagram(String tag, InstagramConfig config)
{
var instagramPosts = await LoadInstagramPosts(tag, config);
dynamic dyn = JsonConvert.DeserializeObject(instagramPosts.ToString());
foreach (var data in dyn.data)
{
Console.WriteLine("{0} - {1}", data.filter, data.images.standard_resolution.url);
}
}
public static async Task<TagResponse> LoadInstagramPosts(String hashTagTerm, InstagramConfig config)
{
var tagApi = new InstaSharp.Endpoints.Tags(config);
var tagInfo = await tagApi.Get("soccer");
}
EDITED code after first comment which solved my initial problem.
I feel like I'm close but something is still missing.
See specific questions below...
I've based the code on the documentation from InstaSharp GitHub (https://github.com/InstaSharp/InstaSharp). GitHubs example is based on an MVC application, mine is not an MVC project, but a console application.
I feel like I am very close and maybe others will benefit from helping me solve this.
My specific questions...
1) Not sure where the 'code' parameter in the OAuth method originate??
2) How to perform the needed call backs with Instagram??
var config = new InstaSharp.InstagramConfig(location.InstagramClientId, location.InstagramClientSecret, "http://localhost");
string instagramLoginLink = InstagramLogin(config);
GetInstagram("soccer", config, instagramLoginLink);
public static async void GetInstagram(String tag, InstagramConfig config, string code)
{
OAuthResponse oAuthResponse = await OAuth(code, config);
var instagramPosts = await LoadInstagramPosts(tag, config, oAuthResponse);
if(instagramPosts.Data != null)
{
dynamic dyn = JsonConvert.DeserializeObject(instagramPosts.Data.ToString());
foreach (var data in dyn.data)
{
Console.WriteLine("{0} - {1}", data.filter, data.images.standard_resolution.url);
}
}
}
public static string InstagramLogin(InstagramConfig config)
{
var scopes = new List<OAuth.Scope>();
scopes.Add(InstaSharp.OAuth.Scope.Likes);
scopes.Add(InstaSharp.OAuth.Scope.Comments);
string link = InstaSharp.OAuth.AuthLink(config.OAuthUri + "authorize", config.ClientId, config.RedirectUri, scopes, InstaSharp.OAuth.ResponseType.Code);
return link;
}
public static async Task<OAuthResponse> OAuth(string code, InstagramConfig config)
{
// add this code to the auth object
var auth = new OAuth(config);
// now we have to call back to instagram and include the code they gave us
// along with our client secret
return await auth.RequestToken(code);
}
public static async Task<TagResponse> LoadInstagramPosts(String hashTagTerm, InstagramConfig config, OAuthResponse OAuth)
{
var tagApi = new InstaSharp.Endpoints.Tags(config, OAuth);
return await tagApi.Get("soccer");
}
I'm a bit late to the show, yet probably my answer will help someone who find this question when googling, someone like me.
The main problem with your approach is that Instagram is using OAuth authentication. I suggest you to google on OAuth to understand the principles, but I will try to explain the practical points of it below.
OAuth approach means that the result of the InstagramLogin method in the snippet above is not the code. It's the link where you need to send you user (yes, using a browser or a web-view) so that they can sign into their Instagram account and then authorize your application to access their data (so-called user consent screen).
In the end, after user consent, Instagram will redirect browser to the URL of your choice (it should be previously added in the list of allowed redirect urls in Instagram API -> Manage Clients (top-right corner) -> Create/Select client to Manage -> Security tab)
You can try set a breakpoint and copy the value of instagramLoginLink into your browser's address box. You will be able to see the whole flow of authentication and consent - and finally the redirect url that will most probably produce 404 in your browser.
This final link will contain the code in a get parameter. It's the so-called grant code that allows you to get an access token. This code is to be extracted from url and then used in your call to OAuth.RequestToken).
PS: Yes, everything I say above means that you need either a web app running that will redirect user to Instagram or a client-side app that will show the user a web view and somehow handle the moment when Instagram sends the user back to your redirect url - to grab the code and proceed.
I have been sitting all day and can not figure this out. I have made seaches on stackoverflow and the web but have found no answers.
Setup
This is not the optimal setup but it is what I have to deal with because of legacy code.
I have a FileController (API controller) class hosted on the client web that is used to upload files. A MVC Controller is making a jquery.ajax call throug angular to the FileController.UploadFiles(). The FileController.UploadFiles() get some data out from the database and then send a request to the storageAPI which store the file in a blob in a certain way depending on the settings parameter inside the CustomResponse object.
The problem is that the parameter CustomResponse is null sometimes when it gets to the controller and sometimes I actually has a value. It's seems to be totally random.
I have tested it manually by clicking a upload button on my Client GUI that calls the FileController with Ajax which in turn calls the API. Both the client web and the storage api is hosted locally and hosted on a local IIS. Can it be a collision?
It's not null when I use the Swagger UI to do posts. Then it gets the content parameter everytime.
The request always reach the Storage API when i debug. So the "wiring" between them is fine.
I have tried to do both async and not async calls and have change the method in the API also to be sync and async no difference.
Code
Code
FileController (CLIENT)
byte[] fileBytes = null;
using(var memoryStream = new MemoryStream())
{
file.InputStream.CopyTo(memoryStream);
fileBytes = memoryStream.ToArray();
}
var fileBytesString = System.Text.Encoding.UTF8.GetString(fileBytes);
var settingsString = "renditions:large,thumbail;assettype:testdataimage;testdataid:" + testData.Id.ToString() + ";filename:" + fileName;
var client = new StorageAPI();
client.BaseUri = new Uri("http://storageapi.mycompany.local/");
CustomResponse response = null;
response = client.Blob.Post(new CustomRequest()
{
FileBytesString = fileBytesString,
Settings = settingsString
});
client.Dispose();
StorageAPI Model CustomRequest
public class CustomRequest
{
public string FileBytesString { get; set; }
public string Settings { get; set; }
}
StorageAPI Controller
[HttpPost]
[ResponseType(typeof(CustomResponse))]
[Route("api/Blob/")]
public HttpResponseMessage Post(CustomRequest content)
{
if (content == null)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
//Parse data and upload file to blob.
return new HttpResponseMessage(HttpStatusCode.OK);
}
I'm looking forward to get some feedback to what I'm doing wrong here.
Regards
First of all thanks for your helpful comments that leed me in the right direction.
I configured both the API and the Web to go through the Fiddler Proxy and looked at the request and nothing that matter was different.
Then I again started to look at the content I'm sending (pictures) and it turns out it is a size issue, the larger picture did not go through but the smaller did not. I searched for what the default size is for a ASP.NET application and it is 4096 kB and one of my images was larger than that!
This line in the web.config in the system.web section solved the problem.
<httpRuntime targetFramework="4.5" maxRequestLength="8192"/>
This has been a good learning for me and I hope it might help someone else that is new to this sending large files over the wire thing!
Thanks!
I am trying to upload an image to Twitter using Twitter API Version 1.1 and the update_with_media.json method.
https://dev.twitter.com/docs/api/1.1/post/statuses/update_with_media
This is the code I have so far, yet despite numerous variations I can not get a successful upload.
public TwitterResponse UpdateStatus(string message, String fileName, String contentType, byte[] image)
{
RestClient client = new RestClient
{
Authority = TwitterConstants.Authority,
VersionPath = TwitterConstants.Version
};
message = HttpUtility.HtmlEncode(message);
client.AddHeader("content-type", "multipart/form-data");
client.AddField("status", message);
client.AddField("media[]", Convert.ToBase64String(image) + ";filename=" + fileName + ";type=" + contentType);
RestRequest request = new RestRequest
{
Credentials = this.Credentials,
Path = "statuses/update_with_media.json",
Method = Hammock.Web.WebMethod.Post
};
return new TwitterResponse(client.Request(request));
}
I am using Hammock to perform these requests.
Just to rule out possible other issues, I can successfully post a status update to Twitter using the update.json method.
I have also tried using the client.AddFile method and using Fiddler it looks like everything is in place. But the error message I keep getting back is
{"errors":[{"code":195,"message":"Missing or invalid url parameter"}]}
Instead of using native Twitter API, you can use TweeterSharp plugin available at Nuget.
Sample with description is written at this article by me Post message with image on twitter using C#
In particular this is the code snippet
using (var stream = new FileStream(imagePath, FileMode.Open))
{
var result = service.SendTweetWithMedia(new SendTweetWithMediaOptions
{
Status = message,
Images = new Dictionary<string, Stream> { { "john", stream } }
});
lblResult.Text = result.Text.ToString();
}
The complete demo is downloadable attached with the article, feel free to download.
Thanks
I've never used Hammock or or c#, but I know that we had a similar issue...
Our core twitter library worked for everything, but we couldn't get image uploads to work. It turns out that the OAuth library that our twitter lib depended on didn't calculate the signature properly when posting files. We had to update our oauth to get it work.
In our case the exact code we were trying to use worked fine once I substituted an updated OAuth.
If you are using an older version of OAuth, I would suggest looking for a more recent version, and pulling together a quick script to try with that.
Regarding that error message, it may be more of a red herring than a valid message - especially because it's not even listed on their error page:
https://dev.twitter.com/docs/error-codes-responses
I'm currently following a tutorial on how to show Twitter feeds with LINQ to XML in C#. However, seeing as Microsoft has replaced WebClient with HTTPClient, I'm unsure how to proceed on my quest of integrating this into my Windows 8 application.
The tutorial: http://www.codeproject.com/Articles/117614/How-to-query-twitter-public-status-using-LINQ-to-X
The thing is that I'm "halfway" used to DownloadStringCompleted, which I believe is not in the HTTPClient api. So after searching for a while on the async/await functionality, I mixed some of my knowledge together with some tutorials and snippets I found, but what I ended up with would not work:
public async Task<XElement> GetXmlAsync()
{
var client = new HttpClient();
var response = await client.GetAsync("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=VladimirPutin");
var text = response.Content.ReadAsStringAsync();
return XElement.Parse(text.Result);
XElement xmlTweets = XElement.Parse(text.Result);
listboxTweetsSecond.ItemsSource = from tweet in xmlTweets.Descendants("status")
select new UserTweet
{
UserImageSrc = tweet.Element("user").Element("profile_image_url").Value,
UserMessage = tweet.Element("text").Value,
UserName = tweet.Element("user").Element("screen_name").Value
};
}
(Yeah, the Twitter username is just a placeholder)
I do not need an entire code as an answer, but I'd love it if someone could point out where I should go from here, as well as what I should do in general, as this (of course) does not work (as in, it does not show anything when I run it/debug mode).
Comment out the line
return XElement.Parse(text.Result);
and it should probably work.
Having done that (i.e. you are no longer returning an XElement) you can probably change the method signature top return just a task.
public async Task GetXmlAsync()
{
// method body
}