[转载]发福利了!!超过100本的linux免费书籍 - Jeff Li - 博客园

mikel阅读(1446)

[转载]发福利了!!超过100本的linux免费书籍 – Jeff Li – 博客园.

Android Application Development Books

Web Development Books

 

Office Productivity Tools

 

Desktop & GUI Toolkit Books

 

Database Books

 

Linux Security Books

 

Linux for Beginners Books

 

System Administration Books

 

Linux System Administration Books – Networking

 

openSolaris System Administration Books

 

Linux System Administration Books – General

 

Linux System Administration Books – OpenSUSE Linux

 

Linux System Administration Books – SUSE Linux

 

Linux System Administration Books – Red Hat Linux

 

Linux System Administration Books – CentOS Linux

 

Linux System Administration Books – Ubuntu

 

Linux System Administration Books – Debian GNU/Linux

 

Programming and Scripting Books

Mac OS X and iPhone Development Books

Python Books

Ruby Books

Python Programming Books

Java Programming Books

Shell Scripting Books

C, C++ and C# Programming Books

Perl Programming Books

Windows Development Books

General Development Books

 

Linux Kernel Programming Books

 

Virtualization Books

 

Linux Installation Guides

 

Linux Tools & Utilities Books

 

Development Tools Books

 

Mail Systems

[转载]ASP.NET MVC网站教程(三):动态布局和站点管理 - 滴答的雨(何雨泉) - 博客园

mikel阅读(1253)

[转载]MVC网站教程(三):动态布局和站点管理 – 滴答的雨(何雨泉) – 博客园.

目录

1.   介绍

2.   软件环境

3.   在运行示例代码之前(源代码 + 示例登陆帐号)

4.   自定义操作结果和控制器扩展

1)   OpenFileResult

2)   ImageResult

5.   控制器扩展

6.   自定义HTML帮助器

1)   ImageButton

2)   EnumDropDownList

3)   CustomCheckBox

4)   ImageFromStream

7.   在MVC4.0中引入jqGrid插件(设计技术:AJAX,JSONJQuery,LINQ,序列化

8.   动态布局和站点管理

1)   数据实体

2)   站点设置

3)   站点文件

4)   实现动态布局

9.   如何扩展动态布局

 

介绍

MVC网站教程”系列的目的是教你如何使用 ASP.NET MVC 创建一个基本的、可扩展的网站

1)   MVC网站教程(一):多语言网站框架

2)   MVC网站教程(二):异常管理

3)   MVC网站教程(三):动态布局和站点管理(涉及技术:AJAXjqGridController扩展、HTML Helpers等等)

4)   MVC Basic Site: Step 4 – jqGrid Integration in MVC 4.0 using AJAX, JSON, JQuery, LINQ, and Serialization

 

系列的第一篇文章“多语言网站框架”, 主要讲解如何去创建一个支持多语言的MVC网站,同时也讲解了用户认证和注册机制的实现。使用了微软的Entity Framework框架和LINQ查询技术。

    系列的第二篇文章异常管理”,提出了详细的异常管理规则并在ASP.NET MVC网站中实现异常管理,还提供一些通用的日志记录和异常管理的源代码。这些源代码不仅可以在任何ASP.NET网站中被重用(或经过比较小的改动适用),而且可以重用到任何.NET项目中。

    系列的第三篇文章(即本文),实现了动态布局和站点管理,使用了AJAXjqGrid、自定义操作结果、控制器扩展、HTML帮助器,还使用了一些通用的C#源代码和JavaScript脚本,这些都能被扩展和被重用到其它项目中。

       MVC网站教程”系列的示例网站是采用增量式和迭代式软件过程开发的,这意味着系列中每一篇博文会在前一篇的解决方案中添加更多的功能,所以本文提供的示例下载只包含系列目前为止所介绍的功能。

    网站的布局通常包括标题页眉、菜单和页脚。注意,这些布局对大多数网站都是不变的。

    动态布局,意思是网站管理员能通过网站应用程序提供的管理页面修改网站的布局。管理员的所有修改数据都会保存在数据库中,并且从此以后网站的布局就会按照网站管理员的设置呈现。因此网站管理员能通过web浏览器在任何时候、任何地点改变网站的布局。

    本博文主要包含三个部分。第一部分描述网站中使用的构建块;第二部分演示使用网站管理页面来动态布局;第三部分就如何扩展动态布局给出一些提示。

    网站的构建块(如:自定义操作结果、控制器扩展、自定义HTML帮助器以及一些其他公用类,Razor视图和JavaScript脚本等等)以及整个站点框架你可以重用和扩展成更复杂的网站。

 

软件环境

1.   .NET 4.0 Framework

2.   Visual Studio 2010 (or Express edition)

3.   ASP.NET MVC 4.0

4.   SQL Server 2008 R2 (or Express Edition version 10.50.2500.0)

 

在运行示例代码之前

在运行示例代码之前,你应该做下面事情:

1.   首先使用“管理员身份”运行CreateEventLogEntry控制台项目程序产生的exe,用来在事件日志中创建“MVC Basic”事件源。(EventLog在写日志时会创建指定名称的类别默认为“应用程序”的事件源。但是ASP.NET网站没有足够的权限来创建事件源,需要本地桌面应用程序)

2.   在你的SQL Server服务器中创建一个名为MvcBasicSite的数据库,然后用我提供的MvcBasicSiteDatabase.bak文件进行数据库还原。

3.   修改MVC应用程序示例的Web.config配置文件中的链接字符串。

 

示例帐号

1)   管理员帐户:Administrator   密码:tm77dac

2)   普通帐户: Ana              密码:ana

 

本博文示例下载:

1)   动态布局和站点管理MVC4—示例源代码.zip

2)   动态布局和站点管理—数据库bak.zip

 

自定义操作结果和控制器扩展

    在本节中,我将介绍自定义操作结果和控制器扩展,用于创建站点的动态布局。

    在控制器类中,响应用户输入的每个操作方法执行完工作后返回一个操作结果。操作结果代表MVC框架执行完一个操作指令。所有操作结果类必须继承自ActionResult抽象类。这个抽象类包含下面成员:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class ActionResult
{
    // Summary:
    //     Initializes a new instance of the System.Web.Mvc.ActionResult class.
    protected ActionResult();
    // Summary:
    //     Enables processing of the result of an action method by a custom type that
    //     inherits from the System.Web.Mvc.ActionResult class.
    //
    // Parameters:
    //   context:
    //     The context in which the result is executed. The context information includes
    //     the controller, HTTP content, request context, and route data.
    public abstract void ExecuteResult(ControllerContext context);
}

这里有一系列MVC4.0框架提供的操作结果,它们都直接或间接继承自ActionResult抽象类:

1)   ContentResult

2)   EmptyResult

3)   FileResult

4)   FileContentResult

5)   FilePathResult

6)   FileStreamResult

7)   HttpStatusCodeResult

8)   HttpUnauthorizedResult

9)   JavaScriptResult

10)JsonResult

11)RedirectResult

12)RedirectToRouteResult

13)PartialViewResult

14)ViewResultBase

15)ViewResult

为了创建动态布局,我使用了一些上面已经存在的操作结果类,但我也创建下面两个自定义操作结果类:

1)   OpenFileResult

2)   ImageResult

 

1.   OpenFileResult

这个操作结果类被用于在新浏览器窗口中打开一个文件。在本网站示例中用于在独立的浏览器窗口中打开一个PDFJPGPNG文件,同样也能用于打开其他类型的文件。

image

    从上面类图可知,OpenFileResult包含3个属性,被用于设置内容类型、文件名字和文件存放的虚拟路径。主要功能方法ExecuteResult()如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public override void ExecuteResult(ControllerContext context)
{
    context.HttpContext.Response.Clear();
    context.HttpContext.Response.ClearContent();
    //
    if(this.ContentType != null)
        context.HttpContext.Response.ContentType = ContentType;
    else
        context.HttpContext.Response.AddHeader("content-disposition", "attachment;filename=" + this.FileName);
    //
    context.HttpContext.Response.Cache.SetCacheability(System.Web.HttpCacheability.Public);
    string filePath = (_isLocal
        ? this.FileName
        : string.Format("{0}\\{1}", context.HttpContext.Server.MapPath(this.VirtualPath), this.FileName));
    //
    if (System.IO.File.Exists(filePath))
    {
        context.HttpContext.Response.TransmitFile(filePath);
    }
    else
    {
        context.HttpContext.Response.Write(Resources.Resource.OpenFileResultFileNotFound);
    }
    //
    context.HttpContext.Response.End();
}

从上面代码可知,这个方法首先设置内容类型,然后将文件传送到HTTP响应中,收到响应结果的浏览器将在新窗口中打开一个文件。

应该像下面例子一样使用OpenFileResult

1
2
3
4
5
6
7
8
9
10
11
12
13
public ActionResult GetFileResult(int id)
{
    SiteDocument SiteDocument = _db.SiteDocuments.FirstOrDefault(d => d.ID == id);
    if (SiteDocument == null)
        return RedirectToAction("Home", "Index");
    //
    OpenFileResult result = new OpenFileResult(
       SiteDocument.IsSystemDoc == null && this.Request.IsLocal, "\\Content\\Doc");
    result.FileName = SiteDocument.FileFullName;
    result.ContentType = SiteDocument.ContentType;
    //
    return result;
}

如果是PDF文档,那么将在浏览器新窗口中打开PDF

 

2.   ImageResult

这个操作结果类将图片数据流在当前视图中呈现一个图片。这个数据流可能是从数据库中加载的包含图像的数据流、也可能是从一个文件中加载的包含图像的文件流、或者其他地方传入的数据流。

image

   从上面类图中可知,这个类包含两个属性用于设置内容类型和图像流,主要功能方法ExecuteResult()如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public override void ExecuteResult(ControllerContext context)
{
    if (context == null)
        throw new ArgumentNullException("context");
    //
    try
    {
        HttpResponseBase response = context.HttpContext.Response;
        response.ContentType = this.ContentType;
        //
        if (this.ImageStream == null)
        {
            string filePath = context.HttpContext.Server.MapPath("/Content/noimageSmall.jpg");
            System.Drawing.Image imageIn = System.Drawing.Image.FromFile(filePath);
            MemoryStream ms = new MemoryStream();
            //
            imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            response.OutputStream.Write(ms.ToArray(), 0, (int)ms.Length);
        }
        else
        {
            byte[] buffer = new byte[4096];
            //
            while (true)
            {
                int read = this.ImageStream.Read(buffer, 0, buffer.Length);
                if (read == 0)
                    break;
                //
                response.OutputStream.Write(buffer, 0, read);
            }
        }
        //
        response.End();
    }
    catch (Exception ex)
    {
        MvcBasicLog.LogException(ex);
    }
}

    从上面源代码中可知,这个方法首先设置内容类型,然后读取图像并存到数据流中,最后将数据流以字节流的方式写到HTTP响应输出流中。在接收到响应的浏览器上会将图片呈现到当前视图中。

    ImageResult操作结果的使用方法和上文提到的OpenFileResult相似,但是在MVC示例网站中我是直接作为控制器扩展(ControllerExtensions类)来使用的,详细见下文。

 

