自学Web开发第十一天-基于VB和ASP.NET;简单项目开发:自定义类文件,研究不同语言的混写操作,页面的访问控制
登录页面设计完了,登录信息也能够写入日志文件了,突然有一个想法:对之后的所有有必要的操作都进行日志记录,例如数据库的写操作、版本更新操作等。如果每次都要写相应代码,感觉有点繁琐。所以决定写成一个自定义类文件,需使用时候进行调用就可以了。需要研究一下.net下怎么写自定义类文件和进行引用。
再这个小项目我准备和朋友一起开发,进行扩充,我们两人分别写不同的页面模块。不过朋友习惯使用C#,我还是习惯VB或C++,所以需要研究下同一个项目下是否可以用不同语言进行混写。
另我发现即使有登录页面,但是直接指定浏览器地址访问的页面就可以跳过登录页面,这个是不对的,所以要进行访问控制。如何进行,效果如何,则需要研究一下。
自定义类
关于自定义类网上的资源不多,整合了一下,得出以下结论
创建自定义类
项目里添加ASP.NET文件夹:App_Code,然后在该文件夹下添加新项目,选择类,就生成了一个新的类文件Class1.vb。这是个空的类文件,写入相应的代码就行。关于基础的类知识只能找一些基础教程了。
使用自定义类
关于使用,很多资料都是直接使用,即在代码中直接Dim就可以了,不过我在测试中发现不行,没有可以使用的类型。又研究了一下,有些资料里说需要进行引用,即Imports。在测试中,试了很多方式,没法引用。又有说要使用命名空间的,经过测试也不行。最后找到一篇文章,这里要感谢潇湘博客,更改下属性里的生成操作,改成编译,就可以了。
注:在我的实际测试中,引用这篇文章中的App_Code是引用不到的,反而不进行引用也可以使用各自定义的类,只要类文件在App_Code中。使用C#试了下,原来是可以的而且必须的。看来VB.net可以直接使用而C#需要引用才能使用,而VB.net使用了命名空间后,必须引用命名空间,或者写代码是使用别名下的类来进行定义。
不同语言进行混写
在ASP.net中,不同页面使用不同语言进行编写,然后合成一整个项目应用,从原理上来说应该是可以的。服务器收到请求后,对相应页面进行编译,和其他页面使用的语言应该是无关的。编译时应该可以将不同语言写的文件分开编译,不过实际如何,我来找点相关资料。
网上资料不多,也不全面,总的来说应该是可以的。根据网上的资料和实际使用中的情况,应该分为两种情况和两种情况的混合使用:
- 在A语言写的页面中使用B语言写的类等数据
- 在A语言写的页面中,嵌入、跳转至B语言写的页面
在A语言写的页面中使用B语言写的类等数据
可以在App_Code中使用不同的编程语言编写类。方法就是,将不同编程语言的类分别放到不同的子目录下,比如,所有C#编写的类,就直接放在App_Code目录下,然后在App_Code目录下创建一个子目录,比如叫VBCode,而所有VB.NET写的类都放到VBCode这个目录下,然后修改一下web.config设置:
<compilation>
<codeSubDirectories>
<add directoryName="VBCode"/>
</codeSubDirectories>
</compilation>
为了对齐好看好解读,在config 中这样写
<compilation debug="true">
<codeSubDirectories>
<add directoryName="CSCode"></add>
<add directoryName="VBCode"></add>
</codeSubDirectories>
</compilation>
现在创建一个VB页面(或者C#页面,效果等同,后面例子就写VB页面中引用 C#类),可以在代码页面顶端引用C#类的名称空间,然后在代码中就可以像使用VB.net类一样了。
在A语言写的页面中,嵌入、跳转至B语言写的页面
因为ASP.NET在编译时是按照页面文件声明的语言来进行编译的,所以可以直接嵌入、跳转。页面间传值使用共用类或者带参数的页面(.aspx?参数=xxx)都行。
实际测试中出现的问题
查看了很多资料后,我自己测试时最大的问题就是建立好的解决方案的项目里添加新建项时只有初始语言,我用的VB语言,不能选择C#。
开始以为VS版本问题,查找资料看到别人VS2010都能添加,VS2015有添加不同语言的视频,现在我用的2022不是取消了吧……
后来受到参考资料的一些启发,建立项目时使用了空网站,而不是常用的Web应用程序,就可以添加不同语言的窗体、类等文件了。至于创建新项目时如果找不到新建空网站,则需要使用VSInstaller程序修改安装组件,在ASP.NET和Web开发下的可选项里,将其他项目模板(早期版本)安装上即可。
经过测试,可以混合使用。下面是测试代码
Web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<compilation debug="true" strict="false" explicit="true" targetFramework="4.5.2" >
<!--添加类库文件夹-->
<codeSubDirectories>
<add directoryName="cs"/>
<add directoryName="vb"/>
</codeSubDirectories>
</compilation>
<httpRuntime targetFramework="4.5.2" />
</system.web>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701" />
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+" />
</compilers>
</system.codedom>
<!-- 默认登录页面-->
<system.webServer>
<defaultDocument>
<files >
<clear/>
<add value ="vbtest.aspx"/>
</files>
</defaultDocument>
</system.webServer>
</configuration>
vbtest.aspx
<%@ Page Language="VB" AutoEventWireup="false" CodeFile="vbtest.aspx.vb" Inherits="vbtest" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="Buttonvb" runat="server" Text="VB页面测试VB类" />
<asp:Button ID="Buttoncs" runat="server" Text="VB页面测试CS类" />
<asp:Button ID="ButtonGo" runat="server" Text="跳转到CS测试页面" />
<br /><iframe src="testcs.aspx" ></iframe>
</div>
</form>
</body>
</html>
vbtest.aspx.vb
Imports vbclass
Imports MyCSclass
Public Class vbtest
Inherits System.Web.UI.Page
Protected Sub Buttonvb_Click(sender As Object, e As EventArgs) Handles Buttonvb.Click
Dim str As String
Dim name As String
name = "tony"
Dim vb As New vbclasstest
str = vb.ReturnHello(name)
Response.Write(str)
End Sub
Protected Sub Buttoncs_Click(sender As Object, e As EventArgs) Handles Buttoncs.Click
Dim str As String
Dim name As String
name = "tony"
Dim cs As New CSClassTest
str = cs.ReturnHello(name)
Response.Write(str)
End Sub
Protected Sub ButtonGo_Click(sender As Object, e As EventArgs) Handles ButtonGo.Click
Response.Redirect("~/testcs.aspx")
End Sub
End Class
testcs.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="testcs.aspx.cs" Inherits="testcs" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="Buttonvb" runat="server" Text="CS页面测试VB类" OnClick="Buttonvb_Click" />
<asp:Button ID="Buttoncs" runat="server" Text="CS页面测试CS类" OnClick="Buttoncs_Click" />
<asp:Button ID="ButtonGo" runat="server" Text="跳转到VB测试页面" OnClick="ButtonGo_Click" />
</div>
</form>
</body>
</html>
testcs.aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using MyCSclass;
using vbclass;
public partial class testcs : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Buttonvb_Click(object sender, EventArgs e)
{
vbclasstest vb = new vbclasstest();
Response.Write(vb.ReturnHello("linda"));
}
protected void Buttoncs_Click(object sender, EventArgs e)
{
CSClassTest cs = new CSClassTest();
Response.Write(cs.ReturnHello("linda"));
}
protected void ButtonGo_Click(object sender, EventArgs e)
{
Response.Redirect("~/vbtest.aspx");
}
}
~/App_Code/vb/vbclasstest.vb
Namespace vbclass
Public Class vbclasstest
Public Function ReturnHello(name As String) As String
Return "VBtest,Hello," & name
End Function
End Class
End Namespace
~/App_Code/cs/CSClassTest.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MyCSclass{
public class CSClassTest
{
public CSClassTest()
{
}
public string ReturnHello(string name)
{
return "CSClass,hello," + name;
}
}
}
参考资料:
ASP.NET 2.0网站专案同时使C#与VB.NET之技巧
vb.net与C#混用解决方案
[C#/VB.NET] C# 与 VB.NET 的类库跨语言引用
VS2013的项目C#和VB不能同时存在?(这篇文章主要看评论)
一个动图
页面的访问控制,Forms身份验证
网站部署后,希望所有使用用户都是授权用户,所以要做页面访问控制,不然没有授权的用户通过地址栏可以直接访问其他页面。
启用了Forms身份验证后,所有页面被访问时会检测身份验证Cookie,有验证才会进行页面访问,否则重定向到指定的登录页面。启用方法是在Web.config文件中加入以下代码(其中loginUrl的值即重定向的登录页面):
<configuration>
<system.web>
<authentication mode="Forms">
<forms loginUrl="login.aspx" defaultUrl="default.aspx" protection="All" timeout="30" />
</authentication>
<authorization >
<deny users="?"/>
</authorization>
</system.web>
</configuration>
其中<authentication />部分声明了启用Forms身份验证;后面的<authorization />是授权部分,也是放到Web.config文件里的,但是这个文件是放到需要做判断的文件夹下。
身份验证authentication
身份验证的代码如下:
FormsAuthentication.SetAuthCookie(username As String, createPersistentCookie As Boolean)
Response.Redirect(url As String)
第一行代码表示Cookie记录登录的用户名参数1;参数2是个布尔型,决定是否永久保存Cookie的登录信息(即下次是直接登录还是需要重新登录)。
第二行代码则是身份验证后跳转到指定页面。
或者用这个:
FormsAuthentication.RedirectFromLoginPage(username As String, createPersistentCookie As Boolean)
其用法和上一例的第一行代码一样,不一样的是,这行代码能跳转回原先希望登录的页面(当访问一个页面没获得授权时,跳转到指定登录页面进行身份验证,这时候地址会增加参数?ReturnUrl=%2fdefault.aspx标亮的部分就是原先希望访问的页面,通过授权后,则返回这个页面)。需注意的是,如果没有原页面则会跳转到设置参数中的defaultUrl页面,如果这个参数没设或没值,则停留在登录页面,即便登录授权成功。原页可以通过FormsAuthentication.GetRedirectUrl
获得。
我们也可以通过增加CheckBox控件来让用户选择是否需要记住登录信息,这时参数二直接写成CheckBox.Checked就可以了。
需要登出时,我们可以这样写:
FormsAuthentication.SignOut()
FormsAuthentication.RedirectToLoginPage()
第一行为登出,即注销授权。第二行为跳转到登录页面。
授权authorization
使用方法
示例代码中<authorization />部分的信息就是授权信息了,可以写在每个文件夹中的Web.config文件中,以控制不同文件夹的访问权限。具体用法如下:
<authorization>
<deny users = "?"/>
<allow users= "*" />
</authorization>
deny users = “?”,?是匿名用户,即阻止匿名用户访问
allow users = “*”,*是所有用户,即允许所有用户访问
像示例这样,先阻止匿名用户后允许所有用户是相冲突的。在使用中,访问权限的设置是由顺序的。所以应该把适用用户群最大的放在最前面,特定的页面或文件夹再单独配置。
对特定用户、角色、HTTP方式进行授权控制
对于特定用户可以这样使用:
<authorization>
<allow users = "zhang,yan,hou"/>
<allow roles = "Admins,PowerUsers" />
<deny users= "*" />
</authorization>
即允许zhang,yan,hou这三个用户和所有Admins角色、PowerUsers角色访问,拒绝其他所有用户访问。
另外,还可以决定用户的某种HTTP方法是否可以被允许,方法是使用verb属性来表明对那种HTTP方法操作。
<allow verb=post users=”Chen,Li” />
<deny verb=get roles=”everyone” />
对特定文件、文件夹进行访问授权控制
对特定文件、文件夹进行访问授权:
有2种方法对特定文件、文件夹进行访问授权
- 在需要进行设置的文件夹下写单独的Web.config文件
- 使用<location>标签来控制
方法1中,下级Web.config继承了上级的配置信息,只需写入单独的授权控制信息就可以了。
方法2使用方法如下:
<configuration>
<!-- 文件夹A,允许所有用户访问 -->
<location path= "A">
<system.web>
<authorization>
<allow users= "*"/>
</authorization>
</system.web>
</location>
<!-- 文件夹B,允许匿名用户访问 -->
<location path= "B">
<system.web>
<authorization>
<allow users= "?"/>
</authorization>
</system.web>
</location>
<!-- 文件夹C,允许管理员组访问 -->
<location path= "C">
<system.web>
<authorization>
<allow roles= "Admins"/>
</authorization>
</system.web>
</location>
</configuration>
角色管理
对于Forms身份验证能使用授权角色的方式进行访问控制,但是实现方法很麻烦,牵扯到成员资格提供程序,相应的数据库设置及使用,准备放后面再研究。
一些注意事项
- 因为验证信息是存在Cookie里,所以在登录周期内(<forms timeout=“30” /> ),即使关闭了浏览器再进行访问,也是通过验证的。除非使用
FormsAuthentication.SignOut()
进行注销行为。 - 如果想在登录页面设置已经通过验证的用户访问时直接跳转,需使用判断
User.Identity.IsAuthenticated
的方法进行跳转,如果判断User.Identity.Name
之类的方法会出错。 - 未验证的情况下,CSS等样式、资源文件也是不能访问的,所以登录页面的样式也会出错。在使用中最好将登录页面及相关资源放置在一个文件夹里,并设置允许所有用户访问。