Этюды на ASP.NET. Пример сайта на СУБД PostgreSQL
В этом топике я покажу step-by-step как создать простейший сайт на PostgreSQL и опубликую общую рыбку сайта со всем необходимым окружением. Эта страничка является продолжением более общей странички: Используем PostgreSQL вместо MS SQL в проектах на .NET и ASP.NET. Эта страничка также является примером технологии MONO - Low cost and platform independent ASP.NET - be free with MONO, позволяющей работать ASP.NET-сайтам на Linux.
При работе с PostgreSQL в ASP.NET есть пару ньансов, в частности я стремлюсь по возможность сохранять коннект в пределах сессии (чего нет смысла делать при работе с MS SQL или MySQL). Для этого я открываю коннект при старте сессии. Global.asax у меня содержит вот такие строки:
1: <%@ Application Language="VB" %>
2:
3: <script runat="server">
4:
5: .....
6:
7: Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
8: Dim PG1 As New PG1.SQL_Postgres
9: Session("PG1") = PG1
10: End Sub
11:
12: Sub Session_End(ByVal sender As Object, ByVal e As EventArgs)
13: If Session("PG1") IsNot Nothing Then
14: CType(Session("PG1"), PG1.SQL_Postgres).Close()
15: End If
16: End Sub
17:
18: </script>
Надо конечно, добавить к сайту пару ссылок на библиотеки Npgsql.dll и Mono.Security.dll, которые при стандартной инсталяции NPGSQL находятся в Win по подобному адресу C:\Program Files\Npgsql2.0.8-bin-ms.net3.5sp1\Npgsql2.0.8-bin-ms.net3.5sp1\bin. Кроме того, я вообще ложу в GAC Npgsql.dll - тогда ссылку на провайдер доступа с PostgreSQL можно вообще не ставить на сайте.
Конфиг сайта на ASP.NET 3.5 после добавления ссылки на Npgsql и строки коннекта к базе будет выглядеть вот так (укажите свой пароль):
1: ....
2: <connectionStrings>
3: <add name="SQLServer_ConnectionStrings" connectionString="HOST=127.0.0.1;PORT=5432;PROTOCOL=3;DATABASE=Disk;USER ID=postgres;POOLING=True;CONNECTIONLIFETIME=1000;MINPOOLSIZE=100;MAXPOOLSIZE=1024;COMMANDTIMEOUT=20;INTEGRATED SECURITY=False;"
4: providerName="Npgsql"/>
5: </connectionStrings>
6:
7: <system.web>
8: <compilation debug="true" strict="false" explicit="true">
9: <assemblies>
10: <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
11: <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
12: <add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
13: <add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
14: <add assembly="Npgsql, Version=2.0.8.0, Culture=neutral, PublicKeyToken=5D8B90D52F46FDA7"/>
15: </assemblies>
16: </compilation>
17: .....
Для удобства c PostgreSQL я сделал крошечную обвязочку, закрывающую ридер (если я забыл его закрыть в основном коде) и считывающую строку коннекта из конфига (чтобы не вспоминать имени строки коннекта). Кроме того она восстанавливает коннект при потере связи с СУБД и также в эту обвязку я позже собирался дописать обработку ошибок коннекта. Я откомпилировал этот крошечный класс, уложил его в библиотечку PG1 и просто ставлю ссылки на этот класс на своих сайтах.
И вот собственно код странички. Я покажу хандлер, а страничка будет выглядеть ровно так же:
1: <%@ WebHandler Language="VB" Class="RemoteSQL" %>
2:
3: Imports System
4: Imports System.Web
5:
6: Public Class RemoteSQL : Implements IHttpHandler, IRequiresSessionState
7:
8:
9: Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
10: context.Response.ContentType = "text/plain"
11: PG_Safe_Connection(context)
12: Dim RDR1 As Npgsql.NpgsqlDataReader = PG1.PG.ExecRDR("Select * from ""МаркаАвто""")
13: While RDR1.Read
14: context.Response.Write(RDR1(3) & vbCrLf)
15: End While
16: RDR1.Close()
17: End Sub
18:
19:
20: Dim PG1 As PG1.SQL_Postgres
21: Sub PG_Safe_Connection(ByVal context As HttpContext)
22: If context.Current.Session("PG1") IsNot Nothing Then
23: PG1 = context.Current.Session("PG1")
24: Else
25: PG1 = New PG1.SQL_Postgres
26: context.Current.Session("PG1") = PG1
27: End If
28: PG1.CheckConnect()
29: End Sub
30:
31: Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
32: Get
33: Return False
34: End Get
35: End Property
36:
37: End Class
Как видите, хандлер показывает то же, что и PgAdmin:
Для полноценной ASPX-странички можно даже вот этот хвостик (строки 20-29) утопить в базовую страничку. Проще всего это сделать поместив в app_code базовый класс всех страничек сайта (который несомненно в вашем проекте позднее разростется и будет не только коннект к базе поддерживать, но и аутентификацию юзеров проверять и многое другое):
1: Imports Microsoft.VisualBasic
2:
3: Public Class BasePage
4: Inherits System.Web.UI.Page
5:
6: Public Sub New()
7: MyBase.New()
8: End Sub
9:
10: Public PG1 As PG1.SQL_Postgres
11: Public Sub PG_Safe_Connection()
12: If HttpContext.Current.Session("PG1") IsNot Nothing Then
13: PG1 = HttpContext.Current.Session("PG1")
14: Else
15: PG1 = New PG1.SQL_Postgres
16: HttpContext.Current.Session("PG1") = PG1
17: End If
18: PG1.CheckConnect()
19: End Sub
20: End Class
Теперь вот такая простейшая страничка для генерации открытых/закрытых RSA-ключей будет выглядеть совсем просто:
1: <%@ Page Language="VB" MasterPageFile="~/M1.master" AutoEventWireup="false" CodeFile="RSA1.aspx.vb" Inherits="RSA" %>
2:
3: <%@ Register Assembly="AspCommon2009" Namespace="vbnet2009" TagPrefix="cc1" %>
4: <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
5: <div style="text-align: left">
6: <cc1:Navigator ID="Navigator1" runat="server" />
7: <h4>Создать новые RSA-ключи для ассимметричного шифрования?</h4>
8: <asp:Button ID="Button1" runat="server" Text="Yes" Width="50px" />
9: <asp:Button ID="Button2" runat="server" Text="No" Width="50px" />
10: <br />
11: <asp:Label ID="lErr1" runat="server" ForeColor="Red" Text=""></asp:Label>
12: </div>
13: </asp:Content>
1: Partial Class RSA
2: Inherits BasePage
3:
4: Protected Sub RSA_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
5: PG_Safe_Connection()
6: End Sub
7:
8: Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click
9: Response.Redirect("Default.aspx")
10: End Sub
11:
12: Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
13: Try
14: Dim RSA As New System.Security.Cryptography.RSACryptoServiceProvider()
15: Dim PublicKey As String = RSA.ToXmlString(False)
16: Dim PrivateKey As String = RSA.ToXmlString(True)
17: PG1.PG.ExecScalar("INSERT INTO ""RSA_KEY"" (""PublicKey"",""PrivateKey"") VALUES ('" & PublicKey & "' , '" & PrivateKey & "');")
18: Response.Redirect("Default.aspx")
19: Catch ex As Exception
20: lErr1.Text = ex.Message
21: End Try
22: End Sub
23:
24: End Class
Как вы видите, эта форма всего-навсего создает новые асимметричные ключи и укладывает их в такую табличку:
1: CREATE TABLE "RSA_KEY"
2: (
3: i serial NOT NULL,
4: "PublicKey" character varying,
5: "PrivateKey" character varying,
6: CONSTRAINT "Rsa_primary_key" PRIMARY KEY (i)
7: )
8: WITH (
9: OIDS=FALSE
10: );
11: ALTER TABLE "RSA_KEY" OWNER TO postgres;
Достать из нее открытый RSA-ключ можно таким же хандлером буквально в две строчки:
1: 'вообще-то это лишь самый первый шаг длинного протокола
2: Dim PublicKey As String = GetPublicKey()
3: context.Response.ContentType = "text/Xml"
4: context.Response.Write(PublicKey)
1: 'Достать из базы сеекретный ключ RSA
2: Private Function GetPrivateKey(ByVal context As HttpContext) As String
3: Dim PrivateKey As String
4: Dim RDR1 As Npgsql.NpgsqlDataReader = PG1.PG.ExecRDR("select ""PrivateKey"" from ""RSA_KEY"" order by i desc limit 1")
5: If RDR1.Read Then
6: PrivateKey = RDR1("PrivateKey")
7: End If
8: RDR1.Close()
9: Return PrivateKey
10: End Function
11:
12: 'Достать из базы публичный ключ RSA
13: Private Function GetPublicKey() As String
14: Dim PublicKey As String
15: Dim RDR1 As Npgsql.NpgsqlDataReader = PG1.PG.ExecRDR("select ""PublicKey"" from ""RSA_KEY"" order by i desc limit 1")
16: If RDR1.Read Then
17: PublicKey = RDR1("PublicKey")
18: End If
19: RDR1.Close()
20: Return PublicKey
21: End Function
Обратите внимание, что я в этот раз (как и всегда) публикую не просто какие-то помои - плоды теоретических измышлений, а небольшие, но ключевые фрагменты реального живого кода, используемые как для моих терминальных сетей на PostgreSQL, так и для работы моего хостинга //www.vb-net.com/. Опубликованный фрагмент находится по адресу http://activator.vb-net.com/Activate.ashx. Этот фрагмент кода также используется во многих моих коммерческих продуктах с закрытым исходным кодом, например WebActivator - клиент/сервер защиты от копирования для платных программ.
|