控制器扩展

控制器扩展,可在MVC中用于扩展控制器功能。

    MVC网站示例中,我使用ControllerExtensions类来提供创建ImageResult自定义操作结果的API,让所有控制器能访问。

image

    从上面类图中可知,ControllerExtensions是一个静态类,它提供一个拥有两个命名为Image的重载方法。 注意,这两个重载方法都返回ImageResult,但它们签名不同,如下:

1
2
3
4
5
6
7
8
9
10
11
12
public static ImageResult Image(this Controller controller, Stream imageStream, string contentType)
{
    return new ImageResult(imageStream, contentType);
}
public static ImageResult Image(this Controller controller, byte[] imageBytes, string contentType)
{
    if(imageBytes == null || imageBytes.Length == 0)
        return new ImageResult( null , contentType);
    else
        return new ImageResult(new MemoryStream(imageBytes), contentType);
}

第一个,用于呈现一个指定内容类型和图片数据流的图片。

第二个,用于呈现一个指定内容类型和图片字节数组的图片,这个字节数组可以是从数据库、图片文件中读取,也可以是从WCF(Windows Communication Foundation)服务中接受到的图片数据。

控制器扩展可以像下面这样使用:

1
2
3
4
5
6
public ImageResult GetHeaderImage(int id)
{
    SiteSetting shopSetting = _db.SiteSettings.First();
    //
    return this.Image(shopSetting.HeaderImage, "image/jpeg");
}

    这段代码,首先从数据库中加载图片数据,然后使用字节数组来呈现一个图片。

 

自定义HTML帮助器

    自定义HTML帮助器也是实现动态布局的重要构建块,所有自定义帮助器都作为RenderHelper类的静态方法成员。

image

    从上面类图中可知,这里有4个不同的自定义HTML帮助器,并且它们都有重载方法,可以在razor视图中、Controller代码中使用不同的参数进行调用。

 

1.   ImageButton

ImageButton自定义辅助帮助器有4个重载,下面这个是重载通过指定的参数呈现图像按钮,这个图像按钮会与指定的控制器操作进行关联。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static MvcHtmlString ImageButton(this HtmlHelper htmlHelper, string altText, string imageUrl, string controllerName,
       string action, object routeValues, object htmlAttributes = null, object linkAttributes = null)
{
    UrlHelper urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
    //
    // Create an image tag builder for the given image.
    //
    var imageBuilder = new TagBuilder("img");
    imageBuilder.MergeAttribute("src", urlHelper.Content(imageUrl));
    imageBuilder.MergeAttribute("alt", altText);
    imageBuilder.MergeAttribute("title", altText);
    imageBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
    //
    // Create a link tag builder that use the image tag builder!
    //
    var linkBuilder = new TagBuilder("a");
    linkBuilder.MergeAttribute("href", urlHelper.Action(action, controllerName, routeValues));
    linkBuilder.MergeAttributes(new RouteValueDictionary(linkAttributes));
    linkBuilder.InnerHtml = imageBuilder.ToString(TagRenderMode.SelfClosing);
    //
    return MvcHtmlString.Create(linkBuilder.ToString(TagRenderMode.Normal));
}

    这个方法是在razor视图中调用的,它用来根据指定参数呈现图片按钮,并且将其与指定控制器的操作关联起来。

    从上面源代码中可知,这个方法使用指定参数创建一个<a>标签并且里面嵌套一个<img>标签。htmlAttributes属性仅用于<img>标签,linkAttributes属性仅用于<a>标签。

    注意,上面这个方法被所有其他ImageButton重载方法使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
public static string ImageButton(Controller controller, string altText, string imageUrl,
       string action, object routeValues, object htmlAttributes = null, object linkAttributes = null)
{
    HtmlHelper htmlHelper = new HtmlHelper(
        new ViewContext(controller.ControllerContext,
                new WebFormView(controller.ControllerContext, action),
                controller.ViewData,
                controller.TempData,
                TextWriter.Null),
        new ViewPage());
    //
    return ImageButton(htmlHelper, altText, imageUrl, action, routeValues, htmlAttributes, linkAttributes).ToHtmlString();
}

上面代码会根据提供的参数呈现一个图片按钮,并且与当前控制器的操作进行关联。这个方法是在当前控制器中调用的。

1
2
3
4
5
public static MvcHtmlString ImageButton(this HtmlHelper htmlHelper, string altText, string imageUrl,
       string action, object routeValues, object htmlAttributes = null, object linkAttributes = null)
{
    return ImageButton(htmlHelper, altText, imageUrl, null, action, routeValues, htmlAttributes, linkAttributes);
}

上面代码会根据提供的参数呈现一个图片按钮,并且与当前控制器的操作进行关联。这个方法是在razor视图中调用的。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static string ImageButton(Controller controller, string altText, string imageUrl, string controllerName,
       string action, object routeValues, object htmlAttributes = null, object linkAttributes = null)
{
    HtmlHelper htmlHelper = new HtmlHelper(
        new ViewContext(controller.ControllerContext,
                new WebFormView(controller.ControllerContext, action),
                controller.ViewData,
                controller.TempData,
                TextWriter.Null),
        new ViewPage());
    //
    return ImageButton(htmlHelper, altText, imageUrl, controllerName, action,
           routeValues, htmlAttributes, linkAttributes).ToHtmlString();
}

    上面代码会根据提供的参数呈现一个图片按钮,并且与controllerName参数指定的控制器的操作进行关联。这个方法是在razor视图中调用的。

    ImageButtonrazor视图中像下面这样使用:

1
2
3
4
5
6
@Html.ImageButton(Resource.ViewTip,
    "~/Content/view.png",
    "GetFileResult",
    new { id = host.ID },
    new { style = "border:0px;" },
    new { target = "blank_" })

    注意,“@Html”语法是用于调用HtmlHelper类提供的方法,以及在RenderHelpers类中为HtmlHelper类定义的扩展方法。注意:

1)   没有使用带controllerName参数的重载,所以默认使用当前控制器。

2)   第一个参数传递的文本是从资源文件中读取的,以便支持多语言。

3)   最后一个参数设置HTML属性target值为“blank_”,所以当用户单击这个图片按钮时,GetFileResult操作会根据指定的Id获取文件并在新的浏览器窗口中呈现。

1
2
3
4
5
6
7
RenderHelpers.ImageButton(this,
    Resource.ViewTip,
    "~/Content/view.png",
    "GetFileResult",
    new { id = host.ID },
    new { style = "border:0px;" },
    new { target = "blank_" })

    注意上面代码中,GetFileResult操作是在SiteDocumentControler控制器中声明,并非当前控制器,所以在本例中我们使用了另一个含有controllerName参数的重载。

       上面代码将呈现一个图片按钮以及浮动提示信息,像下面截图:

    image

2.   EnumDropDownList

       EnumDropDownList自定义帮助器有2个重载方法,能根据泛型参数TEnum以及其他输入参数呈现一个下拉列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public static MvcHtmlString EnumDropDownList<TEnum>(this HtmlHelper htmlHelper, string name, string action,
    TEnum selectedValue, bool isReadOnly = false)
{
    //
    // Create a list of SelectListItem from all values of the given enum.
    //
    IEnumerable<TEnum> values = Enum.GetValues(typeof(TEnum)).Cast<TEnum>();
    IEnumerable<SelectListItem> items = from value in values
        select new SelectListItem
            {
                Text = value.ToString(),
                Value = value.ToString(),
                Selected = (value.Equals(selectedValue))
            };
    //
    // Render the drop down list by using the list created above.
    //
    if (isReadOnly)
    {
        return MvcHtmlString.Create(htmlHelper.DropDownList(
            name,
            items,
            null,
            new
            {
                @disabled = "disabled",
                style = "color: #999999;readonly:true;",
            }
            ).ToString());
    }
    else
    {
        return MvcHtmlString.Create(htmlHelper.DropDownList(
            name,
            items,
            null,
            new
            {
                onchange = string.Format(
"window.location='/{0}?value='+this.options[this.selectedIndex].value+ '&id='+ $(this).parent().parent()[0].id"
, action)
            }
            ).ToString());
    }
}

        从上面源代码中可知,这个方法使用给定参数创建一个下拉列表。列表中你能选择指定的值,并且当下拉列表中当前选择项改变时会使用javascript脚本触发相应的操作。

        方法的最后一个命名为isReadOnlybool类型参数,是可选参数,当这个参数设置为true的时候将呈现一个只读的下拉列表。

        注意,这个方法被下面EnumDropDownList重载方法调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
public static string EnumDropDownList<TEnum>(Controller controller, string name, string action,
       TEnum selectedValue, bool isReadOnly = false)
{
    HtmlHelper htmlHelper = new HtmlHelper(
        new ViewContext(controller.ControllerContext,
                new WebFormView(controller.ControllerContext, action),
                controller.ViewData,
                controller.TempData,
                TextWriter.Null),
        new ViewPage());
    //
    return EnumDropDownList<TEnum>(htmlHelper, name, action, selectedValue, isReadOnly).ToHtmlString();
}

        上面代码使用给定参数呈现一个下拉列表,并关联上指定Controller的操作。这个方法被设计为在当前控制代码中调用。

        在控制器代码中应该像下面示例这样调用EnumDropDownList辅助帮助器方法。

1
2
3
4
5
Culture = RenderHelpers.EnumDropDownList(this,
    "dropDown",
    "SiteDocument/SetCultureInfo",
    host.Culture != null ? (SiteCultures)host.Culture : SiteCultures.All,
    host.IsSystemDoc == null ? false : true);

    上面代码调用将给定一系列值显示为下拉列表像下面截图:

image

   

        注意,上面的下拉列表仅能从给定的Enum枚举参数中选择值。泛型参数TEnum将被替换为当前使用的枚举。action参数为“SiteDocument/SetCultureInfo”必须是URL格式,包含Controller名,接着是action名字。SetCultureInfo操作必须有一个如下签名的重载:

       public ActionResult SetCultureInfo(string value, string id)

        当用户改变下拉列表值时,将触发SetCultureInfo操作。

