I am using TweetSharp in a Windows Phone project and no matter what I do, I can't post a tweet with media.
I am getting the exception 195: Missing or invalid parameter.
I read that usually this can be a cause of invalid data, like the stream that I provide is invalid.
I have tried other way but nothing works , I get the same exception ...
The sharing code, simplified is like this:
MediaLibrary library = new MediaLibrary();
var picture = library.Pictures[0];
var options = new SendTweetWithMediaOptions
{
Images = new Dictionary<string, Stream> { { picture.Name, picture.GetImage() } },
Status = TweetTextBox.Text,
};
AutentificateTwitterService().SendTweetWithMedia(options, (status, response) =>
_dispatcher.BeginInvoke(() =>
{
DonePosting();
if (response.StatusCode == HttpStatusCode.OK)
{
_lastPostId = status.Id;
}
else
{
MessageBox.Show(String.Format(
"There was an error sending image to Twitter{0}{1}",
Environment.NewLine,
response.Error));
}
}));
I tried sharing with linqtotwitter and worked but TweetSharp is more appropriate for my project.
Finally after some time I've found the problem to this and I am sure to other more WP and SendTweetWithMediaOptions related problems.
The thing is that if you dig into SendTweetWithMedia the way it is now you will get to TwitterService.cs where WithHammock will be called, is just the images are not passed as parrameters, so they get lost right there :)
I did fix this passing the parameters and adding
private void WithHammock<T>(WebMethod method, Action<T, TwitterResponse> action, string path, IDictionary<string, Stream> files, params object[] segments) where T : class
{
var url = ResolveUrlSegments(path, segments.ToList());
var request = PrepareHammockQuery(url);
request.Method = method;
request.QueryHandling = QueryHandling.AppendToParameters;
foreach (var file in files)
{
request.AddFile("media[]", file.Key, file.Value);
}
WithHammockImpl(request, action);
}
I will try and see if I can Pull this so that everyone else can have this fix.
Hope this helps.
Related
I am trying to build a service client to simplify calling my microservices in .net core.
Here is a service client sample:
public ProductServiceClient(SystemEnvironment.MachineEnvironment? environment = null)
{
this.url = ServiceEnvironment.Urls.GetUrl(ServiceEnvironment.Service.Product, environment);
}
private RestClient GetClient(string method)
{
return new RestClient(url + "/api/" + method);
}
private RestRequest GetRestRequest(Method method)
{
var restRequest = new RestRequest(method);
restRequest.RequestFormat = DataFormat.Json;
restRequest.AddHeader("Content-Type", "application/json");
return restRequest;
}
public FindProductsResponse FindProducts(FindProductsRequest request)
{
var restRequest = GetRestRequest(Method.GET);
restRequest.AddJsonBody(request);
var client = this.GetClient("Products");
var restResponse = client.Get(restRequest);
return new JsonDeserializer().Deserialize<FindProductsResponse>(restResponse);
}
public void Dispose()
{
}
And here is how I am trying to read it in my .net core api:
[HttpGet]
public ActionResult<FindProductsResponse> Get()
{
var request = "";
using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
{
request = reader.ReadToEnd();
}
var buildRequest = JsonConvert.DeserializeObject<FindProductsRequest>(request);
var products = _service.FindProducts(buildRequest);
if (products != null && products.Any())
{
return new FindProductsResponse()
{
Products = products
};
}
return BadRequest("Not found");
}
However the request variable is always empty after Request.Body has been processed by the StreamReader.
If I make the same request from Postman (also using GET), I get the body just fine.
What am I doing wrong here?
EDIT: This is the unit test calling the api:
[Test]
public void Test1()
{
using (var productServiceClient = new ProductServiceClient())
{
var products = productServiceClient.FindProducts(new FindProductsRequest()
{
Id = 50
}).Products;
}
}
It can be your Request.Body has been already consumed.
Try to call Request.EnableRewind() before to open the StreamReader.
I'm not sure why you are manually doing it. It looks like you are reinventing the wheel. ASP.NET Core already does that for you.
This is what your service should look like:
[HttpGet] // oops, GET requests will not allow Bodies, this won't work
public ActionResult<FindProductsResponse> Get([FromBody]FindProductsRequest buildRequest)
{
// skip all the serialization stuff, the framework does that for you
var products = _service.FindProducts(buildRequest);
if (products != null && products.Any())
{
return new FindProductsResponse()
{
Products = products
};
}
return BadRequest("Not found");
}
And if you don't want to redo all the busy work that is retyping all the code on the client side, I suggest you read up on swagger (probably in the form of Swashbuckle). Client code can be generated. Even from within Visual Studio, if you right-click on the project and in the context menu pick "Add REST API Client...". Please don't erroneously hand-code what can be generated flawlessly by a machine instead. I don't really know what went wrong in your specific case, but searching bugs that could be avoided altogether is just busywork, that time should be spent on other parts of the program.
I just realized this is a GET request. ASP.NET will not recognize bodies for GET-Requests. You will need to make it a PUT or POST request or put your parameters in the query string.
If you happen to make that mistake as often as I did, you might want to write some unit tests that cover this. Because .NET is not helping you there. Been there, done that..
I have searched many times for my problem but I couldn't find any solution.
the problem which faced is I wrote method yesterday in asp.net core API which is return file response as streaming video when I tried to test on my local machine it's working very well, but when uploading it to my VPS server I got the 500 internal server error so please what is the problem.
the following my code which I used:
[HttpPost("youtube")]
public async Task<IActionResult> GetYoutubeVideoURL([FromBody] string url)
{
if (string.IsNullOrEmpty(url))
{
return Ok(new { Error = "Please provide your url for fetch video." });
}
var id = YoutubeClient.ParseVideoId(url);
if (id == null)
{
return Ok(new { Error = "Video id is not available, try again later." });
}
var client = new YoutubeClient(); //This should be initialized in YoutubeController constructor.
var mediaInfoSet = await client.GetVideoMediaStreamInfosAsync(id);
var mediaStreamInfo = mediaInfoSet.Audio.WithHighestBitrate();
var mimeType = $"audio/{mediaStreamInfo.Container.GetFileExtension()}";
var fileName = $"{id}.{mediaStreamInfo.Container.GetFileExtension()}";
return File(await client.GetMediaStreamAsync(mediaStreamInfo), mimeType, fileName, true);
}
I'm trying to access a request's raw input body/stream in ASP.net 5. In the past, I was able to reset the position of the input stream to 0 and read it into a memory stream but when I attempt to do this from the context the input stream is either null or throws an error (System.NotSupportedException => "Specified method is not supported.").
In the first example below I can access the raw request in a controller if I declare the controller method's parameter object type as dynamic. For various reasons, this is not a solution and I need to access the raw request body in an authentication filter anyways.
This Example Works, But Is Not a Reasonable Solution:
[HttpPost("requestme")]
public string GetRequestBody([FromBody] dynamic body)
{
return body.ToString();
}
Throws Error:
[HttpPost("requestme")]
public string GetRequestBody()
{
var m = new MemoryStream();
Request.Body.CopyTo(m);
var contentLength = m.Length;
var b = System.Text.Encoding.UTF8.GetString(m.ToArray());
return b;
}
Throws Error:
[HttpPost("requestme")]
public string GetRequestBody()
{
Request.Body.Position = 0;
var input = new StreamReader(Request.Body).ReadToEnd();
return input;
}
Throws Error:
[HttpPost("requestme")]
public string GetRequestBody()
{
Request.Body.Position = 0;
var input = new MemoryStream();
Request.Body.CopyTo(input);
var inputString = System.Text.Encoding.UTF8.GetString(input.ToArray());
return inputString;
}
I need to access the raw request body of every request that comes in for an API that I am building.
Any help or direction would be greatly appreciated!
EDIT:
Here is the code that I would like to read the request body in.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Http;
namespace API.Filters
{
public class CustomAuthorizationAttribute : Attribute, IAuthorizationFilter
{
public CustomAuthorizationAttribute()
{ }
public void OnAuthorization(AuthorizationContext context)
{
if (context == null)
throw new ArgumentNullException("OnAuthorization AuthorizationContext context can not be null.");
else
{
if (this.AuthorizeCore(context.HttpContext) == false)
{
// Do Other Stuff To Check Auth
}
else
{
context.Result = new HttpUnauthorizedResult();
}
}
}
protected virtual bool AuthorizeCore(HttpContext httpContext)
{
var result = false;
using (System.IO.MemoryStream m = new System.IO.MemoryStream())
{
try
{
if (httpContext.Request.Body.CanSeek == true)
httpContext.Request.Body.Position = 0;
httpContext.Request.Body.CopyTo(m);
var bodyString = System.Text.Encoding.UTF8.GetString(m.ToArray());
return CheckBody(bodyString); // Initial Auth Check returns true/false <-- Not Shown In Code Here on Stack Overflow
}
catch (Exception ex)
{
Logger.WriteLine(ex.Message);
}
}
return false;
}
}
}
This code would be accessed when a call is made to a controller method marked with the CustomAuthorization attribute like so.
[Filters.CustomAuthorizationAuthorization]
[HttpPost]
public ActionResult Post([FromBody]UserModel Profile)
{
// Process Profile
}
Update
The information below is pretty outdated by now. Due to performance reasons this is not possible by default, but fortunately can be changed. The latest solution should be to enable request buffering with EnableBuffering:
Request.EnableBuffering();
See also this blog post for more information: https://devblogs.microsoft.com/aspnet/re-reading-asp-net-core-request-bodies-with-enablebuffering/.
Old, outdated answer for reference
The implementation of Request.Body depends on the controller action.
If the action contains parameters it's implemented by Microsoft.AspNet.WebUtilities.FileBufferingReadStream, which supports seeking (Request.Body.CanSeek == true). This type also supports setting the Request.Body.Position.
However, if your action contains no parameters it's implemented by Microsoft.AspNet.Loader.IIS.FeatureModel.RequestBody, which does not support seeking (Request.Body.CanSeek == false). This means you can not adjust the Position property and you can just start reading the stream.
This difference probably has to do with the fact that MVC needs to extract the parameters values from the request body, therefore it needs to read the request.
In your case, your action does not have any parameters. So the Microsoft.AspNet.Loader.IIS.FeatureModel.RequestBody is used, which throws an exception if you try to set the Position property.
**Solution**: either do not set the position or check if you actually _can_ set the position first:
if (Request.Body.CanSeek)
{
// Reset the position to zero to read from the beginning.
Request.Body.Position = 0;
}
var input = new StreamReader(Request.Body).ReadToEnd();
The exceptions you see in your three last snippets are the direct consequence of trying to read the request body multiple times - once by MVC 6 and once in your custom code - when using a streamed host like IIS or WebListener. You can see this SO question for more information: Read body twice in Asp.Net 5.
That said, I'd only expect this to happen when using application/x-www-form-urlencoded, since it wouldn't be safe for MVC to start reading the request stream with lengthy requests like file uploads. If that's not the case, then it's probably a MVC bug you should report on https://github.com/aspnet/Mvc.
For workarounds, you should take a look at this SO answer, that explains how you can use context.Request.ReadFormAsync or add manual buffering: Read body twice in Asp.Net 5
app.Use(next => async context => {
// Keep the original stream in a separate
// variable to restore it later if necessary.
var stream = context.Request.Body;
// Optimization: don't buffer the request if
// there was no stream or if it is rewindable.
if (stream == Stream.Null || stream.CanSeek) {
await next(context);
return;
}
try {
using (var buffer = new MemoryStream()) {
// Copy the request stream to the memory stream.
await stream.CopyToAsync(buffer);
// Rewind the memory stream.
buffer.Position = 0L;
// Replace the request stream by the memory stream.
context.Request.Body = buffer;
// Invoke the rest of the pipeline.
await next(context);
}
}
finally {
// Restore the original stream.
context.Request.Body = stream;
}
});
I just had this same issue. Remove the parameters from the method signature, and then read the Request.Body Stream how you want to.
You need to call Request.EnableRewind() to allow the stream to be rewound so you can read it.
string bodyAsString;
Request.EnableRewind();
using (var streamReader = new StreamReader(Request.Body, Encoding.UTF8))
{
bodyAsString = streamReader.ReadToEnd();
}
I Know this my be late but in my case its Just I had a problem in routing as bellow
At startup.cs file I was beginning the routing with /api
app.MapWhen(context => context.Request.Path.StartsWithSegments(new PathString("/api")),
a =>
{
//if (environment.IsDevelopment())
//{
// a.UseDeveloperExceptionPage();
//}
a.Use(async (context, next) =>
{
// API Call
context.Request.EnableBuffering();
await next();
});
}
//and I was putting in controller
[HttpPost]
[Route("/Register", Name = "Register")]
//Just Changed the route to start with /api like my startup.cs file
[HttpPost]
[Route("/api/Register", Name = "Register")]
//and now the params are not null and I can ready the body request multiple
NOTE: Using the Sign-in button is NOT an option
A year ago I was having a problem creating a moment. Back then I was using version 1.2 of the Google+ API .Net client. As I described in this post, I had it working although the code failed to insert a moment from time to time. I was hoping that the process is more stable and easier to implement now, and it seems like it as can be seen in the example that you can download here - the current version as of this writing is v1.8. So I created a simple project following the SimpleOAuth2 sample in the download, but implementing Google+. This is the code I came up:
public partial class _Default : System.Web.UI.Page
{
private PlusService service;
// Application logic should manage users authentication.
// This sample works with only one user. You can change
// it by retrieving data from the session.
private const string UserId = "user-id";
protected void Page_Load(object sender, EventArgs e)
{
GoogleAuthorizationCodeFlow flow;
var assembly = Assembly.GetExecutingAssembly();
using (var stream = assembly.GetManifestResourceStream(
"GPlusSample.client_secrets.json"))
{
flow = new GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer
{
DataStore = new FileDataStore("GPlusSample.Store"),
ClientSecretsStream = stream,
//
// Tried only this scope but it did not work
//Scopes = new[] { PlusService.Scope.PlusMe }
//
// I tried the following: but did not work either
//Scopes = new[] { PlusService.Scope.PlusMe,
// "https://www.googleapis.com/auth/plus.moments.write" }
//
// I tried this as well and it failed
//Scopes = new[] { PlusService.Scope.PlusLogin }
//
// Maybe this... but still no joy
Scopes = new[] { PlusService.Scope.PlusLogin,
PlusService.Scope.PlusMe }
});
}
var uri = Request.Url.ToString();
var code = Request["code"];
if (code != null)
{
var token = flow.ExchangeCodeForTokenAsync(UserId, code,
uri.Substring(0, uri.IndexOf("?")), CancellationToken.None).Result;
// Extract the right state.
var oauthState = AuthWebUtility.ExtracRedirectFromState(
flow.DataStore, UserId, Request["state"]).Result;
Response.Redirect(oauthState);
}
else
{
var result = new AuthorizationCodeWebApp(flow, uri, uri)
.AuthorizeAsync(UserId, CancellationToken.None).Result;
if (result.RedirectUri != null)
{
// Redirect the user to the authorization server.
Response.Redirect(result.RedirectUri);
}
else
{
// The data store contains the user credential,
// so the user has been already authenticated.
service = new PlusService(new BaseClientService.Initializer
{
ApplicationName = "Plus API Sample",
HttpClientInitializer = result.Credential
});
}
}
}
/// <summary>Gets the TasksLists of the user.</summary>
public async System.Threading.Tasks.Task InsertMoment()
{
try
{
var me = service.People.Get("me").Execute();
var request = service.Moments.Insert(new Moment()
{
Target = new ItemScope {
Id=Guid.NewGuid().ToString(),
Image="http://www.google.com/s2/static/images/GoogleyEyes.png",
Type="",
Name = "test message",
Description="test",
Text="test message",
},
Type = "http://schemas.google.com/AddActivity",
}, me.Id, MomentsResource.InsertRequest.CollectionEnum.Vault);
var response =await request.ExecuteAsync();
output.Text = "<h1>" + response.Id + "</h1>";
}
catch (Exception ex)
{
var str = ex.ToString();
str = str.Replace(Environment.NewLine, Environment.NewLine + "<br/>");
str = str.Replace(" ", " ");
output.Text = string.Format("<font color=\"red\">{0}</font>", str);
}
}
protected async void createMomentButton_Click(object sender, EventArgs e)
{
await InsertMoment();
}
}
That code always give me a 401 Unauthorized error, even if I have the Google+ API turned on for my project. Here's the actual error I got:
The service plus has thrown an exception: Google.GoogleApiException:
Google.Apis.Requests.RequestError Unauthorized [401] Errors [
Message[Unauthorized] Location[ - ] Reason[unauthorized]
Domain[global] ]
It's interesting to see that the insert moment is failing even though the call to People.Get("me") works - get("me") works with all of the scope combinations I listed above. It's important to note that each time I try a new scope, I first log out of my Google account and delete the access token that is stored in GPlusSample.Store.
EDIT
I tried setting just the Url instead of individual items as suggested by Ian and I got the exact same error.
var request = service.Moments.Insert(new Moment()
{
Target = new ItemScope {
Url = "https://developers.google.com/+/web/snippet/examples/thing"
},
Type = "http://schemas.google.com/AddActivity",
}, me.Id, MomentsResource.InsertRequest.CollectionEnum.Vault);
var response =await request.ExecuteAsync();
https://www.googleapis.com/auth/plus.login is the right scope for writing moments, but you need to have requested the specific app activity types you want to write as well. The parameter for this is request_visible_actions, and it takes a space separated list of arguments of the types (Listed on https://developers.google.com/+/api/moment-types/ - e.g. http://schemas.google.com/AddActivity).
The client library may not have a method for adding request_visible_actions, so you may have to add it on to the auth URL you redirect the user to manually (remember to URLencode the app activity type URLs!)
Ok, so I am writing a service to recieve file uploads from an iPhone application through phonegap. They send me a file and I am trying to grab the actual file without saving it to any type of file system. Currently this is what I have
[HttpPost]
public string processRequest()
{
string ext = "Entered";
Request.Content.ReadAsMultipartAsync<MultipartMemoryStreamProvider>(new MultipartMemoryStreamProvider()).ContinueWith((tsk) =>
{
ext = "Request";
MultipartMemoryStreamProvider prvdr = tsk.Result;
foreach (HttpContent ctnt in prvdr.Contents)
{
ext = "Foreach";
// You would get hold of the inner memory stream here
Stream stream = ctnt.ReadAsStreamAsync().Result;
if (stream == null)
{
ext = "Null Stream";
}
Image img = Image.FromStream(stream);
if (ImageFormat.Jpeg.Equals(img.RawFormat))
{
ext = "jpeg";
}
else if (ImageFormat.Png.Equals(img.RawFormat))
{
ext = "Png";
}
else if (ImageFormat.Gif.Equals(img.RawFormat))
{
ext = "Gif";
}
// do something witht his stream now
}
});
return ext;
}
I have put various responses in there so I can see where the function is getting to. Right now it always returns "Entered" which means its not even reading the content of the request, the end game is for me to grab the file object, convert it into an image and then to base 64. Any direction would be appreciated. Remember I want to do this without any file system so no solutions that involve mapping a path to a server folder.
Ok so a little update, I have edited my code according to my first response and at least it attempts to execute now but it just gets infinitely stuck inside the code. This happens during the ReadAsMultipartAsync function
[HttpPost]
public string processRequest()
{
string ext = "Entered";
Request.Content.ReadAsMultipartAsync(new MultipartMemoryStreamProvider()).ContinueWith((tsk) =>
{
ext = "Request";
MultipartMemoryStreamProvider prvdr = tsk.Result;
foreach (HttpContent ctnt in prvdr.Contents)
{
ext = "Foreach";
// You would get hold of the inner memory stream here
Stream stream = ctnt.ReadAsStreamAsync().Result;
if (stream == null)
{
ext = "Null Stream";
}
Image img = Image.FromStream(stream);
if (ImageFormat.Jpeg.Equals(img.RawFormat))
{
ext = "jpeg";
}
else if (ImageFormat.Png.Equals(img.RawFormat))
{
ext = "Png";
}
else if (ImageFormat.Gif.Equals(img.RawFormat))
{
ext = "Gif";
}
// do something witht his stream now
}
}).Wait();
return ext;
}
The block inside ContinueWith also runs asynchronously (if you look at the signature for ContinueWith, you'll see that it returns a Task as well). So, with the above code, essentially you're returning before any of that has a chance to execute.
Try doing:
Request.Content.ReadAsMultipartAsync().ContinueWith(...).Wait();
Also, not sure you need to go to the trouble of doing Request.Content.ReadAsMultipartAsync<MultipartMemoryStreamProvider>(new MultipartMemoryStreamProvider()); I believe Request.Content.ReadAsMultipartAsync() should suffice.
Hope that helps!