Open simontuffley opened 3 years ago
context.Response.ContentType = "image/jpeg"
Headers.Add()
does not replace, it appends to the headers. Set
would be better suited for that job
Hi there,
Thanks for your prompt response. I have changed the code as suggested but I still don't get the expected response:
[Route(HttpVerbs.Get, "/rooms/{roomId?}")]
public string GetRoomTile(ushort roomId)
{
try
{
HttpContext.Response.ContentType = "image/jpeg";
var img = _fileSystem.Read($"\\RM\\Data\\Rooms\\{roomId}.jpg");
return img;
}
catch (FileNotFoundException ioEx)
{
Console.WriteLine(ioEx.Message);
throw new Exception("Error");
}
}
Cache-Control: no-store, no-cache, must-revalidate
Connection: close
Content-Encoding: gzip
Content-Type: application/json; charset=utf-8
Date: Thu, 04 Mar 2021 20:10:45 GMT
Expires: Sat, 26 Jul 1997 05:00:00 GMT
Last-Modified: Thu, 04 Mar 2021 20:10:44 GMT
Pragma: no-cache
Server: EmbedIO/3.4.3
Vary: Accept-Encoding
Hello @simontuffley, thanks for using EmbedIO!
As the name implies, WebApiModule
was written to serve web APIs, not files. Anything you return from a web API controller method is automatically serialized in JSON format, hence the change in Content-Type
.
It is possible to change the serialization method used by a WebApiModule
; just be aware that all methods of all controllers associated with the module will use the same serializer.
EmbedIO does not currently offer any other ready-made serializer other than the default (with a couple variants to customize JSON output). The upcoming version 3.5.0 will have a "do-nothing" serializer, though, that will let you set your content type and return any data to the client untouched. More details in the relevant PR.
If you dont' want to wait for the 3.5.0 release, here's a quick trick you can use.
First let's define a response serializer that sends data to the client as-is. opy the following code in a CustomResponseSerializer.cs
file in you application:
using EmbedIO.Utilities;
using System;
using System.Threading.Tasks;
namespace YOUR_NAMESPACE
{
/// <summary>
/// Provides custom response serializer callbacks.
/// </summary>
public static class CustomResponseSerializer
{
private static readonly ResponseSerializerCallback ChunkedEncodingBaseSerializer = GetBaseSerializer(false);
private static readonly ResponseSerializerCallback BufferingBaseSerializer = GetBaseSerializer(true);
/// <summary>
/// Sends data in a HTTP response without serialization.
/// </summary>
/// <param name="bufferResponse"><see langword="true"/> to write the response body to a memory buffer first,
/// then send it all together with a <c>Content-Length</c> header; <see langword="false"/> to use chunked
/// transfer encoding.</param>
/// <returns>A <see cref="ResponseSerializerCallback"/> that can be used to serialize data to a HTTP response.</returns>
/// <remarks>
/// <para><see cref="string"/>s and one-dimensional arrays of <see cref="byte"/>s
/// are sent to the client unchanged; every other type is converted to a string.</para>
/// <para>The <see cref="IHttpResponse.ContentType">ContentType</see> set on the response is used to negotiate
/// a compression method, according to request headers.</para>
/// <para>Strings (and other types converted to strings) are sent with the encoding specified by <see cref="IHttpResponse.ContentEncoding"/>.</para>
/// </remarks>
public static ResponseSerializerCallback None(bool bufferResponse)
=> bufferResponse ? BufferingBaseSerializer : ChunkedEncodingBaseSerializer;
private static ResponseSerializerCallback GetBaseSerializer(bool bufferResponse)
=> async (context, data) => {
if (data is null)
{
return;
}
var isBinaryResponse = data is byte[];
if (!context.TryDetermineCompression(context.Response.ContentType, out var preferCompression))
{
preferCompression = true;
}
if (isBinaryResponse)
{
var responseBytes = (byte[])data;
using var stream = context.OpenResponseStream(bufferResponse, preferCompression);
await stream.WriteAsync(responseBytes, 0, responseBytes.Length).ConfigureAwait(false);
}
else
{
var responseString = data is string stringData ? stringData : data.ToString() ?? string.Empty;
using var text = context.OpenResponseText(context.Response.ContentEncoding, bufferResponse, preferCompression);
await text.WriteAsync(responseString).ConfigureAwait(false);
}
};
}
}
Then change you web server initialization code to specify the new serializer:
// Change the false parameter to true if you need to transmit a "Content-Length" header
// (some non-browser clients need it to work properly);
// otherwise the response will use chunked transfer encoding,
// which is fine for browsers and will save time and memory.
.WithWebApi("/YOUR_PATH", CustomResponseSerializer.None(false), m => m
.WithController<YOUR_CONTROLLER_CLASS>())
Your controller method has a few issues too:
_fileSystem
object has a method that reads a file and returns its contents as a byte array, use that. Trying to treat binary data as strings is bound to fail;FileNotFoundException
into a 404 Not Found
response;roomId
parameter will be passed as zero, so unless you have a 0.jpg
file to send, it's simpler and faster to make roomId
compulsory by removing the question mark from your route. EmbedIO will send a 404 Not Found
response without even caring to call GetRoomTile
if the request path has no room ID.In the following code I'll assume the existence of a ReadAsByteArray
method that returns byte[]
:
[Route(HttpVerbs.Get, "/rooms/{roomId}")]
public string GetRoomTile(ushort roomId)
{
try
{
Response.ContentType = "image/jpeg";
return _fileSystem.ReadAsByteArray($"\\RM\\Data\\Rooms\\{roomId}.jpg");
}
catch (FileNotFoundException ioEx)
{
Console.WriteLine(ioEx.Message);
throw HttpException.NotFound();
}
}
I hope this helps you.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Describe the bug I am trying to send an image to a web browser. But I cannot clear the headers so that the Content-Type is just image/jpeg.
To Reproduce
This is the response header I get though:
Expected behavior