3.   CustomCheckBox

       CustomCheckBox自定义帮助器有2个重载方法,它使用给定参数呈现一个复选框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public static MvcHtmlString CustomCheckBox(this HtmlHelper helper, string name, string value,
    string action, bool isReadOnly, object htmlAttributes = null)
{
    TagBuilder builder = new TagBuilder("input");
    //   
    if (Convert.ToInt32(value) == 1)
        builder.MergeAttribute("checked", "checked");
    //
    if (isReadOnly)
    {
        htmlAttributes = new
        {
            @disabled = "disabled",
            style = "color: #999999;readonly:true;",
        };
    }
    else
    {
        htmlAttributes = new
        {
            style = "margin-left:auto; margin-right:auto;",
            onchange = string.Format(
"window.location='/{0}?rowid=' +$(this).parent().parent()[0].id + '&value='+$(this).val()"
, action)
        };
    }
    //
    builder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
    builder.MergeAttribute("type", "checkbox");
    builder.MergeAttribute("name", name);
    builder.MergeAttribute("value", value);
    //
    return MvcHtmlString.Create(builder.ToString(TagRenderMode.SelfClosing));
}

       这个方法设计为在razor视图中调用。

       如上面代码所示,这个方法呈现一个复选框并且通过javascript脚本关联一个给定的控制器操作。倒数第二个参数命名为isReadOnly,当设置为true时,将呈现一个只读的复选框。

       注意,这个方法被下面CustomCheckBox重载方法调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
public static string CustomCheckBox(Controller controller, string name, string action, string value,
    bool isReadOnly, object htmlAttributes = null)
{
    HtmlHelper htmlHelper = new HtmlHelper(
        new ViewContext(controller.ControllerContext,
                new WebFormView(controller.ControllerContext, action),
                controller.ViewData,
                controller.TempData,
                TextWriter.Null),
        new ViewPage());
    //
    return CustomCheckBox(htmlHelper, name, value, action, isReadOnly, htmlAttributes).ToHtmlString();
}

       上面代码根据给定参数呈现一个复选框,并关联上指定Controller的操作。这个方法被设计为在当前控制代码中调用。

       在控制器代码中应该像下面示例这样调用CustomCheckBox辅助帮助器方法:

1
2
3
4
5
IsNotPublic = RenderHelpers.CustomCheckBox(this,
    "checkBox1",
    "SiteDocument/SetIsNotPublic",
    host.IsNotPublic != null && host.IsNotPublic == true ? "1" : "0",
    host.IsSystemDoc == null ? false : true);

    上面代码调用将根据给定参数呈现一个复选框像下面截图:

image

   

4.   ImageFromStream

    ImageFromStream自定义帮助器有4个重载方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public static MvcHtmlString ImageFromStream(this HtmlHelper helper, string altText, string controllerName,
    string action, int imageID, object htmlAttributes = null)
{
    if (imageID > 0)
    {
        UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
        //
        // Create an image tag builder for the given image.
        //
        var imageBuilder = new TagBuilder("img");
        imageBuilder.MergeAttribute("src", (controllerName == null
            ? urlHelper.Action(action, new { ID = imageID })
            : urlHelper.Action(action, controllerName, new { ID = imageID })));
        //
        if (altText != null)
        {
            imageBuilder.MergeAttribute("alt", altText);
            imageBuilder.MergeAttribute("title", altText);
        }
        //
        imageBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
        //
        return MvcHtmlString.Create(imageBuilder.ToString(TagRenderMode.SelfClosing));
    }
    else
    {
        //
        // For invalid image ID return an empty string.
        //
        TagBuilder brTag = new TagBuilder("br");
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.Append("");
        stringBuilder.Append(brTag.ToString(TagRenderMode.SelfClosing));
        //
        return MvcHtmlString.Create(stringBuilder.ToString());
    }
}

    这个方法被设计为在razor视图中调用,这是最多参数的一个方法重载。这个方法根据给定的参数获取数据流最后呈现一个图片。这个图片数据可以从数据库中加载也可以从服务器文件夹中获取。

    如上面代码所示,这个方法根据给定的参数创建了一个图片标签。URL指定的控制器操作用于获取图片数据。给定的HTML属性对象同样用于生成Image标签。

    注意,这个方法被下面ImageFromStream重载方法调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
public static string ImageFromStream(Controller controller, string altText,
    string action, int imageID, object htmlAttributes = null)
{
    HtmlHelper htmlHelper = new HtmlHelper(
        new ViewContext(controller.ControllerContext,
                new WebFormView(controller.ControllerContext, action),
                controller.ViewData,
                controller.TempData,
                TextWriter.Null),
        new ViewPage());
    //
    return ImageFromStream(htmlHelper, altText, action, imageID, htmlAttributes).ToHtmlString();
}

    上面代码根据给定的参数呈现一个图片并且关联当前控制器的操作用于加载图片数据。这个方法被设计为在控制器中调用。

1
2
3
4
5
public static MvcHtmlString ImageFromStream(this HtmlHelper helper, string altText,
    string action, int imageID, object htmlAttributes = null)
{
    return ImageFromStream(helper, altText, null, action, imageID, htmlAttributes);
}

    上面代码根据给定的参数呈现一个图片并且关联当前控制器的操作用于加载图片数据。这个方法被设计为在razor视图中调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
public static string ImageFromStream(Controller controller, string altText, string controllerName,
    string action, int imageID, object htmlAttributes = null)
{
    HtmlHelper htmlHelper = new HtmlHelper(
        new ViewContext(controller.ControllerContext,
                new WebFormView(controller.ControllerContext, action),
                controller.ViewData,
                controller.TempData,
                TextWriter.Null),
        new ViewPage());
    //
    return ImageFromStream(htmlHelper, altText, action, imageID, htmlAttributes).ToHtmlString();
}

    上面代码根据给定的参数呈现一个图片并且关联controllerName参数指定控制器的操作用于加载图片数据。这个方法被设计为在控制器中调用。

    razor视图代码中应该像下面示例这样调用ImageFromStream辅助帮助器方法:

1
2
3
4
5
@Html.ImageFromStream("Home",
    "SiteSetting",
    "GetHeaderImage",
    7,
    new { id = "_leftImage" })

       注意,这里的“@Html”语句用于调用在RenderHelpers类中我们自定义的HTML辅助方法,传入的最后一个参数是将HTMLid属性值设置为“_leftImage”。根据传入参数将调用SiteSettingController控制器的GetHeaderImage操作从数据库中加载图片数据。

    上面代码呈现的页眉图片像下面截图:

image

 

MVC4.0中引入jqGrid插件(设计技术:AJAXJSONJQueryLINQ,序列化)

    jqGrid是一个开源的支持AJAXjavascript控件,它提供在网络上显示和操作表格数据,并且支持通过AJAX回调函数动态的加载数据。

    关于jqGrid控件的帮助文档和示例详细请看:http://www.trirand.com

    我们在MVC基础网站中引入jqGrid插件。这个插件是本网站的一个重要部件,我将在下一篇文章中详细介绍该插件,但是我已将jqGrid插件的源代码包含在本篇博文对应的解决方案中。

   下面是引入jqGrid插件显示的“Visitors”页面截图。(你必须使用指定帐户登录才能查看,帐户:Administrator密码:tm77dac

image

    从上面截图中可知,页面中的grid的列有不同的类型(字符串、日期和bool),并且所有列支持排序功能。最后一个命名为“Actions”的列,使用了ImageButton自定义帮助器方法,当用户按下“Delete”图片按钮时,当前行的访问日志将从数据库中删除。

    注意,grid列表的下面有两个操作按钮:“Reolad Grid”按钮和“Delete All”按钮。

 

动态布局和站点管理

    本章节详细介绍:使用前文提到的技术(自定义操作结果、控制器扩展、HTML帮助器、AJAXjqGrid),来实现动态布局和站点管理。

    首先,我简单的描述下用于站点管理的数据实体,然后我再描述用于管理员

动态布局的站点管理页面和相关控制器类,最后我将描述动态布局的详细实现过程。

1)   数据实体

image

       上图是MVC基础站点解决方案中的部分数据实体。这里有6个实体,并且分别与数据库中的一个表关联。下面3个实体:CountryAddressUser已经在《MVC网站教程(一):多语言网站框架》中介绍了。

1)   VistorLog:存储站点的访问日志条目。每个新用户访问站点将会记录以下信息:用户Id,开始日期,结束日期和过期标记。

2)   SiteSetting:存储当前站点的设置。包含如下信息:联系Id,联系Email,备用Email,页眉描述,页眉图片和站点标题。注意,这个实体存储的信息用于站点动态布局。

3)   SiteDocument: 存储站点的文件数据。站点文件是由站点系统管理员动态上传的,并且与特定语言或所有语言关联。对于每个站点文件会记录下面信息:名字(显示在站点布局 中),文件全名(服务器存放文件路径),语言(文件所关联的特定语言或所有语言),非公共文档标识(非公共文档只有认证用户可查看),是系统文档标识(系 统文档不能被编辑和删除),是协议文档标识(协议文档是用于特定语言的站点协议),日期(上传日期)。注意,这个实体存储的信息用于站点动态布局。

2)   站点设置

    站点主要设置是由站点管理员通过“Settings”页面进行维护的。

image

       从上面图片中可知,通过“Settings”页面,管理员能将设置数据存储到SiteSetting实体中。

       在这个页面,我们不仅能修改联系数据,还能设置下面主要数据的动态布局:

1)   页眉图片:管理员可以选择使用默认图片,或通过上传图片设置一个新的页眉图片。

2)   页眉描述:页眉描述将显示在每个页面的页眉。

3)   页面标题:页面标题将显示在浏览器标签上。

       在像上面这样修改站点设置后,新的页眉图标和页眉描述将从此刻应用到所有站点页面,如下面截图所示:

image

       SiteSettingController继承自BaseController,这个控制器管理着Settings页面的所有操作。

image

       从上面类图可知:这些操作被用于Settings页面,属性HasHeaderImageSiteTitle以及方法GetHeaderImage被用于布局头部信息。详见源代码。

3)   站点文件

    站点文档是站点管理员动态上传到服务器的文件。然后基于文档的属性设置,如用户类型(匿名用户或认证用户),当前语言(English, Română, Deutsch)等判别文档能否被用户访问并且在页脚在显示。

    在当前实现中,站点文档可以是PDFTXTHTMHTMLPNGJPG文件,但可以扩展支持其他类型。

    注意,如果文档是非公开的,那只有认证用户能查看这个文档。并且如果文档是特定语言的,那只有当用户使用这个语言时才能查看。

    站点文档是由站点管理员通过“Site Documents”页面进行管理的。

image

       从上面截图中,我们看到站点文档数据加载到jqGrid列表控件中并且支持排序。

       注意,这些列:CultureNot PublicFor AgreementActions使用了前面描述的自定义HTML帮助器。

       站点管理员可以通过页面底部的上传控件动态添加任意多的文档。注意,每个文档的数据将被存储到数据库中,文件本身将会保存在服务器中当前站点的Content\Doc\folder文件夹中。

       这个grid列表中有3个系统文档,分别命名为SiteAgreement-DE.htmSiteAgreement-RO.htmSiteAgreement-EN.htm,它们被用于特定语言的站点协议。这些文档是系统文档和默认站点协议文档的一部分,被用于特定语言环境,所以不能对它们进行编辑和删除。管理员可以设置其他的文档用于特定语言或所有语言的站点协议,而不再使用默认设置。

       根据文档的类型(PDFJPGPNG文件不能被编辑)将会在最后一列显示一系列的操作。系统文档不能删除,但是可以编辑。PDF文档可以被查看和删除。TXTHTMHTML文档可以被编辑和删除。

       比如,如果你想编辑英语语言环境下的站点协议,你必须按下触发编辑操作的图片按钮。

image

       如上面截图所示,我正在改变英语语言环境下的站点协议文档,新增一段。

       然后保存更改,如果我退出并且在Home页面设置为使用英语语言环境,然后在Home页面的页脚点击“Site-Agreement”文档。文档将在站点中打开,如下图:

image

       在上面截图中,可以看到我在英语语言环境下的站点协议中所添加的一段。

       注意,现在是非认证用户,所以他只能访问公共文档,并且是英语环境或全语言环境属性的文档。

       现在,如果用户使用用户名Ana和密码ana登录,然后改变当前语言环境为“Romănă,用户将可以在页脚访问更多的文档,并且这些文档和之前语言环境的文档不同,就像下图:

image

       从上图中,你能看到当前是认证用户,所以他能访问非公开文档。当前用户设置的是Romanian语言环境,所以能访问该语言或全语言属性的站点文档。在当前页面会显示联系信息。

       注意,在所有情况下,在页脚列出的前两个文档都是特殊的。第一个显示联系信息(一个含有动态信息的静态页面),第二个显示的是当前语言环境下站点协议文档。

       如果你点击的文档是TXTHTMLHTML类型,将直接在站点中打开,就像上文刚提到的协议文档。但是,如果点击的文档是PDFJPGPNG的类型,将在一个新的浏览器窗口中打开提供给用户查看、打印或保存在本地,如下图:

image

       用户点击命名为“icon-sd”的站点文档,则与它关联的PDF文档会在一个新浏览器窗口中打开,用户可以查看,打印,发邮件和保存文档。

       SiteDocumentController继承自BaseController,这个控制器类管理“Site Document”页面的所有操作和从主布局中发出与“Site Document”关联的请求。

image

       从上面类图中,你能看到SiteDocumentController的所有操作方法。更详细请看源代码。

4)   实现动态布局

在本章节,我将向你介绍动态布局的详细实现。

在当前版本,MVC基础网站使用下面两个排版:

a)   _AdminLayout.cshtml:只用于管理员页面。

b)   _Layout.cshtml:用户页面的主要排版。

       这两个排版都通过“_Header.cshtml”局部页面实现相同的页眉,但是它们的菜单和页脚不同。

        系统管理员可以动态设置站点文档,文档将动态呈现在主布局的页脚,就像下面razor代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<tr>
    <td id="headerLeftImage">
        @if (MvcBasicSite.Controllers.SiteSettingController.HasHeaderImage)
        {
            @Html.ImageFromStream("Home", "SiteSetting", "GetHeaderImage", 7, new { id = "_leftImage" })
        }
        else
        {
            <img id="_leftImage" src="@Url.Content("~/Content/HeaderLogo.png")"/>
        }
    </td>
    <td>
        <div class="headerTitle">
             
            @MvcBasicSite.Controllers.SiteSettingController.HeaderDescription
        </div>
        @if (!(Model is LogOnModel))
        {
            <div class="errorMessage">
                @Html.ValidationSummary(true)
            </div>
        }
    </td>
</tr>

       注意,我使用了在前文描述的ImageFromStream自定义HTML帮助器,使用SiteSettingController返回的数据呈现一个图片。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
@if(siteDocuments != null)
{  
    int j, m = siteDocuments.Length;
    //
    // 1st column.
    //
    @:<ul class="column first"><li><a href="#">@Html.ActionLink(
       Resources.Resource.HomeContact, "Contact", "Home")</a></li>
    if (m > 3)
    {
        for (j = 3; j < m; j += 4)
        {
            SiteDocument doc = siteDocuments[j];
            if (doc.IsEditable)
            {
                @:<li><a href="#">@Html.ActionLink(doc.Name,
                  "Details", "SiteDocument", new { id = doc.ID }, null)</a></li>
            }
            else
            {
                @:<li><a href="#">@Html.ActionLink(doc.Name,
                  "GetFileResult", "SiteDocument",
                  new { id = doc.ID }, new { target = "_blank" })</a></li>
            }
        }
    }
    @:</ul>
    //
    // 2nd column.
    //
    @:<ul class="column">
    if (m > 0)
    {
        for (j = 0; j < m; j += 4)
        {
            SiteDocument doc = siteDocuments[j];
            if (doc.IsEditable)
            {
                @:<li><a href="#">@Html.ActionLink(doc.Name,
                  "Details", "SiteDocument", new { id = doc.ID }, null)</a></li>
            }
            else
            {
                @:<li><a href="#">@Html.ActionLink(doc.Name, "GetFileResult",
                  "SiteDocument", new { id = doc.ID }, new { target = "_blank" })</a></li>
            }
        }
    }
    @:</ul>
    //
    // 3rd column.
    //
    if (m > 1)
    {
        @:<ul class="column">
        for (j = 1; j < m; j += 4)
        {
            SiteDocument doc = siteDocuments[j];
            if (doc.IsEditable)
            {
                @:<li><a href="#">@Html.ActionLink(doc.Name, "Details",
                  "SiteDocument", new { id = doc.ID }, null)</a></li>
            }
            else
            {
                @:<li><a href="#">@Html.ActionLink(doc.Name,
                  "GetFileResult", "SiteDocument", new { id = doc.ID },
                  new { target = "_blank" })</a></li>
            }
        }
        @:</ul>
    }
    //
    // 4th column.
    //
    if (m > 2)
    {
        @:<ul class="column">
        for (j = 2; j < m; j += 4)
        {
            SiteDocument doc = siteDocuments[j];
            if (doc.IsEditable)
            {
                @:<li><a href="#">@Html.ActionLink(doc.Name, "Details",
                  "SiteDocument", new { id = doc.ID }, null)</a></li>
            }
            else
            {
                @:<li><a href="#">@Html.ActionLink(doc.Name, "GetFileResult",
                  "SiteDocument", new { id = doc.ID }, new { target = "_blank" })</a></li>
            }
        }
        @:</ul>
    }
}  

       注意,从上面razor代码可知,站点文档被动态呈现为4列。表格的第一行第一个位置不是用于站点文档,而是用来打开“Contact”页面。第二个位置总是用于放置当前站点协议文档.

    注意,SiteDocumentController中的DetailsGetFileResult操作是将文档直接在站点中打开还是在一个新浏览器窗口中打开取决于当前文档是可编辑的(TXTHTMLHTM)还是不可编辑的(PDFJPGPNG)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public ActionResult Details(int id)
{
    SiteDocument SiteDocument = _db.SiteDocuments.Single(u => u.ID == id);
    if (SiteDocument == null)
        return RedirectToAction("Home", "Index");
    //
    try
    {
        string filePath = (SiteDocument.IsSystemDoc == null && this.Request.IsLocal
            ? SiteDocument.FileFullName
            : Path.Combine(HttpContext.Server.MapPath("/Content/Doc"),
               Path.GetFileName(SiteDocument.FileFullName)));
        //
        SiteDocument.FileData = System.IO.File.ReadAllText(filePath);
    }
    catch (Exception ex)
    {
        MvcBasicLog.LogException(ex);
        ModelState.AddModelError("", ex.Message);
    }
    //
    return View(SiteDocument);
}

       上面代码将在用户单击页脚的文档并将从服务器读取一个可编辑的文档时执行,然后将文档数据显示在站点的“Details”页面。注意,Request.IsLocal属性用于判断请求是否来自本地计算机。

1
2
3
4
5
6
7
8
9
10
11
12
public ActionResult GetFileResult(int id)
{
    SiteDocument SiteDocument = _db.SiteDocuments.FirstOrDefault(d => d.ID == id);
    if (SiteDocument == null)
        return RedirectToAction("Home", "Index");
    //
    OpenFileResult result = new OpenFileResult(SiteDocument.IsSystemDoc == null && this.Request.IsLocal, "\\Content\\Doc");
    result.FileName = SiteDocument.FileFullName;
    result.ContentType = SiteDocument.ContentType;
    //
    return result;
}

        上面代码将在用户单击页脚的文档并将从服务器读取一个不可编辑的文档时执行,然后使用OpenFileResult自定义操作返回结果将PDFIMGJPG发送到浏览器并且打开一个新的浏览器窗口。

       注意一下,在用于站点主布局之前,在HomeControllerIndex方法中已将站点文档数据从数据库中读出并且缓存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public ActionResult Index()
{
    if(Session["siteDocuments"] == null)
    {
        //
        // Load and cache the site documents for the current user.
        //
        SiteSession siteSession = this.CurrentSiteSession;
        User user = (siteSession == null ? null : _db.Users.FirstOrDefault(u => u.ID == siteSession.UserID));
        //
        SiteDocument[] shopDocuments = SiteDocument.GetSiteDocumentsForUser(_db, user, SiteSession.CurrentUICulture);
        Session["siteDocuments"] = shopDocuments;
    }
    // TO DO!
    return View();
}

       因为站点文档依赖于当前登录用户和当前选择的语言环境,在源代码中我首先获取当前站点会话数据,然后读取当前用户数据和根据当前选择的语言环境从数据库中读取站点文档。返回的站点文档会被缓存在session中,然后被用于主布局。

       在源代码中还是用了下面SiteCultures枚举:

