ServiceStack忽略接受标头

时间:2014-02-24 22:22:38

标签: servicestack

ServiceStack会忽略Accept标头的原因是什么?该服务托管在ASP.NET应用程序中,并在IDE中的调试中运行。使用System.Web.WebRequest对象调用该服务的前40个左右会导致服务正确响应。在大约50次调用之后,客户端检测到404错误(断点未在服务中命中)。从那时起,将忽略Accept标头。所有后续请求始终返回XML。

正在使用的客户......

var client = (HttpWebRequest)WebRequest.Create(uri);
client.Method = WebRequestMethods.Http.Post;
client.AllowWriteStreamBuffering = false;       
client.SendChunked = true;                      
client.ContentType = "multipart/form-data;";    
client.Timeout = int.MaxValue;                  // HACK:REMOVE
client.Accept = "application/json";

现在调用有点乱(尝试调试失败)......

using (FileStream fileStream = File.OpenRead(filePaths[i]))
{
    fileStream.Copy(client.GetRequestStream());
}

var responseString = string.Empty;
try { responseString = new StreamReader(client.GetResponse().GetResponseStream()).ReadToEnd(); }
catch (Exception ex) { Debug.WriteLine(ex.Message); }
if (String.IsNullOrWhiteSpace(responseString)) { continue; }

PutFileResponse response = null;
try { response = responseString.FromJson<PutFileResponse>(); }
catch (Exception ex) { Debug.WriteLine(ex.Message); }
if (response == null)
{
    try { response = responseString.FromXml<PutFileResponse>(); }
    catch (Exception ex) { Debug.WriteLine(ex.Message); }
    if (response == null)
    {
        continue;
    }
}

我原样离开了这个以显示响应。前50个(大约)调用按要求返回JSON。在404之后,所有后续调用总是返回XML。

有什么想法吗?

编辑(2014-02-25 10:35 EST):

