Why is the response content in this streaming service example empty? - stream

I could really use some help understanding why this unit test is failing. I suspect it's due to the way I'm handling the streams. I have a number of other tests that successfully use this self-hosting server setup, but they all read services that return primitives like strings.
Here's the test in question:
using System.Net.Http;
using System.Threading;
using System.Web.Http.SelfHost;
using AttributeRouting.Web.Http.SelfHost;
using NUnit.Framework;
[TestFixture]
public class StreamControllerTests
{
[Test]
public void Can_get_simple_streaming_service_to_respond()
{
using (var config = new HttpSelfHostConfiguration("http://in-memory"))
{
config.Routes.MapHttpAttributeRoutes();
using (var server = new HttpSelfHostServer(config))
{
// I get the same behavior if I use HttpClient
using (var client = new HttpMessageInvoker(server))
{
using (var request = new HttpRequestMessage(HttpMethod.Get, "http://in-memory/stream/notepad"))
{
using (HttpResponseMessage response = client.SendAsync(request, CancellationToken.None).Result)
{
Assert.IsNotNull(response.Content);
// FAILS, content length is 0
Assert.Greater(response.Content.Headers.ContentLength, 0);
}
}
}
}
}
And here is the controller that feeds the test:
using System;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using AttributeRouting.Web.Mvc;
using MyUnitTests.Properties;
[GET("stream/notepad")]
public HttpResponseMessage StreamAnImageFromResources()
{
var imageStream = new MemoryStream(); // closed when stream content is read
Resources.a_jpeg_in_resources.Save(imageStream, ImageFormat.Jpeg);
try
{
HttpResponseMessage response = Request.CreateResponse();
// at this point, imageStream contains about 120K bytes
response.Content = new StreamContent(imageStream);
return response;
}
catch (Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.ServiceUnavailable, e);
}
}

I don't see anything really wrong but your test is more complicated than it needs to be.
Try this,
[Test]
public void Can_get_simple_streaming_service_to_respond2()
{
var config = new HttpConfiguration();
config.Routes.MapHttpAttributeRoutes();
var server = new HttpServer(config);
var client = new HttpClient(server);
var request = new HttpRequestMessage(HttpMethod.Get, "http://in-memory/stream/notepad");
HttpResponseMessage response = client.SendAsync(request, CancellationToken.None).Result;
Assert.IsNotNull(response.Content);
// FAILS, content length is 0
Assert.Greater(response.Content.Headers.ContentLength, 0);
}
EDIT: In the comments, Darrel gave me the true answer, which I'm moving to the answer body for visibility:
Check the position of your image stream after doing Save. You need to reset it back to 0 before passing to StreamContent. Also, you might want to consider doing GetManifestResourceStream instead, it will save copying the bytes into managed memory.

Related

logic apps web hook to chalkboard API timeout error

How do I change the timeout duration in logic apps web hook and also in chalkboard API.
The error message I get is.
"message": "Http request failed: the server did not respond within the timeout limit. Please see logic app limits at https://aka.ms/logic-apps-limits-and-config#http-limits"
You can refer to Perform long-running tasks with the webhook action pattern.
After understanding the webhook pattern, you need to design some code, you can refer to the following sample:
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Host;
using Newtonsoft.Json;
using System.Threading;
using System.Net.Http;
using System;
namespace HttpToQueueWebhook
{
public static class HttpTrigger
{
[FunctionName("HttpTrigger")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "post")]HttpRequest req,
TraceWriter log,
[Queue("process")]out ProcessRequest process)
{
log.Info("Webhook request from Logic Apps received.");
string requestBody = new StreamReader(req.Body).ReadToEnd();
dynamic data = JsonConvert.DeserializeObject(requestBody);
string callbackUrl = data?.callbackUrl;
//This will drop a message in a queue that QueueTrigger will pick up
process = new ProcessRequest { callbackUrl = callbackUrl, data = "some data" };
return new AcceptedResult();
}
public static HttpClient client = new HttpClient();
/// <summary>
/// Queue trigger function to pick up item and do long work. Will then invoke
/// the callback URL to have logic app continue
/// </summary>
[FunctionName("QueueTrigger")]
public static void Run([QueueTrigger("process")]ProcessRequest item, TraceWriter log)
{
log.Info($"C# Queue trigger function processed: {item.data}");
//Thread.Sleep(TimeSpan.FromMinutes(3));
//ProcessResponse result = new ProcessResponse { data = "some result data" };
//handle your business here.
client.PostAsJsonAsync<ProcessResponse>(item.callbackUrl, result);
}
}
public class ProcessRequest
{
public string callbackUrl { get; set; }
public string data { get; set; }
}
public class ProcessResponse
{
public string data { get; set; }
}
}
The above code will first save your callbackUrl and the passed data to the queue, and then return the result of 202 to the logic app.
The QueueTrigger function will be triggered, and you can handle your business here.
You can call your http function like this in Azure logic app:
This solution can help you solve the http timeout problem. For more details, you can refer to this article.

