This site uses cookies to deliver our services. By using this site, you acknowledge that you have read and understand our Cookie and Privacy policy. Your use of Kontext website is subject to this policy. Allow Cookies and Dismiss

[WebMatrix] [MVC] 自定义站点范围行为

378 views 0 comments last modified about 7 years ago Raymond Tang Fahao

原文地址: http://www.asp.net/web-pages/tutorials/working-with-pages/18-customizing-site-wide-behavior

翻译者: Raymond Tang http://kontext.tech/

自定义站点范围行为

这一章节介绍怎样配置整个站点或者整个文件夹范围的权限,而不是配置仅仅单个页面。

你将学到以下内容:

· 怎样运行允许你为站点中所有页设置值(全局值或者帮助类程序设置)的代码;

· 怎样运行允许你为一个文件夹下的所有页设置值的代码;

· 怎样在一个页面加载前和加载后运行代码;

· 怎样发送错误异常信息到一个集中的错误处理页面;

· 怎样为一个文件夹下的所有页添加身份认证;

· ASP.NET怎样使用路由允许你使用可读性和查询性更优的URL。

添加网站启始程序代码

你编写的大多数的代码以及作的设置都是基于单个的页面。比如,如果一个页面需要发送电子邮件,比较典型的做法是在此页面包含所有需要用来初始化发送电子邮件设置(如SMTP服务器的设置)以及发送页面信息本身的代码。

然而,在一些情况下,你可能在站点任何页面开始启动之前运行一些代码。这对于设置可以在站点中任何页面使用的设置值(被称为全局值)很有帮助。一些帮助类程序设置也需要你提供一些信息,比如邮件设置或者账号的密钥,将这些设置保存在全局值中就会很方便。

你可以在网站的根目录下创建一个名为_AppStart.cshtml的页面实现此功能。如果此页面存在,它会在站点中任何页面第一次被请求时运行。因此它是一个好的页面用于运行代码设置全局值(因为_AppStart.cshtml文件名以下划线开始,ASP.NET不会响应页面请求至浏览器即便用户直接请求此页面)。

下图展示了是如何_AppStart.cshtml生效的。当一个来自客户端页面请求发送到服务器端,并且如果这是针对站点中任一页面的第一个请求,ASP.NET会首先检查_AppStart.cshtml是否存在。如果存在,在_AppStart.cshtml中的任何代码都会首先运行,接下来再会处理被请求的页。

clip_image001

为你的站点设置全局值

1. 在WebMatrix站点的根目录下,创建一个名为_AppStart.cshtml的文件。这个文件必须位于站点的根目录中。

2. 用以下代码替换默认的标记和代码:

@{
   AppState["customAppName"] = "Application Name";
}

以上代码将一个值保存在AppState字典表中,它将自动的允许被站点中的任何页面使用。

注意 当你在_AppStart.cshtm文件中添加代码的时候一定要小心。如果添加的代码引起任何错误异常都会导致站点不能成功启动。

3. 在根目录下创建一个新页面名为AppName.cshtml

4. 用以下代码替换此页的默认标记和代码:

<!DOCTYPE html>
<html>
     <head>
         <title>Show Application Name</title>
     </head>
     <body>
         <h1>@AppState["customAppName"]</h1>
     </body>
</html>

以上代码从AppState对象中提取你在_AppStart.cshtml 设置的值.

5. 在浏览器中运行AppName.cshtml页(请确保在你运行之前此页在文件工作区内被选中)。这个页面将会显示设置的全局值。

clip_image002

为帮助类程序设置值

一个 _AppStart.cshtml文件比较典型的用处就是为在你站点中使用且不得不被初始化的帮助类程序设置值。一个比较完美的例子是ReCaptcha帮助类,它需要你为你的reCAPTCHA账号指定公玥和私钥。你可以仅在_AppStart.cshtml 中设置一次,然后站点中的所有页面就可以使用,而无需在需要用到这些密钥的所有页面设置。其他你可以在_AppStart.cshtml 中设置的值包括电子邮件SMTP服务器设置,请参加Chapter 16 - Adding Security and Membership.