1
2
3
4
5
6
7
public enum SiteCultures : int
{
    All = -1,
    English = 0,
    Romana = 1,
    Deutsh = 2,
}

       上面源代码定义了站点语言环境以及其对应的整数值。注意,在SiteDocument实体中Culture属性的整数值就与此枚举对应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static SiteDocument[] GetSiteDocumentsForUser(MvcBasicSiteEntities dataContext, User user, int culture)
{
    List<sitedocument> resultsList = (user != null
        ? dataContext.SiteDocuments.Where(d => d.IsForAgreement == null &&
         (d.Culture == -1 || d.Culture == culture)).OrderBy(d => d.ID).ToList()
        : dataContext.SiteDocuments.Where(d => d.IsForAgreement == null &&
         (d.IsNotPublic == null || d.IsNotPublic == false)
            && (d.Culture == -1 || d.Culture == culture)).OrderBy(d => d.ID).ToList());
    //
    SiteDocument document = GetShopAgreementDocument(dataContext, culture);
    if (document != null)
        resultsList.Insert(0, document);
    //
    return resultsList.ToArray();
}

       在应用程序逻辑层,GetSiteDocumentsForUser方法根据给定的参数,使用LINQ从数据库中读取当前用户用权限的商店文档以及读取站点协议文档插入返回数组的第一个位置。

1
2
3
4
5
6
7
8
9
10
public static SiteDocument GetShopAgreementDocument(MvcBasicSiteEntities dataContext, int culture)
{
    SiteDocument SiteDocument = dataContext.SiteDocuments.Where(d => d.IsForAgreement == true
        && d.IsSystemDoc == null && (d.Culture == -1 || d.Culture == culture)).FirstOrDefault();
    if (SiteDocument != null)
        return SiteDocument;
    else
        return dataContext.SiteDocuments.Where(d => d.IsForAgreement == true
            && (d.Culture == -1 || d.Culture == culture)).FirstOrDefault();
}

       上面代码使用LINQ读取适合语言环境下的商店协议文档。如果存在站点管理员定义的协议文档代替默认文档,那么直接使用。否则特定语言环境的系统定义的默认协议文档将被使用

 

如何扩展动态布局

本篇博文解决方案提供的站点动态布局可通过下面方式进行扩展:

a)   Settings表中添加更多设置,然后扩展“Settings”管理员页面允许编辑新增的设置,最后将这些信息在Contact页面或站点布局中显示。

b)   用其他方式使用解决方案中提供的文件类型(TXTHTMLPDFIMGPNG)。

c)   添加CSS文档类型,并且将其用于站点布局。所有站点的CSS也能通过相同方式进行编辑。

    为了实现这个,首先必须修改SiteDocument类的IsEditableContentType类型,然后在SiteDocuments表中添加一个或更多“system document”的CSS文件,最后将CSS样式用于站点布局。

d)   添加更多文档类型并在你的布局中使用它们。

 

 

原文:http://www.codeproject.com/Articles/576286/MVC-Basic-Site-Step-Dynamic-Layouts-and-Site-Adm

作者:Raul Iloc

怎样在IIS下配置PHP,isapi模式php,解决php-cgi.exe占用CPU过多,甚至100%

mikel阅读(910)

.

1、 CGI(通用网关接口/Common Gateway Interface)一般是可执行程序,例如EXE文件,和WEB服务器各自占据着不同的进程,而且一般一个CGI程序只能处理一个用户请求。这样,当用 户请求数量非常多时,会大量占用系统的资源,如内存、CPU时间等,造成效能低下。
2、ISAPI(Internet Server Application Program Interface)是微软提供的一套面向WEB服务的API接口,它能实现CGI提供的全部功能,并在此基础上进行了扩展,如提供了过滤器应用程序接 口。ISAPI应用大多数以DLL动态库的形式使用,可以在被用户请求后执行,,在处理完一个用户请求后不会马上消失,而是继续驻留在内存中等待处理别的 用户输入。此外,ISAPI的DLL应用程序和WEB服务器处于同一个进程中,效率要显著高于CGI。

安装步骤/方法

  • 首先下载Windows的PHP安装包。随后将该包解压至C:\PHP。
  • 完成上面的步骤后,将C:\php目录下的php.ini-dist文件改名为php.ini,然后拷到C: \Windows目录下。用记事本打开C:\Windows目录php.ini文件php.ini里找到register_globals = Off 将 Off 改成 On。再找到extension_dir = “./” 吧”./”里的./改成php里的ext所在的位置,如”C:\php\ext”。
  • 最后将php目录下的php5ts.dll拷到C:\WINDOWS\system32目录下。至此完成PHP的安装步骤。
  • 设定IIS支持PHP
    添加新的WEB扩展服务。按“开始”→“程序”→“管理工具”→“Internet 信息服务(IIS)管理器”打开IIS管理器。选中“Web服务扩展”→添加一个新的Web 服务扩展。添加:php目录下的php5isapi.dll,名称:PHP设置扩展状态为允许:√。完成后选择确定。

  • 添加ISAPI筛选器。按 “Internet 信息服务(IIS)管理器”→“网站”→“属性”→“ISAPI 筛选器”→添加筛选器名称:PHP可执行文件:php目录下的php5isapi.dll。
  • 添加php映射文件。
    选中“Internet 信息服务(IIS)管理器”→“网站”→“默认网站”→“属性”→“主目录”→“配置映射”→添加可执行文件:php目录下的php5isapi.dll扩展名:.PHP。

  • 添加首页默认文件。选中“Internet 信息服务(IIS)管理器”→“网站”→“默认网站”→“属性”→“主目录”→“文档”。添加index.php首页文件。
  • 设定相关权限
    设定PHP文件夹权限。打开PHP文件夹,添加everyone和IUSR及IWAM用户权限。查看相关资料说只需添加IUSR用户权限就可以但我个人试过如果不加入everyone用户则执行不成功。如果设定有误欢迎指正。因为加入everyone不能保证安全。

  • 设定网站文件夹权限。具体设定如上所述。只是文件夹路径不一样。
  • 测试
    重启IIS。查看PHP首页文件。

  • 测试页面。 在浏览器中输入本机的IP地址。(本文转载自百度经验)

[转载]SQL Server 2000/2005数据字典生成方法 - 北纬28.33 - 博客园

mikel阅读(1244)

[转载]SQL Server 2000/2005数据字典生成方法 – 北纬28.33 – 博客园.

--sql server 2000
SELECT
表名 = case when a.colorder=1 then d.name else '' end,
表说明 = case when a.colorder=1 then isnull(f.value,'') else '' end,
字段序号 = a.colorder,
字段名 = a.name,
标识 = case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then '√'else '' end,
主键 = case when exists(SELECT 1 FROM sysobjects where xtype='PK' and parent_obj=a.id and name in (
SELECT name FROM sysindexes WHERE indid in(
SELECT indid FROM sysindexkeys WHERE id = a.id AND colid=a.colid))) then '√' else '' end,
类型 = b.name,
占用字节数 = a.length,
长度 = COLUMNPROPERTY(a.id,a.name,'PRECISION'),
小数位数 = isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0),
允许空 = case when a.isnullable=1 then '√'else '' end,
默认值 = isnull(e.text,''),
字段说明 = isnull(g.[value],'')
FROM
syscolumns a
left join
systypes b
on
a.xusertype=b.xusertype
inner join
sysobjects d
on
a.id=d.id and d.xtype='U' and d.name&lt;&gt;'dtproperties'
left join
syscomments e
on
a.cdefault=e.id
left join
sysproperties g
on
a.id=g.id and a.colid=g.smallid
left join
sysproperties f
on
d.id=f.id and f.smallid=0
where
d.name='要查询的表' --如果只查询指定表,加上此条件
order by
a.id,a.colorder

/*
表名 表说明 字段序号 字段名 标识 主键 类型 占用字节数 长度 小数位数 允许空 默认值 字段说明
------- ----- ------- -------- ---- ------- ------ ------- --------------- ------ ---------- ----------
authors 1 au_id √ id 11 11 0
2 au_lname varchar 40 40 0
3 au_fname varchar 20 20 0
4 phone char 12 12 0 ('UNKNOWN')
5 address varchar 40 40 0 √
6 city varchar 20 20 0 √
7 state char 2 2 0 √
8 zip char 5 5 0 √
9 contract bit 1 1 0
(所影响的行数为 9 行)
*/

--sql server 2005
-- 1. 表结构信息查询
-- ========================================================================
-- 表结构信息查询
-- 邹建 2005.08(引用请保留此信息)
-- ========================================================================
SELECT
TableName=CASE WHEN C.column_id=1 THEN O.name ELSE N'' END,
TableDesc=ISNULL(CASE WHEN C.column_id=1 THEN PTB.[value] END,N''),
Column_id=C.column_id,
ColumnName=C.name,
PrimaryKey=ISNULL(IDX.PrimaryKey,N''),
[IDENTITY]=CASE WHEN C.is_identity=1 THEN N'√'ELSE N'' END,
Computed=CASE WHEN C.is_computed=1 THEN N'√'ELSE N'' END,
Type=T.name,
Length=C.max_length,
Precision=C.precision,
Scale=C.scale,
NullAble=CASE WHEN C.is_nullable=1 THEN N'√'ELSE N'' END,
[Default]=ISNULL(D.definition,N''),
ColumnDesc=ISNULL(PFD.[value],N''),
IndexName=ISNULL(IDX.IndexName,N''),
IndexSort=ISNULL(IDX.Sort,N''),
Create_Date=O.Create_Date,
Modify_Date=O.Modify_date
FROM sys.columns C
INNER JOIN sys.objects O
ON C.[object_id]=O.[object_id]
AND O.type='U'
AND O.is_ms_shipped=0
INNER JOIN sys.types T
ON C.user_type_id=T.user_type_id
LEFT JOIN sys.default_constraints D
ON C.[object_id]=D.parent_object_id
AND C.column_id=D.parent_column_id
AND C.default_object_id=D.[object_id]
LEFT JOIN sys.extended_properties PFD
ON PFD.class=1
AND C.[object_id]=PFD.major_id
AND C.column_id=PFD.minor_id
-- AND PFD.name='Caption' -- 字段说明对应的描述名称(一个字段可以添加多个不同name的描述)
LEFT JOIN sys.extended_properties PTB
ON PTB.class=1
AND PTB.minor_id=0
AND C.[object_id]=PTB.major_id
-- AND PFD.name='Caption' -- 表说明对应的描述名称(一个表可以添加多个不同name的描述)
LEFT JOIN -- 索引及主键信息
(
SELECT
IDXC.[object_id],
IDXC.column_id,
Sort=CASE INDEXKEY_PROPERTY(IDXC.[object_id],IDXC.index_id,IDXC.index_column_id,'IsDescending')
WHEN 1 THEN 'DESC' WHEN 0 THEN 'ASC' ELSE '' END,
PrimaryKey=CASE WHEN IDX.is_primary_key=1 THEN N'√'ELSE N'' END,
IndexName=IDX.Name
FROM sys.indexes IDX
INNER JOIN sys.index_columns IDXC
ON IDX.[object_id]=IDXC.[object_id]
AND IDX.index_id=IDXC.index_id
LEFT JOIN sys.key_constraints KC
ON IDX.[object_id]=KC.[parent_object_id]
AND IDX.index_id=KC.unique_index_id
INNER JOIN -- 对于一个列包含多个索引的情况,只显示第1个索引信息
(
SELECT [object_id], Column_id, index_id=MIN(index_id)
FROM sys.index_columns
GROUP BY [object_id], Column_id
) IDXCUQ
ON IDXC.[object_id]=IDXCUQ.[object_id]
AND IDXC.Column_id=IDXCUQ.Column_id
AND IDXC.index_id=IDXCUQ.index_id
) IDX
ON C.[object_id]=IDX.[object_id]
AND C.column_id=IDX.column_id
-- WHERE O.name=N'要查询的表' -- 如果只查询指定表,加上此条件
ORDER BY O.name,C.column_id