How do I use OData $filter results on the server

I have a working OData controller, which supports all the normal get/put etc.
What I want to do is pass a normal odata $filter string from the client, parse and execute the filter on the server and run some code on the resulting IEnumerable.
I've messed around with ODataQueryContext, ODataQueryOptions, FilterQueryOption etc, but not really got anywhere.
Does anyone have any working examples?
Edit: I've added my function skeleton, just need to fill in the blanks
public HttpResponseMessage GetJobs(string filter)
{
*** How to convert the filter into IQueryable<Job> ***
var queryable = ?????
var settings = new ODataQuerySettings();
var jobs = queryOptions.ApplyTo(querable, settings) as IQueryable<Job>;
CsvSerializer csvSerializer = new CsvSerializer();
string csv = csvSerializer.Serialise(jobs);
string fileName = string.Format("{0} Jobs.csv", filter);
return CreateCsvResponseMessage(csv, fileName);
}
I recently had a scenario where I needed this sort of feature as well. This is what I came up with.
private static IQueryable<T> ApplyODataFilter<T>(IQueryable<T> data, string filterString) where T : class
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<T>(typeof(T).Name);
ODataQueryContext context = new ODataQueryContext(builder.GetEdmModel(), typeof(T), new ODataPath());
ODataQueryOptionParser queryOptionParser = new ODataQueryOptionParser(
context.Model,
context.ElementType,
context.NavigationSource,
new Dictionary<string, string> { { "$filter", filterString } });
FilterQueryOption filter = new FilterQueryOption(filterString, context, queryOptionParser);
IQueryable query2 = filter.ApplyTo(data, new ODataQuerySettings());
return query2.Cast<T>();
}
Try using OData code generator to generate client side code. you can following the following blog:
How to use OData Client Code Generator to generate client-side proxy class
The for the filter, the following is an example:
var q2 = TestClientContext.CreateQuery<Type>("Accounts").Where(acct => acct.Birthday > new DateTimeOffset(new DateTime(2013, 10, 1)));
There are some sample code in the codeplex to show how to do query.
Check this:
https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v3/ODataQueryableSample/Program.cs
Update:
There is some sample code in the controller of the sample I gave you.
Write your code as below:
public IQueryable<Order> Get(ODataQueryOptions queryOptions)
{
if (queryOptions.Filter != null)
{
var settings = new ODataQuerySettings();
var filterResult = queryOptions.ApplyTo(OrderList.AsQueryable(), settings) as IQueryable<Order>;
// Use the filter result here.
}
}
Update 2:
You can get the raw string of the filter from ODataQueryOptions.
public IQueryable<Order> Get(ODataQueryOptions queryOptions)
{
string filterString = queryOptions.Filter.RawValue;
// Use the filterString
}
Update 3:
(Note: ODataProperties is an extension method in static class
System.Web.Http.OData.Extensions.HttpRequestMessageExtensions)
public HttpResponseMessage GetJobs(string filter)
{
var context = new ODataQueryContext(Request.ODataProperties().Model, typeof(Job));
var filterQueryOption = new FilterQueryOption(filter, context);
IQueryable<Job> queryable = GetAllJobs();
var settings = new ODataQuerySettings();
var jobs = filterQueryOption.ApplyTo(queryable, settings) as IQueryable<Job>;
CsvSerializer csvSerializer = new CsvSerializer();
string csv = csvSerializer.Serialise(jobs);
string fileName = string.Format("{0} Jobs.csv", filter);
return CreateCsvResponseMessage(csv, fileName);
}

File Name from HttpRequestMessage Content