下面的步骤将向你展示如果全局性地设置ReCaptcha的密钥(关于使ReCaptcha帮助类的更多信息,请参考see Chapter 16 - Adding Security and Membership)。

1. 如果还没有添加ASP.NET Web Helpers Library至你的站点,按照Chapter 1所述添加。

2. 如果你还没有为你的站点注册ReCaptcha账号,请在at ReCaptcha.Net (http://recaptcha.net)注册。当你完成注册,你会活得一个公钥和私钥。

3. 如果你还没有创建名为_AppStart.cshtml的文件,请在站点根目录下创建此文件。

4. 使用以下代码替换文件_AppStart.cshtml的内容:

@{
   // Add the PublicKey and PrivateKey strings with your public
   // and private keys. Obtain your PublicKey and PrivateKey
   // at the ReCaptcha.Net (http://recaptcha.net) website.
   ReCaptcha.PublicKey = ""; 
   ReCaptcha.PrivateKey = ""; 
}

5. 使用你自己的公私钥设置PublicKeyPrivateKey 两个属性。

6. 保存 _AppStart.cshtml文件并关闭。

7. 在站点根目录下,创建新页Recaptcha.cshtml

8. 使用以下代码替换默认的标记和代码:

@{
   var showRecaptcha = true;
   if (IsPost) { 
     if (ReCaptcha.Validate()) { 
         @:Your response passed!
         showRecaptcha = false;
     }
     else{
       @:Your response didn't pass! 
     } 
   } 
}
<!DOCTYPE html>
<html>
     <head>
         <title>Testing Global Recaptcha Keys</title>
     </head>
     <body>
     <form action="" method="post">
     @if(showRecaptcha == true){
         if(ReCaptcha.PrivateKey != ""){
             <p>@ReCaptcha.GetHtml()</p>
             <input type="submit" value="Submit" />
         }
         else {
             <p>You can get your public and private keys at
             the ReCaptcha.Net website (http://recaptcha.net).
             Then add the keys to the _AppStart.cshtml file.</p>
         }
     }
     </form>
     </body>
</html>

9. 在浏览器中运行页面 Recaptcha.cshtml。如果属性 PrivateKey 的值合法,页面会展示reCAPTCHA 控件以及一个提交按钮。如果你还没有在_AppStart.html 中设置这些密钥的全局值,那么页面则会显示一个错误信息。

clip_image003

10. 输入测试的文字。如果你通过了reCAPTCHA 测试,你会在页面看到相应的提示信息,如“Your response passed”;否则,你会看到一个错误提示信息,并且reCAPTCHA 控件将会重新绘制显示。

在文件夹中文件开始运行之前和之后运行代码

就类似于你可以在_AppStart.cshtml 中编写代码,用于在站点任意页面运行之前执行,你也可以添加在某一特定文件夹下任意页面开始运行前(或者之后)执行的代码。这对于类似设置文件夹下所有页面的布局页面为同一页面,或者运行文件下页面之前检查用户是否登陆将会非常有用。

对于在某一文件夹下的页面,你可以在一个名为_PageStart.cshtml的文件中创建代码。下图展示了_PageStart.cshtml的工作原理。当收到对某一个页面的请求时,ASP.NET会首先检查_AppStart.cshtml页,并运行。然后ASP.NET会检查是否存在_PageStart.cshtml页,如果存在便运行。 最后才运行被请求的页。

_PageStart.cshtml页中,可以通过包含一个名为RunPage的方法来指定在处理过程中何时运行被请求的页。这允许你在被请求页之前并且之后运行代码。如果你不包含RunPage方法,在_PageStart.cshtml中的所有代码会首先运行,然后被请求的页会自动运行。

clip_image004

ASP.NET允许你创建_PageStart.cshtml文件的层级。你可以在站点根目录或者任何子文件夹下创建_PageStart.cshtml文件。当一个页面被请求,位于最上层(最靠近站点根目录)的_PageStart.cshtml文件会首先运行,然后是位于下级子文件夹的_PageStart.cshtml文件,这样依次按照子文件夹的结构运行下去直至包含被请求页的文件夹。当所有适用的_PageStart.cshtml文件运行后,被请求的页才开始运行。

例如,你可能会运用如下_PageStart.cshtml 文件和default.cshtml文件的组合:

@* ~/_PageStart.cshtml *@
@{
   PageData["Color1"] = "Red";
   PageData["Color2"] = "Blue";
}


@* ~/myfolder/_PageStart.cshtml *@
@{
   PageData["Color2"] = "Yellow";
   PageData["Color3"] = "Green";
}


@* ~/myfolder/default.cshtml *@
@PageData["Color1"]
<br/>
@PageData["Color2"]
<br/>
@PageData["Color3"]

当你运行default.cshtml,你将看到以下结果:

Red
Yellow
Green

为文件夹中的所有页运行初始化代码

文件一个典型的用处就是为某一文件夹下的所有文件设置相同的布局页。

1. 在根目录下创建一个新的名为InitPages的子文件夹。

2. 在你网站的InitPages 文件夹中,创建一个文件名为_PageStart.cshtml ,并且用以下代码替换默认的标记和代码:

@{
     // Sets the layout page for all pages in the folder.
     Layout = "~/Shared/_Layout1.cshtml";

     // Sets a variable available to all pages in the folder.
     PageData["MyBackground"] = "Yellow";
}

3. 在网站根目录下,创建一个子文件夹,名为Shared

4. 在Shared 文件夹中,创建名为_Layout1.cshtml的文件,并且用以下代码替换默认的标记和代码:

@{
   var backgroundColor = PageData["MyBackground"];
}
<!DOCTYPE html>
<html>
<head>
   <title>Page Title</title>
   <link type="text/css" href="/Styles/Site.css" rel="stylesheet" />
</head>
<body>
   <div id="header">
     Using the _PageStart.cshtml file
   </div>
   <div id="main" style="background-color:@backgroundColor">
     @RenderBody()
   </div>
<div id="footer">
   &copy; 2010 Contoso. All rights reserved
</div>
</body>
</html>

5. 在InitPages 文件夹中,创建名为Content1.cshtml 的文件,并且用以下代码替换默认的标记:

<p>This is content page 1.</p>

6. 在InitPages 文件夹中,创建另外一个文件,名为Content2.cshtml 并且用以下代码替换默认的标记:

<p>This is content page 2.</p>

7. 在浏览器中运行Content1.cshtml

clip_image005

当页面Content1.cshtml运行,_PageStart.cshtml 文件会设置其Layout,并且设置PageData["MyBackground"] 的值为一种颜色。在Content1.cshtml中,设置的布局和颜色将会体现出来。

8. 在浏览器中显示Content2.cshtml

此页面的布局将会一样,因为这两个页面都使用的在_PageStart.cshtml中初始化的相同的布局页和颜色。

使用 _PageStart.cshtml处理错误异常

另外一个_PageStart.cshtml 文件的好用处是创建一种途径用于处理在一个文件夹下任一.cshtml文件可能抛出的程序错误(异常)。下面的示例将演示如何实现此功能。

1. 在根目录下,创建子文件夹InitCatch

2. 在InitCatch 文件夹中,创建一个名为_PageStart.cshtml 的文件,并且使用以下代码替换默认的标记和代码:

@{ 
     try
     {
         RunPage();
     }
     catch (Exception ex)
     {
         Response.Redirect("~/Error.cshtml?source=" +
             HttpUtility.UrlEncode(Request.AppRelativeCurrentExecutionFilePath));
     }
}

在这段代码中,你尝试通过调用在try 语句块中的RunPage方法显示地运行被请求的页面。如果在被请求页中有任何的程序异常抛出,在catch 语句块中的代码即会运行。在这个例子中,代码会跳转至一个页面 (Error.cshtml) 并且将产生错误的页面的文件名作为URL的参数传递 (稍后你将创建此页面)。

3. 在 InitCatch 文件夹中,创建一个名为Exception.cshtml 的文件,并且用以下代码替换默认的标记和代码:

@{ 
     var db = Database.Open("invalidDatabaseFile");
}

为了达到本例的目的,你正在做的是故意创建一个错误,此错误由尝试打开一个不存在的数据库文件引起。

4. 在根目录下,创建一个名为Error.cshtml 的文件,并且用以下代码替换默认的标记和代码:

<!DOCTYPE html>
<html>
     <head>
         <title>Error Page</title>
     </head>
     <body>
<h1>Error report</h1>
<p>An error occurred while running the following file: @Request["source"]</p>
     </body>
</html>

在此页面中,表达式@Request["source"] 会获取URL中的参数并且显示它。

5. 在工具栏中,点击保存按钮。

6. 在浏览器中运行Exception.cshtml 页面。

clip_image006

由于Exception.cshtml页面发生了一个错误, _PageStart.cshtml 页将会将请求重定向至Error.cshtml 页面,这个页面将会显示错误信息。

关于异常处理的更多信息,请参考Chapter 2 - Introduction to ASP.NET Web Programming Using the Razor Syntax

使用_PageStart.cshtml 限制文件夹访问权限

你还可以使用_PageStart.cshtml 文件限制一个文件夹下的所有页面的访问权限。

1. 使用Site From Template 选项创建一个新的网站。

2. 从现有的模板中,选取Starter Site

3. 在根目录下,创建一个子文件夹名为AuthenticatedContent

4. 在AuthenticatedContent 文件夹中,创建文件_PageStart.cshtml,并且用以下代码替换替换默认的标记和代码:

@{
     Response.CacheControl = "no-cache";

     if (!WebSecurity.IsAuthenticated) {
         Response.Redirect("~/Account/Login");
     }
}

以上代码首先会阻止此文件夹下所有文件被缓存 (这在一些场景,如公共电脑上,是需要的,因为你不想一个用户缓存的页面对下一个用户可用)。 然后,代码会决定用户是否已经登录,只有当授权后,他们才能访问这个文件夹下的任一页面。如果用户未登录,页面将会跳转自登录页面。

5. 在AuthenticatedContent 创建文件Page.cshtml

6. 用以下代码替换默认标记:

@{  
     Layout = "~/_SiteLayout.cshtml";
     Page.Title = "Authenticated Content";
}
<!DOCTYPE html>
<html>
   <head>
     <meta charset="utf-8" />
   </head>
   <body>
     Thank you for authenticating!
   </body>
</html>

7. 在浏览器中运行Page.cshtml。你将会被重定向至登录页面。你只有先注册后才能登录。当你注册登录后,你可以导航至这个页面并且看到它的内容。

创建可读性和查询性更优的URL

在你站点中网页的URL将会对你的站点运行效果产生影响。一个用户体验友好的URL将会使得人们更容易的使用你的网站。它还能帮助站点搜索引擎优化。基于ASP.NET 的网站包含有自动使用友好URL的功能。

关于路由

ASP.NET允许你创建有意义的描述用户操作的URL而不是仅仅指向服务器上一个文件。比较以下一个虚拟的博客的几对URL:

http://www.contoso.com/Blog/blog.cshtml?categories=hardware
http://www.contoso.com//Blog/blog.cshtml?startdate=2009-11-01&enddate=2009-11-30

http://www.contoso.com/Blog/categories/hardware/
http://www.contoso.com/Blog/2009/November

在第一对URL中,用户可能必须知道博客是通过blog.cshtml页面展示,同时还不得不构造带有正确的分类或则日期范围的查询字符串。而上面示例中的第二对URL则能够更容易地理解和创建。

在上面第一个例子中的URL同时还直接的指向了一个特定的文件(blog.cshtml)。如果由于一些原因,博客被移动到服务器上的另外一个文件夹,或者博客被重写使用了另外的页面,那么这些链接URL则会出错。而第二对的URL没有指向特定的页面,因此即便博客的实现或者地址更改,这些URL也依然有效。

在ASP.NET中,因为其使用路由(routing),你可以创建如以上示例所示的更友好的URL。路由会创建能够满足请求的从一个URL到一个文件(或者多个文件)的逻辑上的映射。因为映射是逻辑上的(而非物理上的,指向特定的文件),它可以为你定义你站点中的URL提供更大的灵活性。

路由怎样工作

当ASP.NET处理一个请求的时候,它会读取URL来决定如何路由它。ASP.NET会尝试从左至右的匹配单独的URL片段至硬盘上的文件。如果匹配成功,那么URL中剩余的部分则会作为路径信息传递给匹配的页面。比如,下图是一个网站的文件夹结构:

clip_image007

假设某人用请求以下URL:

http://www.contoso.com/a/b/c

那么搜索会按照以下的方式进行:

1. 是否存在路径为/a/b/c.cshtml的文件? 如果是,则会运行此文件,并且不会传递任何信息。否则 ...

2. 是否存在路径为 /a/b.cshtml的文件? 如果是使用此文件并且将 c 传递给它。.否则…

3. 是否存在路径为 /a.cshtml的文件?如果是,则运行此页面,并且将信息b/c 床底给它。

如果此搜索在指定文件夹中没有找到确切匹配的.cshtml 文件,ASP.NET 会继续轮留寻找以下文:

1. /a/b/c/default.cshtml (无路径信息).

2. /a/b/c/index.cshtml (无路径信息).

注意 请明确这一点,对于指定页面的请求(就是对带有.cshtml文件名后缀的请求)将会按照你预期的运行。一个对于如http://www.contoso.com/a/b.cshtml 的请求会直接运行页面b.cshtml

在一个页面中,你可以通过页面的一个字电表UrlData 属性获取路径信息。假设你有一个名为ViewCustomers.cshtml 的文件,并且你的网站得到了如下的请求:

http://mysite.com/myWebSite/ViewCustomers/1000

如同以上所描述的规则,最终会运行你的页面。在此页面中,你可以使用类似以下的代码用户获取和现实路径信息 (在本例中,值为"1000"):

<!DOCTYPE html>
<html>
     <head>
         <title>URLData</title>
     </head>
     <body>
       Customer ID: @UrlData[0].ToString()
     </body>
</html>

注意Because routing doesn't involve complete file names, there can be ambiguity if you have pages that have the same name but different file-name extensions (for example, MyPage.cshtml and MyPage.html). In order to avoid problems with routing, it's best to make sure that you don't have pages in your site whose names differ only in their extension.

更多的资源

ASP.NET Web Pages with Razor Syntax Reference

Related pages

Issue - Unable to get property 'apply' of undefined or null reference occurred in Angular 4.*, VS2017 15.3, ASP.NET Core 2.0

7302 views   10 comments last modified about 2 years ago

Issue Context After installed Visual Studio 2017 15.3 preview and .net core 2.0 preview SDK, I upgraded one of my existing asp.net core project to 2.0. The project was created using ‘dotnet new angular’ SPA template.&nbsp; I also upgraded all the client app packages to the latest. For exa...

View detail

Retrieve Identity username, email and other information in ASP.NET Core

3331 views   0 comments last modified about 2 years ago

The identity system in ASP.NET has evolved over time. If you are using ASP.NET Core, you probably found User property is an instance of ClaimsPrincipal in Controller or Razor views. Thus to retrieve the information, you need to utilize the claims.

View detail

Retrieve Azure AppSettings and Connection String Settings in ASP.NET Core Apps

2467 views   0 comments last modified about 2 years ago

In ASP.NET Core, we can easily use user secrets to manage our password or credentials. This post will summarize the approaches we can use after the websites are deployed into Azure.

View detail

Server.MapPath Equivalent in ASP.NET Core 2

7083 views   0 comments last modified about 2 years ago

In traditional asp.net applications, Server.MapPath is commonly used to generate absolute path in the web server. However, this has been removed from ASP.NET Core. So what is the equivalent way of doing it?

View detail

Migrating from ASP.NET Core 1.x to ASP.NET Core 2.0

1427 views   2 comments last modified about 2 years ago

Migrating from ASP.NET Core 1.x to 2.0 is not an easy job especially if you have customized Identity and used customized authentication. This post summarizes the issues and errors I have experienced and their resolutions when upgrading my project. Hopefully it can save you sometime if you are doi...

View detail

OpenIddict Refresh Token Flow issue ASP.NET Core 2.0

1496 views   0 comments last modified about 2 years ago

Context When I followed OpenIDDict refresh flow sample, I constantly got the issue “The refresh token is no longer valid”, which is returned by the following code in my authorization web api controller: result.Content = new OpenIdConnectResponse &nbsp;&...

View detail

Add comment

Please login first to add comments.  Log in New user?  Register

Comments (0)

No comments yet.