看着小提琴后,这比我想象的要奇怪一点。在559个请求中,其中34个导致404错误。但是,服务会在错误之前和之后继续响应而不会出现问题。 404错误是第一个令人费解的部分。第二项(XML和JSON之间的转换有点令人费解,但奇怪的是没有道德。

该应用程序是一个文件存储应用程序,并且正在递归测试目录以将文件推送到服务。它遇到的一些文件是实际的XML文件。所有文件都在Stream中发送,嵌套在DTO中,客户端为每个请求添加“application / json”的Accept标头。如果发送了XML文件,即使已发送Accept标头,服务也会以XML响应。

示例请求标头(会话94):

POST
http://localhost:50205/Files/Put/8178F94DBDBC4AB18F42118AFD01D1A2/AA10C004D624DA892171F8A7E8CD8D05/201760/ServiceStack.xml HTTP/1.1
Content-Type: multipart/form-data;
Accept: application/json
Host: localhost:50205
Transfer-Encoding: chunked
Expect: 100-continue

1000
<?xml version="1.0"?>
<doc>
    [SNIP]
</doc>

0

示例响应标头(会话#94):

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/xml
Server: Microsoft-IIS/8.0
X-Powered-By: ServiceStack/4.011 Win32NT/.NET
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcX1NvdXJjZVxGSUwwMVx0cnVua1xTcG90bGVzc1xGcmFtZXdvcmtzXEZpbGVNYW5hZ2VtZW50XFByb2plY3RzXEZNRi5TdG9yYWdlU2VydmVyLkhvc3RpbmcuUHVibGljXEZpbGVzXFB1dFw4MTc4Rjk0REJEQkM0QUIxOEY0MjExOEFGRDAxRDFBMlxBQTEwQzAwNEQ2MjREQTg5MjE3MUY4QTdFOENEOEQwNVwyMDE3NjBcU2VydmljZVN0YWNrLnhtbA==?=
X-Powered-By: ASP.NET
Date: Tue, 25 Feb 2014 15:19:06 GMT
Content-Length: 563

<?xml version="1.0" encoding="utf-8"?><PutFileResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/FMF.StorageServer.Services.Messages.Files"><ResponseStatus xmlns:d2p1="http://schemas.servicestack.net/types" i:nil="true" /><Status><FileSignature><Checksum>AA10C004D624DA892171F8A7E8CD8D05</Checksum><SizeBytes>201760</SizeBytes></FileSignature><IsAvailable i:nil="true" /><IsKnown i:nil="true" /><IsOnDisk i:nil="true" /><IsSuccessful i:nil="true" /><StatusMessage i:nil="true" /></Status></PutFileResponse>

不幸的是,我必须在将每个文件发送到服务器之前检测每个文件的内部结构,并且永远不能信任文件扩展名。或者总是假设服务器可能决定在我没想到的时候发回XML。

更紧迫的问题是为什么仅针对某些请求检测到404错误。在559个请求中,产生404错误的项目是77,232,235,238,246,275等...所以服务或客户端只是随机请求失败。

编辑(2014-02-25 12:20 EST):

似乎所有失败的文件(404错误)都是基于文本的。例如......

示例请求标头(会话#560):

POST http://localhost:50205/Files/Put/060C976372174F51BEB84FE524E57C57/1931975CE8E1090A6D66738A560888AD/1426/AssemblyInfo.cs HTTP/1.1
Content-Type: multipart/form-data;
Accept: application/json
Host: localhost:50205
Transfer-Encoding: chunked
Expect: 100-continue

592
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following 
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Utility")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Utility")]
[assembly: AssemblyCopyright("Copyright ©  2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible 
// to COM components.  If you need to access a type in this assembly from 
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("1071992e-2d4c-49df-9526-6d4d29f979b4")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version 
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers 
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

0

示例响应标头(会话#560):

HTTP/1.1 404 Not Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/8.0
X-SourceFiles: =?UTF-8?B?RDpcX1NvdXJjZVxGSUwwMVx0cnVua1xTcG90bGVzc1xGcmFtZXdvcmtzXEZpbGVNYW5hZ2VtZW50XFByb2plY3RzXEZNRi5TdG9yYWdlU2VydmVyLkhvc3RpbmcuUHVibGljXEZpbGVzXFB1dFwwNjBDOTc2MzcyMTc0RjUxQkVCODRGRTUyNEU1N0M1N1wxOTMxOTc1Q0U4RTEwOTBBNkQ2NjczOEE1NjA4ODhBRFwxNDI2XEFzc2VtYmx5SW5mby5jcw==?=
X-Powered-By: ASP.NET
Date: Tue, 25 Feb 2014 15:24:10 GMT
Connection: close
Content-Length: 5106

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<title>IIS 8.0 Detailed Error - 404.7 - Not Found</title> 
<style type="text/css"> 
<!-- 
body{margin:0;font-size:.7em;font-family:Verdana,Arial,Helvetica,sans-serif;} 
code{margin:0;color:#006600;font-size:1.1em;font-weight:bold;} 
.config_source code{font-size:.8em;color:#000000;} 
pre{margin:0;font-size:1.4em;word-wrap:break-word;} 
ul,ol{margin:10px 0 10px 5px;} 
ul.first,ol.first{margin-top:5px;} 
fieldset{padding:0 15px 10px 15px;word-break:break-all;} 
.summary-container fieldset{padding-bottom:5px;margin-top:4px;} 
legend.no-expand-all{padding:2px 15px 4px 10px;margin:0 0 0 -12px;} 
legend{color:#333333;;margin:4px 0 8px -12px;_margin-top:0px; 
font-weight:bold;font-size:1em;} 
a:link,a:visited{color:#007EFF;font-weight:bold;} 
a:hover{text-decoration:none;} 
h1{font-size:2.4em;margin:0;color:#FFF;} 
h2{font-size:1.7em;margin:0;color:#CC0000;} 
h3{font-size:1.4em;margin:10px 0 0 0;color:#CC0000;} 
h4{font-size:1.2em;margin:10px 0 5px 0; 
}#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS",Verdana,sans-serif; 
 color:#FFF;background-color:#5C87B2; 
}#content{margin:0 0 0 2%;position:relative;} 
.summary-container,.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;} 
.content-container p{margin:0 0 10px 0; 
}#details-left{width:35%;float:left;margin-right:2%; 
}#details-right{width:63%;float:left;overflow:hidden; 
}#server_version{width:96%;_height:1px;min-height:1px;margin:0 0 5px 0;padding:11px 2% 8px 2%;color:#FFFFFF; 
 background-color:#5A7FA5;border-bottom:1px solid #C1CFDD;border-top:1px solid #4A6C8E;font-weight:normal; 
 font-size:1em;color:#FFF;text-align:right; 
}#server_version p{margin:5px 0;} 
table{margin:4px 0 4px 0;width:100%;border:none;} 
td,th{vertical-align:top;padding:3px 0;text-align:left;font-weight:normal;border:none;} 
th{width:30%;text-align:right;padding-right:2%;font-weight:bold;} 
thead th{background-color:#ebebeb;width:25%; 
}#details-right th{width:20%;} 
table tr.alt td,table tr.alt th{} 
.highlight-code{color:#CC0000;font-weight:bold;font-style:italic;} 
.clear{clear:both;} 
.preferred{padding:0 5px 2px 5px;font-weight:normal;background:#006633;color:#FFF;font-size:.8em;} 
--> 
</style> 

</head> 
<body> 
<div id="content"> 
<div class="content-container"> 
  <h3>HTTP Error 404.7 - Not Found</h3> 
  <h4>The request filtering module is configured to deny the file extension.</h4> 
</div> 
<div class="content-container"> 
 <fieldset><h4>Most likely causes:</h4> 
  <ul>  <li>Request filtering is configured for the Web server and the file extension for this request is explicitly denied.</li> </ul> 
 </fieldset> 
</div> 
<div class="content-container"> 
 <fieldset><h4>Things you can try:</h4> 
  <ul>  <li>Verify the configuration/system.webServer/security/requestFiltering/fileExtensions settings in applicationhost.config and web.config.</li> </ul> 
 </fieldset> 
</div> 

<div class="content-container"> 
 <fieldset><h4>Detailed Error Information:</h4> 
  <div id="details-left"> 
   <table border="0" cellpadding="0" cellspacing="0"> 
    <tr class="alt"><th>Module</th><td>&nbsp;&nbsp;&nbsp;RequestFilteringModule</td></tr> 
    <tr><th>Notification</th><td>&nbsp;&nbsp;&nbsp;BeginRequest</td></tr> 
    <tr class="alt"><th>Handler</th><td>&nbsp;&nbsp;&nbsp;ServiceStack.Factory</td></tr> 
    <tr><th>Error Code</th><td>&nbsp;&nbsp;&nbsp;0x00000000</td></tr> 

   </table> 
  </div> 
  <div id="details-right"> 
   <table border="0" cellpadding="0" cellspacing="0"> 
    <tr class="alt"><th>Requested URL</th><td>&nbsp;&nbsp;&nbsp;http://localhost:50205/Files/Put/060C976372174F51BEB84FE524E57C57/1931975CE8E1090A6D66738A560888AD/1426/AssemblyInfo.cs</td></tr> 
    <tr><th>Physical Path</th><td>&nbsp;&nbsp;&nbsp;D:\_Source\FIL01\trunk\Spotless\Frameworks\FileManagement\Projects\FMF.StorageServer.Hosting.Public\Files\Put\060C976372174F51BEB84FE524E57C57\1931975CE8E1090A6D66738A560888AD\1426\AssemblyInfo.cs</td></tr> 
    <tr class="alt"><th>Logon Method</th><td>&nbsp;&nbsp;&nbsp;Not yet determined</td></tr> 
    <tr><th>Logon User</th><td>&nbsp;&nbsp;&nbsp;Not yet determined</td></tr> 
    <tr class="alt"><th>Request Tracing Directory</th><td>&nbsp;&nbsp;&nbsp;C:\Users\Fred\Documents\IISExpress\TraceLogFiles\FMF.STORAGESERVER.HOSTING.PUBLIC</td></tr> 
   </table> 
   <div class="clear"></div> 
  </div> 
 </fieldset> 
</div> 

<div class="content-container"> 
 <fieldset><h4>More Information:</h4> 
  This is a security feature. Do not change this feature unless the scope of the change is fully understood. If the file extension for the request should be allowed, remove the denied file extension from configuration/system.webServer/security/requestFiltering/fileExtensions. 
  <p><a href="http://go.microsoft.com/fwlink/?LinkID=62293&amp;IIS70Error=404,7,0x00000000,9200">View more information &raquo;</a></p> 

 </fieldset> 
</div> 
</div> 
</body> 
</html> 

编辑(2014-02-25 14:27 EST):

继续测试并发现忽略Accept标头的ServiceStack是一个比我希望的更大的问题。由于所有文件都必须保留,并且由于这些文件必须同时包含HTML和XML文件,因此我需要确保ServiceStack仅发回请求的响应。我上次测试中发送的一些文件包含HTML文件,很不幸的是,ServiceStack发送了一份HTML文档作为响应。

临时文件夹包含一堆随机文件。并且,正如您所料,因为我手头有大量的源文件,temp文件夹包含一些C#/ VS2K12解决方案。例如,我复制了DoFactory解决方案的源代码,其中几个.Config,.cs,.csproj文件失败,而其他类型的文件则通过。

正在使用DTO ......

//[Route("/Files/Put/{Token}/{Checksum}/{SizeBytesText}/{FileNameOrExtension}", "POST")]
[Route("/Files/Put/{PathInfo*}", "POST")]
public class PutFileRequest : IReturn<PutFileResponse>, IRequiresRequestStream
{
    public string Token { get; set; }
    public string Checksum { get; set; }
    public string SizeBytesText { get; set; }
    public string FileNameOrExtension { get; set; }

    public System.IO.Stream RequestStream { get; set; }
}

我故意包含了我正在使用的原始路线。请注意,URI是使用一组变量和文件名构造的。在服务器上使用该文件的名称是为了方便使用原始文件扩展名来保存文件。

以下是来自测试应用程序的Main方法,该方法一致地失败。任何发布此文件的尝试都将导致404错误。 请注意,该文件完好无损,FileSteam可以成功打开并复制文件的内容。如果文件存在问题则应该失败。

static void Main(string[] args)
{
    var filePath = @"D:\Temp\_Source\DoFactory\CS_4.5\Spark\Art.Web\Areas\Shop\Models\ProductsModel.cs";
    var fileInfo = Files.GetInfo(filePath, calculateChecksum: true);


    var uri = @"http://localhost:50205/Files/Put/" +
                Guid.NewGuid().ToString("N") + "/" +
                fileInfo.Checksum + "/" +
                fileInfo.SizeBytes.Value + "/" +
                System.IO.Path.GetFileName(filePath);

    var client = (HttpWebRequest) WebRequest.Create(uri);
    client.Method = WebRequestMethods.Http.Post;
    client.AllowWriteStreamBuffering = false;       
    client.SendChunked = true;                      
    client.ContentType = "multipart/form-data;";    
    client.Timeout = int.MaxValue;                  
    client.Accept = "application/json";             

    using (FileStream fileStream = File.OpenRead(filePath))
    {
        fileStream.CopyTo(client.GetRequestStream());
    }

    var responseString = string.Empty;
    try { responseString = new StreamReader(client.GetResponse().GetResponseStream()).ReadToEnd(); }
    catch (Exception ex) { Debug.WriteLine(ex.Message); }
    if (String.IsNullOrWhiteSpace(responseString)) { Environment.Exit(1); }

    PutFileResponse response = null;
    try { response = responseString.FromJson<PutFileResponse>(); }
    catch (Exception ex) { Debug.WriteLine(ex.Message); }

    if (response == null)
    {
        try { response = responseString.FromXml<PutFileResponse>(); }
        catch (Exception ex) { Debug.WriteLine(ex.Message); }
    }

    if (response == null)
    {
        try { response = responseString.FromJsv<PutFileResponse>(); }
        catch (Exception ex) { Debug.WriteLine(ex.Message); }
    }

    if (response == null) { Environment.Exit(2); }

    Console.ReadLine();
}

1 个答案:

答案 0 :(得分:4)

经过一天的测试和大约1,000次测试后,我找到了解决问题的方法。但是,在调查结果中,我向ServiceStack团队请求找到解决方法。

如果文件发布到最后似乎包含文件名的路径,ServiceStack将使用与RequestStream中包含的项目内容匹配的结果进行响应。如果项目是HTML,则响应将是HTML响应。如果该项是XML,则响应将是XML响应。在这些情况下,始终忽略Accept标头。

要解决此问题,我已将文件名分成URL中的单独部分。最初解析为:

的路径
http://localhost:1234/Files/Put/ABC123/MyFile.xml

...现在发布为......

http://localhost:1234/Files/Put/ABC123/F/MyFile/X/xml

...或者,如果我只需要扩展名,那么扩展部分就会被破坏......

http://localhost:1234/Files/Put/ABC123/X/tar/gz

在这些示例中,服务知道如何重新组合文件名和/或扩展名。

此更改的最终结果是按预期发布XML和HTML文件,ServiceStack服从Accept标头。在此更改之后的所有测试中,ServiceStack都使用所需的JSON对象进行响应。此外,不会遇到404错误。