I implemented a POST Rest service to upload files to my server. the problem i have right now is that i want to restrict the uploaded files by its type. lets say for example i only want to allow .pdf files to be uploaded.
What I tried to do was
Task<Stream> task = this.Request.Content.ReadAsStreamAsync();
task.Wait();
FileStream requestStream = (FileStream)task.Result;
but unfortunately its not possible to cast the Stream to a FileStream and access the type via requestStream.Name.
is there an easy way (except writing the stream to the disk and check then the type) to get the filetype?
If you upload file to Web API and you want to get access to file data (Content-Disposition) you should upload the file as MIME multipart (multipart/form-data).
Here I showed some examples on how to upload from HTML form, Javascript and from .NET.
You can then do something like this, this example checks for pdf/doc files only:
public async Task<HttpResponseMessage> Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable,
"This request is not properly formatted - not multipart."));
}
var provider = new RestrictiveMultipartMemoryStreamProvider();
//READ CONTENTS OF REQUEST TO MEMORY WITHOUT FLUSHING TO DISK
await Request.Content.ReadAsMultipartAsync(provider);
foreach (HttpContent ctnt in provider.Contents)
{
//now read individual part into STREAM
var stream = await ctnt.ReadAsStreamAsync();
if (stream.Length != 0)
{
using (var ms = new MemoryStream())
{
//do something with the file memorystream
}
}
}
return Request.CreateResponse(HttpStatusCode.OK);
}
}
public class RestrictiveMultipartMemoryStreamProvider : MultipartMemoryStreamProvider
{
public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
{
var extensions = new[] {"pdf", "doc"};
var filename = headers.ContentDisposition.FileName.Replace("\"", string.Empty);
if (filename.IndexOf('.') < 0)
return Stream.Null;
var extension = filename.Split('.').Last();
return extensions.Any(i => i.Equals(extension, StringComparison.InvariantCultureIgnoreCase))
? base.GetStream(parent, headers)
: Stream.Null;
}
}

How do I deserialize an a list of objects from a Memory Stream through WCF

MVC 4.0
I have the following running on a service:
[OperationContract(Name = "GetHierarchyReportContents")]
[FaultContract(typeof(InvalidHierarchyNameException))]
[ServiceKnownType(typeof(Node))]
MemoryStream GetContents();
This function provides a memory stream which contains a list of Node (APINode because of an alias). Essentially, all it does is the following:
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
formatter.Serialize(stream, data.ToList<APINode>());
stream.Seek(0, SeekOrigin.Begin);
return stream;
The following is the definition of Node, which is defined in a namespace to prevent conflict with another node.
[DataContract (Name="Node",Namespace="API")]
[Serializable]
public class Node
{
public Node()
{
}
[DataMember]
public string Name { get; private set; }
}
On my client app, I do the following:
BinaryFormatter bf = new BinaryFormatter();
List<Node> nodes = (List<Node>) bf.Deserialize(client.GetContents());
I am getting the error that says:
Unable to find assembly 'API, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null'.
I am using a wsHttpBinding for the client connection.
I must be missing something, perhaps the namespace screws something up. Any ideas?
If the version of the .NET runtime on your client is any different from that which is on your server, binary serialization will likely fail. I would recommend using DataContractSerializer and writing as binary using XmlDictionaryWriter:
var stream = new MemoryStream();
var writer = XmlDictionaryWriter.CreateBinaryWriter(stream);
var serializer = new DataContractSerializer(List<APINode>);
serializer.WriteObject(writer, data.ToList<APINode>());
writer.Flush();
stream.Position = 0;
return stream;
on the client side:
using (var reader = XmlDictionaryReader.CreateBinaryReader(client.GetContents(), XmlDictionaryReaderQuotas.Max))
{
var serializer = new DataContractSerializer(List<Node>);
return (List<Node>)serializer.ReadObject(reader, true);
}

Redirect request to another url

In my action I have Request property. I want to use this request to get response from another URL (from another website). Is it possible?
To request a external webpage from C# code and retrieve it as a string. You can use the following snippet of code:
try
{
var request = WebRequest.Create("http://www.steelcm.com/");
var response = request.GetResponse();
var responseStream = response.GetResponseStream();
using(var reader = new StreamReader(responseStream))
{
// Convert stream object to string
string myWebPage = reader.ReadToEnd();
}
}catch(Exception e)
{
// TODO: Handle error exception
}
This will return the HTML in the string variable myWebPage. Also you will need to include the following 2 libraries:
using System.Net;
using System.IO;
Of course you can manipulate the request URL dependant on a users input.

Resources