-- 2. 索引及主键信息
-- ========================================================================
-- 索引及主键信息
-- 邹建 2005.08(引用请保留此信息)
-- ========================================================================
SELECT
TableId=O.[object_id],
TableName=O.Name,
IndexId=ISNULL(KC.[object_id],IDX.index_id),
IndexName=IDX.Name,
IndexType=ISNULL(KC.type_desc,'Index'),
Index_Column_id=IDXC.index_column_id,
ColumnID=C.Column_id,
ColumnName=C.Name,
Sort=CASE INDEXKEY_PROPERTY(IDXC.[object_id],IDXC.index_id,IDXC.index_column_id,'IsDescending')
WHEN 1 THEN 'DESC' WHEN 0 THEN 'ASC' ELSE '' END,
PrimaryKey=CASE WHEN IDX.is_primary_key=1 THEN N'√'ELSE N'' END,
[UQIQUE]=CASE WHEN IDX.is_unique=1 THEN N'√'ELSE N'' END,
Ignore_dup_key=CASE WHEN IDX.ignore_dup_key=1 THEN N'√'ELSE N'' END,
Disabled=CASE WHEN IDX.is_disabled=1 THEN N'√'ELSE N'' END,
Fill_factor=IDX.fill_factor,
Padded=CASE WHEN IDX.is_padded=1 THEN N'√'ELSE N'' END
FROM sys.indexes IDX
INNER JOIN sys.index_columns IDXC
ON IDX.[object_id]=IDXC.[object_id]
AND IDX.index_id=IDXC.index_id
LEFT JOIN sys.key_constraints KC
ON IDX.[object_id]=KC.[parent_object_id]
AND IDX.index_id=KC.unique_index_id
INNER JOIN sys.objects O
ON O.[object_id]=IDX.[object_id]
INNER JOIN sys.columns C
ON O.[object_id]=C.[object_id]
AND O.type='U'
AND O.is_ms_shipped=0
AND IDXC.Column_id=C.Column_id
-- INNER JOIN -- 对于一个列包含多个索引的情况,只显示第1个索引信息
-- (
-- SELECT [object_id], Column_id, index_id=MIN(index_id)
-- FROM sys.index_columns
-- GROUP BY [object_id], Column_id
-- ) IDXCUQ
-- ON IDXC.[object_id]=IDXCUQ.[object_id]
-- AND IDXC.Column_id=IDXCUQ.Column_id

[转载]已经不再使用的表为什么数据页还在SQLServer的内存缓存中 - JentleWang - 博客园

mikel阅读(1027)

[转载]已经不再使用的表为什么数据页还在SQLServer的内存缓存中 – JentleWang – 博客园.

1. 问题发现

 

在学习内存调优时,使用如下代码,查询目前内存缓冲区中生产数据库的每个对象缓存页计数

SELECT count(*)AS cached_pages_count
,name ,index_id
FROM sys.dm_os_buffer_descriptors AS bd
INNER JOIN
(
SELECT object_name(object_id) AS name
,index_id ,allocation_unit_id
FROM sys.allocation_units AS au
INNER JOIN sys.partitions AS p
ON au.container_id = p.hobt_id
AND (au.type = 1 OR au.type = 3)
UNION ALL
SELECT object_name(object_id) AS name
,index_id, allocation_unit_id
FROM sys.allocation_units AS au
INNER JOIN sys.partitions AS p
ON au.container_id = p.partition_id
AND au.type = 2
) AS obj
ON bd.allocation_unit_id = obj.allocation_unit_id
WHERE database_id = db_id()
GROUP BY name, index_id
ORDER BY cached_pages_count DESC;

 

发现前段应用程序已经不再使用的表,数据页缓存数排在首位,而且页数非常之 多。这张表是之前用来作为数据统计分析的计算报表,后来上了数据仓库以及BI系统后,这张表就废弃掉了。但为什么内存中还是缓存这么多数据页?现在系统的 内存并非是足够大,大到可以缓存整个数据库的数据页面,所以,感觉这些不再使用的数据页面应该被置换出内存

 

2.问题分析

 

 为了验证这种表的使用情况,使用如下语句查询表的索引使用情况

 

1
2
3
SELECT
FROM sys.dm_db_index_usage_stats ddius
WHERE ddius.object_id = OBJECT_ID('t_rpt_office_shop_data')

 

发现索引的user_scan 等确实为0,代表自上次服务启动以来,应用程序没用提交过对这个表的查询。

 

  但发现system_scan不为0,而且last_system_scan的日期为凌晨。

 

  所以猜想是不是凌晨的数据库维护计划中有导致system_scan的操作

 

  继续查看维护计划,发现果真有一个任务–更新统计信息任务,而且扫描类型是完全扫描

 

3.问题原因

 

所以,问题是由每天早上的更新统计信息任务造成,因为执行完全扫描,相当于每天凌晨对这个表执行了一次全表扫描,此时会将数据页加载到内存缓冲区中。而且我发现凌晨这段时间内存的页面生命周期基本接近为0,估计也是这个造成的。

 

4.问题处理

 

以下是我的处理方式,大家指正下~

 

  1. 将维护计划中的更新统计信息步骤删除掉,第二天发现内存缓冲的对象数据页计数基本和预测一致。
  2. 将不在使用的表进行数据压缩,减少磁盘空间的占用

[转载]android学习日记19--四大组件之Services(服务) - aiguozhe1991 - 博客园

mikel阅读(1135)

[转载]android学习日记19–四大组件之Services(服务) – aiguozhe1991 – 博客园.

Android四大基本组件分别是Activity,Content Provider内容提供者,Service服务,BroadcastReceiver广播接收器。
其中Activity和Content Provider在前面都有介绍过。这里主要讲讲Service服务和BroadcastReceiver广播接收器。
一、Services(服务)

 

1、简述
Services(服务)简单来说就是剥夺界面的Activity。它和Activity很多概念都是相似的,都是封装有一个完整的功能逻辑实现。
Services是运行在后台的一段代码,它可以运行在它自己的进程,也可以运行在其他应用程序进程的上下文(context)里面,
其它的组件可以绑定到一个服务(Service)上面,通过远程过程调用(RPC)来调用这个方法。常见的Services如后台音乐播放,
后台计算数据。

 

2、运行原理
有两种运行方式,原理如下:
a、使用Context.startService()来启动一个Service,从而可以在后台调用Service。同时,系统也将保持这个Service一直执行,
直到这个Service运行结束。
b、使用Context.bindService()方法,连接到一个Service上(如果这个Service还没有运行将启动它)。当连接到一个Service之后,
我们还可以Service提供的接口与它进行通讯。

 

3、生命周期
官方生命周期的图示:

a、startService后,即使调用startService的进程结束了,Service仍然还存在,直到有进程调用stopService,或者Service自己自杀(stopSelf())。
b、bindService后,Service就和调用bindService的进程同生共死了,也就是说当调用bindService的进程死了,那么它bind的Service也要跟着被结束,
当然期间也可以调用unbindservice让 Service结束。
c、两种方式混合使用时,比如说你startService了,我bindService了,那么只有你stopService了而且我也unbindservice了,这个Service才会被结束。

 

4、使用步骤
a、继承service类(一般用它的子类IntentService)

b、AndroidManifast.xml配置清单文件中<application>节点里对服务进行配置

<service name=”.SMSService”/>
c、服务不能自己运行,需要通过Contex.startService()或Contex.bindService()启动服务

通过startService()方法启动的服务于调用者没有关系,即使调用者关闭了,服务仍然运行想停止服务要调用Context.stopService(),
此时系统会调用onDestory(),使用此方法启动时,服务首次启动系统先调用服务的onCreate()–>onStart(),如果服务已经启动再次调用只会触发onStart()方法
使用bindService()启动的服务与调用者绑定,只要调用者关闭服务就终止,使用此方法启动时,服务首次启动系统先调用服务的onCreate()–>onBind(),
如果服务已经启动再次调用不会再触发这2个方法,调用者退出时系统会调用服务的onUnbind()–>onDestory(),想主动解除绑定可使用Contex.unbindService(),
系统依次调用onUnbind()–>onDestory();

 

5、运行实例

写个后台播放音乐的例子,点击’start’按钮调用Service播放音乐,点击’stop’按钮停止Service停止音乐。

MyServices代码:

public class MyService extends Service {

private static final String TAG = "MyService";
MediaPlayer player;

@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public void onCreate() {
Toast.makeText(this, "My Service Created", Toast.LENGTH_LONG).show();
Log.v(TAG, "onCreate");

player = MediaPlayer.create(this, R.raw.ten_year);//运行例子是,需要替换音乐的名称
player.setLooping(false); // Set looping
}

@Override
public void onDestroy() {
Toast.makeText(this, "My Service Stopped", Toast.LENGTH_LONG).show();
Log.v(TAG, "onDestroy");
player.stop();
}

@Override
public void onStart(Intent intent, int startid) {
Toast.makeText(this, "My Service Started", Toast.LENGTH_LONG).show();
Log.v(TAG, "onStart");
player.start();
}

}

调用、停止服务:

1  startService(new Intent(this, MyService.class));  
2  stopService(new Intent(this, MyService.class));

最后别忘了AndroidManifast.xml里声明服务:

