<input type="file" name="icon" multiple accept="image/*" />
注意:可上传文件的表单元素,enctype值必须为:multipart/form-data。
所以,在View中写form,可以直接用HTML原生form标签,或者:
@using(Html.BeginForm("Index", "Register", FormMethod.Post, new { enctype = "multipart/form-data" })) { }
然后,可以通过两种方式上传文件内容:
HttpPostedFileBase icon = Request.Files["icon"];
[HttpPost] public ActionResult Index(HttpPostedFileBase icon)
注意:参数名必须是icon,和input中的name相对应。
包含有属性:
if (Path.GetExtension(icon.FileName) == ".jpg")
if (icon.ContentType != "image/png")
if (icon.ContentLength > 500*300)
以及SaveAs()方法:可将上传文件存放到指定位置。(演示)
PS:绝大多数情况,我们都不会(虽然可以)把文件直接存放到数据库中。
icon.SaveAs(Server.MapPath("/Images/qq.jpg"));注意这里的Server.MapPath()方法,它可以将url格式的path自动“映射”到当前项目所在文件夹位置,非常好用!
此外,ASP.NET默认限制文件上传最大为5M,如果要允许更大的文件上传,需要在web.config中更改:
<system.webServer> <!--IIS 7(包含)以上--> <security> <requestFiltering> <requestLimits maxAllowedContentLength="1000" /> </requestFiltering> </security> </system.webServer>
超过该大小的文件上传,会被IIS拦截,不会进入ASP.NET中进行处理。(演示)
服务器上的静态文件,可以直接通过a标签、img标签等显示/下载(复习:HTML - 链接和图片),但动态生成的文件(比如图形验证码)呢?
MVC的Action可以通过File()方法返回FileResult的三种子类:
返回类型都是ActionResult的子类,所以可以:
public ActionResult _Captcha(){
注意他们都要要求一个参数:
文件类型由此决定,常用的有:
从C#进阶:IO和文件操作复制captcha图片的生成代码,
不同的是最后需要将bitmap的放置到MemoryStream中:
MemoryStream stream = new MemoryStream(); image.Save(stream, ImageFormat.Png);
注意:image.Save()到stream之后,其Position被放置到最末尾端,所以如果这时候直接使用:
return File(stream, "image/png");(依赖于Position的)File()方法就读取不到任何内容。有两种方法可以选择:
return File(stream.ToArray(), "image/png");
stream.Seek(0, SeekOrigin.Begin);
这样,/Captcha/Get就可以作为一个url存在,输出的就是一个png图片:
<img src="/Register/_Captcha" />
ASP.NET Framework为我们准备了HttpCookie对象,使用还是很方便的:
PS:有些版本,(因为包含隐私的立法)需要更改默认的cookie policy等
HttpCookie cookie = new HttpCookie("user");
cookie.Values.Add("id", "98"); cookie.Values.Add("pwd", "1234");
Response.Cookies.Add(cookie);
Response是对HTTP请求响应的封装。
演示:生成的cookie名为user,值为id=98&pwd=1234
接下来,通过
就可以获得前端传来的cookies,然后一步一步的获取各个cookie,以及他们的value和values:
HttpCookie cookie = Request.Cookies.Get("user"); string rawValue = cookie.Value; //id=98&pwd=1234 string id = cookie.Values["id"]; //98 string pwd = cookie.Values["pwd"]; //1234
但是,这里面有一个大陷阱:对Request.Cookies的操作不会影响到客户端,比如:
Request.Cookies.Remove("user");
并不会删除掉user cookie。@想一想@:为什么?
另外,通过Request获得的cookie对象有“不太靠谱”,比如Expires(演示:总是0001/01/01 00:00:00)
cookie.Expires = DateTime.Now.AddDays(14);要想真正改变cookie,需要把同名(且同domain同path)的cookie发送到客户端。这需要在Response.Cookies中处理:
Response.Cookies.Add(cookie);
这个cookie可以从Request.Cookies获取,但更多的时候是直接new一个,再进行各种设置。比如
一个cookie:
HttpCookie cookie = new HttpCookie("user"); //cookie.Path = "/Register"; 如果待删除cookie有path的话 cookie.Expires = DateTime.Now.AddDays(-1); Response.Cookies.Add(cookie);
ASP.NET Framework的Session也是开箱即用的:
PS:有些版本,需要显式的enable(默认是disabled)
if (Session[KEY_OF_STUDENT] == null) { Session[KEY_OF_STUDENT] = new Student { Id = new Random().Next(100) }; } else { Student student = Session[KEY_OF_STUDENT] as Student; }
演示:
说明:Session依赖于框架自动生成的ASP.NET_SessionId来辨别用户。
且Session[KEY_OF_STUDENT]中存放的“不是一个而是多个”Student:
演示:Session中Timeout和Mode属性
在web.config的system.web节点下,添加sessionState节点,可以更改上述默认设置:
<sessionState timeout="10" mode="StateServer"></sessionState>
Timeout是session保留的时长,单位分钟,默认是20分钟。
Mode是session存放的位置,默认是InProc。还可以:
|
存放位置 |
速度 |
特点 |
InProc |
IIS进程中内存 |
最快 |
不稳定,因为IIS重启/Application pool回收等丢失 |
StateServer |
独立的windows服务 |
比较快 |
比较稳定,一般来说只有服务器重启才会丢失 |
SQLServer |
数据库 |
不快 |
稳定,在指定的过期时间内不丢失;不担心size,大量session时使用 |
Off | 关闭session功能 |
|
mode设为
需要:
[Serializable] public class Student
演示:
首先需要打开VS的Developer Command Prompt
执行如下命令生成数据库:
aspnet_regsql.exe -S (localdb)\MSSQLLocalDB -E -ssadd -sstype p其中:
运行成功,就会生成如下数据库:
最后,在web.config中配置:
<sessionState timeout="10" mode="SQLServer" sqlConnectionString="Data Source=(localdb)\MSSQLLocalDB;Integrated Security=True;" > </sessionState>
注意:连接字符串中删除掉 Initial Catalog,由ASP.NET自己匹配数据库
演示:生成的session保存在上述数据库表中
#常见面试题:你知道哪些页面传值方式?#
页面之间其实不能传值(复习:HTTP无状态)。
所谓页面传值,指的是把在一个页面(后台)生成的值,传递给另外一个页面(后台)。
所有的“页面传值”都是通过“迂回”的方法:
Filter是ASP.NET面向切口的实现(复习:AOP),以供开发人员在HTTP生命周期中特定节点插入自定义的逻辑。
MVC常用的时间节点有这些:
利用Filter的方式有两种:
MVC为我们内置了一个FilterAttribute(继承自Attribute,所以可以当做特性使用),下面再派生出若干子类,常用的有:
通常我们只需要继承这些子类就可以了:
public class ModelValidationAttribute : ActionFilterAttribute{} public class ErrorLogAttribute : HandleErrorAttribute{} public class NeedLogOnAttribute : AuthorizeAttribute{}
通过override父类的方法,就可以实现我们自己的逻辑。
这些Filter既可以放在Action上,也可以放在Controller上(源代码:AttributeUsage)
[HttpGet] [NeedLogOn] [ModelValidation] [ErrorLog] public ActionResult Index()
演示:Filter的执行顺序
同一种类型的Attribute,可以使用Order属性来设置执行顺序
[ModelValidation(Order=100)]
@想一想@:当出现ChildAction的嵌套时,执行顺序是怎么样的?(依次类推)
所有的“Filter(override)方法”都提供了这个参数(ControllerContext的不同子类),通过它大量的“上下文信息”(context)供我们编程使用:
(filterContext.Controller as Controller).ModelState
filterContext.Result = new RedirectResult("/Log/On");
另外,注意几个有用的方法/属性:
首先要声明一个类,实现Filter接口(见上文):
public class ContextPerRequest : IActionFilter
然后在App_Start文件夹FilterConfig.RegisterGlobalFilters()中注册:
filters.Add(new ContextPerRequest());
注意:
Filter接口的执行一样是有顺序的。
有没有感觉之前的PRG实现“怪怪的”?使用TempData/Merge()在POST和GET方法里搞一堆,性价比也不高嘛。
有没有更简单的解决办法?当然有:
//ModelStateDictionary state = ((Controller)filterContext.Controller).ModelState; 或者 ModelStateDictionary state = filterContext.Controller.ViewData.ModelState; if (!state.IsValid) { filterContext.Controller.TempData[Const.Other.MODEL_VALIDATE_ERROR] = state; filterContext.Result = new RedirectResult(filterContext.HttpContext.Request.Url.PathAndQuery); }//else nothing
ModelStateDictionary state = filterContext.Controller.TempData[Const.Other.MODEL_VALIDATE_ERROR] as ModelStateDictionary; if (state != null) { filterContext.Controller.ViewData.ModelState.Merge(state); }
@想一想@:能不能把两个Filter合成一个?(可以的)但哪一种方式更好呢?
多快好省!前端后端,线上线下,名师精讲
更多了解 加: