Есть self-hosted WCF служба со следующим контрактом:
[ServiceContract]
interface IMyService
{
[OperationContract]
[WebInvoke(Method = "HEAD", UriTemplate = "Files/{fileName}")]
void GetFileInfo(string fileName);
[OperationContract]
[WebGet(UriTemplate = "Files/{fileName}")]
Stream StreamFile(string fileName);
}
Упрощённая реализация её такова:
class MyService : IMyService
{
public void GetFileInfo(string fileName)
{
string filePath = Path.Combine("Files", fileName);
FileInfo fi = new FileInfo(filePath);
var response = WebOperationContext.Current.OutgoingResponse;
response.ContentType = "application/octet-stream";
response.ContentLength = fi.Length;
response.StatusCode = HttpStatusCode.OK;
response.SuppressEntityBody = true;
}
public Stream StreamFile(string fileName)
{
...
}
}
Проблема в методе GetFileInfo. Когда происходит HEAD запрос к службе (например, с помощью WebRequest):
var uri = new Uri("http://localhost:8733/MyService/Files/test.dat");
var req = WebRequest.Create(uri);
req.Method = "HEAD";
using (var resp = req.GetResponse() as HttpWebResponse)
{
Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
foreach (string header in resp.Headers)
Console.WriteLine("{0}: {1}", header, resp.Headers[header]);
}
то в ответ приходит:
HTTP/1.1 200 OK
Content-Length: 1234
Content-Type: application/octet-stream
Date: Wed, 22 Jun 2016 10:17:38 GMT
Server: Microsoft-HTTPAPI/2.0
но это в случае, если длина запрашиваемого файла не превышает int.MaxValue. Если же длина файла превышает int.MaxValue то Content-Length в ответе имеет значение 0:
Content-Length: 0
Как это победить? Как заставить возвращать нормальную длину для больших файлов?
Конфигурация WCF-службы (если это важно):
Ответ
Похоже, что это ошибка в коде System.ServiceModel, а конкретно - вот в этом участке. Интересно то, что проблема сохраняется и при хостинге сервиса в IIS.
Как вариант исправления - создать кастомный байндинг, наследующий от WebHttpBinding, с кастомным transport channel, и в нем организовать правильную расстановку заголовков у HttpListener (этот класс используется при self-хостинге сервисов с http-транспортом).
IMHO, это неоправданно сложно. Проще пойти на нарушение принципов RESTful-архитектуры и сделать запрос размера файла через POST с результатом в теле ответа. Или отправлять длину в другом заголовке.
Проблемные участки кода
// HttpChannelHelpers.cs, line 2988
if (string.Compare(name, "content-length", StringComparison.OrdinalIgnoreCase) == 0)
{
int contentLength = -1;
if (httpMethodIsHead &&
int.TryParse(value, out contentLength))
{
this.SetContentLength(contentLength);
}
// else
//this will be taken care of by System.Net when we write to the content
}
и
// HttpChannelHelpers.cs, line 3061
protected override void SetContentLength(int contentLength)
{
listenerResponse.ContentLength64 = contentLength;
}
Комментариев нет:
Отправить комментарий