1 <service android:enabled="true" android:name=".MyService" />

 

BroadcastReceiver广播接收器放在下一篇介绍吧!

[转载]解决easyui datagrid加载数据时,checkbox列没有根据checkbox的值来确定是否选中 - junjieok - 博客园

mikel阅读(1145)

[转载]解决easyui datagrid加载数据时,checkbox列没有根据checkbox的值来确定是否选中 – junjieok – 博客园.

背景:

    昨天帮朋友做一个easyui datagrid的小实例时,才发现easyui datagrid的checkbox列,没有根据值为true或false来选中checkbox,当时感觉太让人失望了,这样的问题就没有解决,太不人性化了。因为从去年到今年一直用的是miniui,所以用起来就会有对比了,easyui和miniui各个方便相比差的太远了,也有可能是miniui是商业的吧!

  记录下来解决方法,以便日后用的时候方便查找,如果有更好方法请留言,互相学习学习!

 

问题:

先上个几个图片让大家有一个直观的印象

easyui datagrid表格的列配置内容

json数据

datagrid显示的效果

   就是一个简单的datagrid,有一个checkbox列,问题就出在 checkbox列,当加载数据时checkbox列的值有的给的是true,但是给true的checkbox却没有被选中,这就是问题的所在,相信大 家从图片中也能看出,现在前端UI很多,但大多数的datagrid应该实现了这个功能。

 

解决方法:

方法一:在onLoadSuccess事件中处理

代码如下:

1
2
3
4
5
6
7
8
function onLoadSuccess(data) {
    var rowData = data.rows;
    $.each(rowData, function (idx, val) {
        if (val.ck) {
            $("#dg").datagrid("selectRow", idx);
        }
    });
}

此方法是从网上找的,但是感觉不是很好,而且数据多了,效率肯定不行了,所以就有了方法二。

 

方法二修改easyui源代码

跟踪源代码最后找到了要修改的地方,主要修改renderRow方法中的checkbox分支

源代码如下:

1
2
3
if (col.checkbox) {
    cc.push("<input type=\"checkbox\" name=\"" + _69e + "\" value=\"" + (_69f != undefined ? _69f : "") + "\">");
}

 仔细一看发现的的确确,没有实现此功能,其实很简单,改一下当_69f为true的时候加上checked=”checked”的属性即可(注意:我改的是1.3.5的源代码)。

改后的代码如下:

1
2
3
4
if (col.checkbox) {
    //<input type="checkbox" value="true" checked="checked"/>
    cc.push("<input type=\"checkbox\" name=\"" + _69e + "\" value=\"" + (_69f != undefined ? _69f : "") + "\"" + (_69f ? "checked=\"checked\"" : "") + "\"/>");
}

 

成果:

上一个简单的图片来看一下

[转载]C#开发微信门户及应用(1)--开始使用微信接口 - 伍华聪 - 博客园

mikel阅读(976)

[转载]C#开发微信门户及应用(1)–开始使用微信接口 – 伍华聪 – 博客园.

微信应用如火如荼,很多公司都希望搭上信息快车,这个是一个商机,也是一个技术的方向,因此,有空研究下、学习下微信的相关开发,也就 成为日常计划的重要事情之一了。本系列文章希望从一个循序渐进的角度上,全面介绍微信的相关开发过程和相关经验总结,希望给大家了解一下相关的开发历程。 本随笔主要针对微信开发过程的前期准备和一些初始的工作的介绍。

在写下本文的之前一周时间里,我主要就是参考一些介绍文章以及微信公众平台的相关接口说明,并结合C#的代码开发,整理了自己公司的门 户界面,实现了微信工作号的初步用户交互和信息展示工作,随着工作的进一步开展,越来越多的功能可能加入,并希望从应用角度上扩展微信的接口,从而实现我 对微信接口的技术探秘和了解过程。

1、微信账号

要开发使用微信的平台API,就需要到微信的公众平台(https://mp.weixin.qq.com/)去注册,拥有一个服务号或者订阅号,服务号主要面对企业和组织,订阅号主要面向组织和个人,他们之间有一定的差异,根据不同的需要自己申请对应的账号即可。

为了使用一些高级的接口,你可能需要拥有服务号和高级的认证。账号注册过程,需要下载一个申请表格,打印并盖公章,另外还需要申请人拿着身份证拍照(有点怪异,呵呵),然后上传到服务器进行审核,一般很快就能获取批复。

我以公司名义申请了服务号,账号注册后,会在主界面上显示你的相关信息,另外给你申请一个二维码的东西,扫描二维码即可进入公司的微信关注确认对话框,非常方便。如下就是我申请后的公司账号二维码,可以直接使用扫描。

2、微信菜单定义

微信有两种方式的菜单定义,一种是编辑模式,一种是开发模式,两者互斥,也就是说,一旦我们采用了开发模式,就不能使用编辑模式了,反过来也一样。编辑下的菜单,其实也是可以管理的,但是微信不支持,觉得很不爽。

一般情况下,如果我们刚刚申请了微信号码,可以使用编辑菜单测试一下,根据说明编辑一些菜单试试。虽然微信说24小时内更新,不过一般很快,最快可能一两分钟就更新了,感觉还是不错的。

使用开发者模式,你需要根据微信的要求,在服务器上放置一个页面链接,使用C#开发的,可以采用***.ashx的命名方式,使用ASP.NET的一般处理程序即可,不需要使用普通的页面。

使用开发模式的菜单,也就是可以调用微信API进行菜单创建的工作,对于调用微信的API(微信有很多API可以调用),我们需要知道,有几个参数的重要性,所以在开发模式打开的时候,会给你列出这些参数,如下所示。

 3、接入微信的链接处理

上面说了,你申请开发模式对菜单或者对其他API的调用,你需要顺利通过接入微信的测试,也就是确认你填写的链接存在并能顺利经过微信的回调测试。微信提供了一个PHP的页面处理例子,如果我们是C#开发的呢,可以搜一下就会得到答案,我的处理方式如下所示。

创建一个一般处理程序,然后在其处理页面里面增加一个处理逻辑,如果是非POST方式的内容,就是表示微信进行的Get测试,你需要增加一些处理逻辑,把它给你的内容传回去即可,如果是POST方式的,就是微信服务器对接口消息的请求操作了,后面介绍。

///
/// 微信接口。统一接收并处理信息的入口。
///

public class wxapi : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string postString = string.Empty;
if (HttpContext.Current.Request.HttpMethod.ToUpper() == "POST")
{
using (Stream stream = HttpContext.Current.Request.InputStream)
{
Byte[] postBytes = new Byte[stream.Length];
stream.Read(postBytes, 0, (Int32)stream.Length);
postString = Encoding.UTF8.GetString(postBytes);
}

if (!string.IsNullOrEmpty(postString))
{
Execute(postString);
}
}
else
{
Auth(); //微信接入的测试
}
}

一般来说,Auth函数里面,就是要对相关的参数进行获取,然后进行处理返回给微信服务器。

string token = "****";//你申请的时候填写的Token

string echoString = HttpContext.Current.Request.QueryString["echoStr"];
string signature = HttpContext.Current.Request.QueryString["signature"];
string timestamp = HttpContext.Current.Request.QueryString["timestamp"];
string nonce = HttpContext.Current.Request.QueryString["nonce"];

完整的Author函数代码如下所示,其中我把业务逻辑进行进一步抽取到了一个新的类里面,方便业务逻辑的管理。

///
/// 成为开发者的第一步,验证并相应服务器的数据
///

private void Auth()
{
string token = ConfigurationManager.AppSettings["WeixinToken"];//从配置文件获取Token
if (string.IsNullOrEmpty(token))
{
LogTextHelper.Error(string.Format("WeixinToken 配置项没有配置!"));
}

string echoString = HttpContext.Current.Request.QueryString["echoStr"];
string signature = HttpContext.Current.Request.QueryString["signature"];
string timestamp = HttpContext.Current.Request.QueryString["timestamp"];
string nonce = HttpContext.Current.Request.QueryString["nonce"];

if (new BasicApi().CheckSignature(token, signature, timestamp, nonce))
{
if (!string.IsNullOrEmpty(echoString))
{
HttpContext.Current.Response.Write(echoString);
HttpContext.Current.Response.End();
}
}
}

而对微信参数的签名并返回的操作CheckSignature,代码如下所示。

///
/// 验证微信签名
///

public bool CheckSignature(string token, string signature, string timestamp, string nonce)
{
string[] ArrTmp = { token, timestamp, nonce };

Array.Sort(ArrTmp);
string tmpStr = string.Join("", ArrTmp);

tmpStr = FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1");
tmpStr = tmpStr.ToLower();

if (tmpStr == signature)
{
return true;
}
else
{
return false;
}
}

4、使用开发方式创建菜单

一旦你顺利通过微信的认证,那么它就让你以开发方式调用它的API,并且可以随意创建你的菜单了。

创建菜单的方式,你可以通过下面的位置进入到他的API处理界面里面。

进入后,你会发现微信把很多消息的处理,分门别类放到不同的分类里面了。

其实我们现在初步要做的就是如何看看,使用代码方式调用创建菜单,进入菜单的API调试界面里面。

你会发现里面还需要输入一个Access_Token的东西,这个是一个会话身份认证,因此你还需要到接口里面去找这个如何创建的。下面图中的两个红色部分,就是我们开始的时候,微信提示我们“开发者凭据”的两个关键参数。

弄完这些,你就可以根据获得的Access_Token进行菜单的创建工作了,根据菜单的定义,它分为几类,可以分为URL方式(View),事件方式(Click)。

click:用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event 的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;
view:用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的url值 (即网页链接),达到打开网页的目的,建议与网页授权获取用户基本信息接口结合,获得用户的登入个人信息。

 

4、我创建的菜单案例

在随笔的开始,我公布了一个二维码,一旦使用微信扫一扫,进行关注服务号后,那么就可以看到我自己创建的菜单了。主菜单一般最多三列,每个主菜单还可以有子菜单,他们的文字都有所限制的。

我们来看看我公司的微信门户菜单,看起来是不是很酷呢。

[转载]C#开发微信门户及应用(2)--微信消息的处理和应答 - 伍华聪 - 博客园

mikel阅读(1113)

[转载]C#开发微信门户及应用(2)–微信消息的处理和应答 – 伍华聪 – 博客园.

