我理解HttpPostedFileBase
和HttpPostedFileWrapper
之间的关系,就两者的需要而言(即在单元测试/模拟中)。但是,当我在HttpPostedFileBase
的回报上设置断点时,为什么会将其显示为HttpPostedFileWrapper
?
此外,HttpPostedFileBase
没有实现ContentType属性。那么,为什么当我的代码仅引用HttpPostedFileBase
而不是HttpPostedFileWrapper
时,它会返回一个值?这有什么诡计?
编辑#1:
感谢@ lawliet29的精彩回复。我按照建议书写了结构。
public sealed class MyHttpPostedFile
{
public string ContentType { get { return "123"; } }
}
public abstract class MyHttpPostedFileBase
{
}
public class MyHttpPostedFileWrapper : MyHttpPostedFileBase
{
private MyHttpPostedFile _myHttpPostedFile;
public MyHttpPostedFileWrapper(MyHttpPostedFile myHttpPostedFile) {
_myHttpPostedFile = myHttpPostedFile;
}
public string ContentType { get { return _myHttpPostedFile.ContentType; } }
}
为了使其工作,我需要传递这样的参数:
GetFiles(new MyHttpPostedFileWrapper(new MyHttpPostedFile());
这似乎是我质疑的诡计存在的地方。 .NET如何知道传递给它的字节是类MyHttpPostedFile
的类,它应该接受该对象并将其作为参数传递给我的构造函数?
编辑#2:
我没有意识到ASP.NET MVC绑定器不仅仅通过传递这些更高级别的对象来传递字节。这是我想知道的诡计!感谢您的回复。
答案 0 :(得分:18)
然而,在现实生活中,HttpPostedFile是处理已发布文件的方法,为了保持一致,我们创建了HttpPostedFileWrapper。该类通过包装HttpPostedFile为HttpPostedFileBase提供实现。
所以HttpPostedFileBase是一个统一的抽象,HttpPostedFile是一个表示发布文件的类,而HttpPostedFileWrapper是HttpPostedFileBase的实现,它包装了HttpPostedFile。 HttpPostedFileWrapper的ContentType属性的实现从底层HttpPostedFile中读取内容类型。
编辑:某种解释
ASP.NET MVC收到一个文件,在其下方的某个地方创建了一个HttpPostedFile实例,因为这是.NET Framework 1.0以来的工作方式。 HttpPostedFile的定义如下:
public sealed class HttpPostedFile
这基本上意味着它不能被继承,也不能被模拟用于单元测试。
要解决此问题,ASP.NET MVC开发人员创建了一个可模拟的抽象 - HttpPostedFileBase,其定义如下:
public abstract class HttpPostedFileBase
现在,您可以定义MVC操作,以便它们接受HttpPostedFileBase而不是不可模拟的HttpPostedFile:
[HttpPost]
public ActionResult PostFile(HttpPostedFileBase file)
{
// some logic here...
}
问题是,在下面的某处,表示已发布文件的唯一方法是使用旧的刚性HttpPostedFile。因此,为了支持这种抽象,MVC开发人员创建了一个名为HttpPostedFileWrapper的装饰器,看起来大致如下:
public class HttpPostedFileWrapper : HttpPostedFileBase
{
private HttpPostedFile _httpPostedFile;
public HttpPostedFileWrapper(HttpPostedFile httpPostedFile) {
_httpPostedFile = httpPostedFile;
}
public string ContentType { get { return _httpPostedFile.ContentType; } }
// implementation of other HttpPostedFileBase members
}
所以现在HttpPostedFileWrapper是您在使用已发布文件执行真正的HTTP POST请求时实际获得的。多亏了多态,你可以将派生类的实例 - HttpPostedFileWrapper - 传递给接受基类的方法 - HttpPostedFileBase。
一直以来,您都可以创建自己的模拟实现,例如,看起来像是要发布的视频文件。你这样做
public class MockPostedVideoFile : HttpPostedFileBase
{
public string ContentType { get { return "video/mp4"; } }
// rest of implementation here
}
另一个编辑: HttpPostedFile的实际实例化都由System.Web为您处理。 ASP.NET MVC绑定器对发布的表单数据非常智能。它会自动检测某些post值实际上是文件的字节,因此为了正确表示它们,它可以使用System.Web框架中的旧东西来创建一个HttpPostedFile实例。
这一点的主要观点是 - 你不必担心它。幕后有很多事情发生在这里,我们真的需要感谢ASP.NET MVC团队放弃所有那些低级别的东西。
唯一需要担心的地方是单元测试。在您的测试中,您可以使用模拟实现调用您的操作,如下所示:
myController.PostFile(new MockPostedVideoFile())