微信应用如火如荼,很多公司都希望搭上信息快车,这个是一个商机,也是一个技术的方向,因此,有空研究下、学习下微信的相关开发,也就 成为计划的安排事情之一了。本系列文章希望从一个循序渐进的角度上,全面介绍微信的相关开发过程和相关经验总结,希望给大家了解一下相关的开发历程。本篇 随笔主要基于上一篇《C#开发微信门户及应用(1)–开始使用微信接口》的基础上进行深入的介绍,介绍微信消息的处理和应答的过程。

1、微信的消息应答交互

我们知道,微信的服务器架起了客户手机和开发者服务器的一个桥梁,通过消息的传递和响应,实现了与用户的交互操作,下面是它的消息流程图。

微信向开发者服务器请求的消息包含了多种类型,不过基本来说,分为了文本消息处理、事件消息处理、语音消息的识别,以及成为开发者之前的那个消息认证操作基本分类,下面是我绘制的一个消息分类图,其中介绍了这几种关系,以及各自的消息细化分类。

对于这些消息的请求,我们在开发服务器端,需要编写相关的逻辑进行对应给的处理,然后给微信服务器平台回应消息即可。

在前一篇的随笔里面我贴过代码,介绍微信消息处理的入口操作,代码如下所示。

public void ProcessRequest(HttpContext context)
{
//WHC.Framework.Commons.LogTextHelper.Info("测试记录");

string postString = string.Empty;
if (HttpContext.Current.Request.HttpMethod.ToUpper() == "POST")
{
using (Stream stream = HttpContext.Current.Request.InputStream)
{
Byte[] postBytes = new Byte[stream.Length];
stream.Read(postBytes, 0, (Int32)stream.Length);
postString = Encoding.UTF8.GetString(postBytes);
}

if (!string.IsNullOrEmpty(postString))
{
Execute(postString);
}
}
else
{
Auth();
}
}

其中的Execute(postString);就是对消息的处理函数,它实现了对不同消息的分发处理过程。‘

///
/// 处理各种请求信息并应答(通过POST的请求)
///

///POST方式提交的数据 private void Execute(string postStr)
{
WeixinApiDispatch dispatch = new WeixinApiDispatch();
string responseContent = dispatch.Execute(postStr);

HttpContext.Current.Response.ContentEncoding = Encoding.UTF8;
HttpContext.Current.Response.Write(responseContent);
}

里面的WeixinApiDispatch就是一个分发的管理类,它提取请求消息的内容,并构建不同类型的消息参数,传递给不同的响应函数进行处理,然后返回封装好的XML内容,作为响应。

具体的代码处理逻辑如下图所示。

这个消息处理接口,其实就是定义好一系列的对请求消息的处理操作,参数是不同给的消息对象,具体的代码定义如下所示(由于篇幅原因,省略部分接口,具体可以参考上图)。

///
/// 客户端请求的数据接口
///

public interface IWeixinAction
{
///
/// 对文本请求信息进行处理
///

///文本信息实体 ///
string HandleText(RequestText info);

///
/// 对图片请求信息进行处理
///

///图片信息实体 ///
string HandleImage(RequestImage info);

...........................

///
/// 对订阅请求事件进行处理
///

///订阅请求事件信息实体 ///
string HandleEventSubscribe(RequestEventSubscribe info);

///
/// 对菜单单击请求事件进行处理
///

///菜单单击请求事件信息实体 ///
string HandleEventClick(RequestEventClick info);

..............................
}

从上面的代码可以看出,不同的消息,到处理函数这里,就以不同的消息实体类的方式传递过来了(注意:实体类是我根据程序开发需要自己定义的,非微信本身的实体类),这样非常方便我们处理操作,否则每次需要解析不同的消息内容,很容易出现问题,这样强类型的数据类型,提高了我们开发微信应用的强壮型和高效性。这些实体类的对象有一定的继承关系的,他们的继承关系如下所示。

2、微信的管理接口

上面的消息分类是微信服务器向开发者服务器发送的消息请求操作,还有一种消息,是我们开发者服务器向微信服务器进行的消息请求或者响应,这种这里暂且称之为微信的管理接口,它表明了我们可以通过这些接口进行相关的消息回复或者数据管理操作。它的分类图如下所示。

微信的回复消息处理,它也和上面小节的信息一样,它也是继承自BaseMessage实体类的(同样,下图的实体类及其继承关系也是自定义的,方便程序开发),它的关系如下所示

回复的消息,一般用的最多的是文本消息和图文消息。

文本消息的效果如下所示。

图文消息,可以增加图片,还可以增加详细的链接页面,是非常好看的一种效果,对于一些内容比较多,希望展现更好效果的,一般采用这种,效果如下所示。

[转载]PHP网站常见安全漏洞,及相应防范措施总结 - 站长之家

mikel阅读(1043)

[转载]PHP网站常见安全漏洞,及相应防范措施总结 – 站长之家.

目前,基于PHP的网站开发已经成为目前网站开发的主流,本文笔者重点从PHP网站攻击与安全防范方面进行探究,旨在减少网站漏洞,希望对大家有所帮助!

一、常见PHP网站安全漏洞

对于PHP的漏洞,目前常见的漏洞有五种。分别是Session文件漏洞、SQL注入漏洞、脚本命令执行漏洞、全局变量漏洞和文件漏洞。这里分别对这些漏洞进行简要的介绍。

1、session文件漏洞

Session攻击是黑客最常用到的攻击手段之一。当一个用户访问某一个网站时,为了免客户每进人一个页面都要输人账号和密码,PHP设置了Session和Cookie用于方便用户的使用和访向。

2、SQL注入漏洞

在进行网站开发的时候,程序员由于对用户输人数据缺乏全面判断或者过滤不严导致服务器执行一些恶意信息,比如用户信息查询等。黑客可以根据恶意程序返回的结果获取相应的信息。这就是月行胃的SQL注入漏洞。

3、脚本执行漏洞

脚本执行漏洞常见的原因是由于程序员在开发网站时对用户提交的URL参数过滤较少引起的,用户提交的URL可能包含恶意代码导致跨站脚本攻击。脚本执行漏洞在以前的PHP网站中经常存在,但是随着PHP版本的升级,这些间题已经减少或者不存在了。

4、全局变量漏洞

PHP中的变量在使用的时候不像其他开发语言那样需要事先声明,PHP中的变量可以不经声明就直接使用,使用的时候系统自动创建,而且也不需要对变 量类型进行说明,系统会自动根据上下文环境自动确定变量类型。这种方式可以大大减少程序员编程中出错的概率,使用起来非常的方便。

5、文件漏洞

文件漏洞通常是由于网站开发者在进行网站设计时对外部提供的数据缺乏充分的过滤导致黑客利用其中的漏洞在Web进程上执行相应的命令。假如在 lsm.php中包含这样一段代码:include($b.”/aaa.php”.),这对黑客来说,可以通过变量$b来实现远程攻击,可以是黑客自已的 代码,用来实现对网站的攻击。可以向服务器提交a.php include=http://lZ7.0.0. 1/b.php,然后执行b.php的指令。

二、PHP常见漏洞的防范措施

1、对于Session漏洞的防范

从前面的分析可以知道,Session攻击最常见的就是会话劫持,也就是黑客通过各种攻击手段获取用户的Session ID,然后利用被攻击用户的身份来登录相应网站。为此,这里可以用以下几种方法进行防范:一是定期更换Session ID,更换Session ID可以用PHP自带函数来实现;二是更换Session名称,通常情况下Session的默认名称是PHPSESSID,这个变量一般是在cookie 中保存的,如果更改了它的名称,就可以阻档黑客的部分攻击;三是对透明化的Session ID进行关闭处理,所谓透明化也就是指在http请求没有使用cookies来制定Session id时,Sessioin id使用链接来传递.关闭透明化Session ID可以通过操作PHP.ini文件来实现;四是通过URL传递隐藏参数,这样可以确保即使黑客获取了session数据,但是由于相关参数是隐藏的,它 也很难获得Session ID变量值。

2、对SQL注入漏洞的防范

黑客进行SQL注入手段很多,而且灵活多变,但是SQL注人的共同点就是利用输入过滤漏洞。因此,要想从根本上防止SQL注入,根本解决措施就是加 强对请求命令尤其是查询请求命令的过滤。具体来说,包括以下几点:一是把过滤性语句进行参数化处理,也就是通过参数化语句实现用户信息的输入而不是直接把 用户输入嵌入到语句中。二是在网站开发的时候尽可能少用解释性程序,黑客经常通过这种手段来执行非法命令;三是在网站开发时尽可能避免网站出现bug,否 则黑客可能利用这些信息来攻击网站;仅仅通过防御SQL注入还是不够的,另外还要经常使用专业的漏洞扫描工具对网站进行漏洞扫描。

3、对脚本执行漏洞的防范

黑客利用脚本执行漏洞进行攻击的手段是多种多样的,而且是灵活多变的,对此,必须要采用多种防范方法综合的手段,才能有效防止黑客对脚本执行漏洞进 行攻击。这里常用的方法方法有以下四种。一是对可执行文件的路径进行预先设定。可以通过safe_moade_exec_dir来实现;二是对命令参数进 行处理,一般用escapeshellarg函数实现;三是用系统自带的函数库来代替外部命令;四是在操作的时候进可能减少使用外部命令。

4、对全局变量漏洞防范

对于PHP全局变量的漏洞问题,以前的PHP版本存在这样的问题,但是随着PHP版本升级到5.5以后,可以通过对php.ini的设置来实现,设 置ruquest_order为GPC。另外在php.ini配置文件中,可以通过对magic_quotes_runtime进行布尔值设置是否对外部 引人的数据中的溢出字符加反斜线。为了确保网站程序在服务器的任何设置状态下都能运行。可以在整个程序开始的时候用 get_magic_quotes_runtime检测设置状态决定是否要手工处理,或者在开始(或不需要自动转义的时候)用 set_magic_quotes_runtime(0)关掉。

5、对文件漏洞的防范

对于PHP文件漏桐可以通过对服务器进行设置和配置来达到防范目的。这里具体的操作如下:一是把PHP代码中的错误提示关闭,这样可以避免黑客通过 错误提示获取数据库信息和网页文件物理路径;二是对open_basedir尽心设置,也就是对目录外的文件操作进行禁止处理;这样可以对本地文件或者远 程文件起到保护作用,防止它们被攻击,这里还要注意防范Session文件和上载文件的攻击;三是把safe-made设置为开启状态,从而对将要执行的 命令进行规范,通过禁止文件上传,可以有效的提高PHP网站的安全系数。

声明:本文由郑州批发市场:http://www.shun-e.com/market/ 原创投稿,尊重他人成果,转载请注明出处!

注:相关网站建设技巧阅读请移步到建站教程频道。