Część 6. Członkostwo platformy ASP.NET

Autor : Joe Stagner

Uwaga

Od czasu napisania tego artykułu dostawcy członkostwa ASP.NET zostali zastąpioni przez ASP.NET Identity. Zdecydowanie zalecamy aktualizowanie aplikacji w celu korzystania z platformy ASP.NET Identity , a nie dostawców członkostwa w momencie pisania tego artykułu. ASP.NET Identity ma wiele zalet w porównaniu z systemem członkostwa ASP.NET, w tym :

  • Lepsza wydajność
  • Ulepszona rozszerzalność i możliwość testowania
  • Obsługa uwierzytelniania OAuth, OpenID Connect i uwierzytelniania dwuskładnikowego
  • Obsługa tożsamości opartej na oświadczeniach
  • Lepsze współdziałanie z platformą ASP.Net Core

Tailspin Spyworks pokazuje, jak niezwykle proste jest tworzenie zaawansowanych, skalowalnych aplikacji dla platformy .NET. Pokazuje, jak używać wspaniałych nowych funkcji w ASP.NET 4 do tworzenia sklepu internetowego, w tym zakupów, wyewidencjonowania i administracji.

W tej serii samouczków szczegółowo opisano wszystkie kroki wykonywane w celu utworzenia przykładowej aplikacji Tailspin Spyworks. Część 6 dodaje członkostwo ASP.NET.

Praca z członkostwem w ASP.NET

Zrzut ekranu pokazujący, gdzie wybrać konfigurację S P dot NET.

Kliknij pozycję Zabezpieczenia

Zrzut ekranu przedstawiający miejsce kliknięcia pozycji Zabezpieczenia.

Upewnij się, że używamy uwierzytelniania formularzy.

Zrzut ekranu przedstawiający sposób potwierdzania, że używasz uwierzytelniania formularzy.

Użyj linku "Utwórz użytkownika", aby utworzyć kilku użytkowników.

Zrzut ekranu pokazujący, gdzie kliknąć pozycję Utwórz użytkownika.

Po zakończeniu zapoznaj się z oknem Eksplorator rozwiązań i odśwież widok.

Zrzut ekranu pokazujący, gdzie odświeżyć widok.

Zwróć uwagę, że baza danych ASPNETDB. Utworzono funkcję MDF. Ten plik zawiera tabele do obsługi podstawowych usług ASP.NET, takich jak członkostwo.

Teraz możemy rozpocząć wdrażanie procesu wyewidencjonowania.

Zacznij od utworzenia strony CheckOut.aspx.

Strona CheckOut.aspx powinna być dostępna tylko dla użytkowników, którzy są zalogowani, dlatego ograniczymy dostęp do zalogowanych użytkowników i przekierowujemy użytkowników, którzy nie są zalogowani do strony LogIn.

W tym celu dodamy następujący kod do sekcji konfiguracji naszego pliku web.config.

<location path="Checkout.aspx">
    <system.web>
      <authorization>
        <deny users="?" />
      </authorization>
    </system.web>
  </location>

Szablon dla aplikacji ASP.NET Web Forms automatycznie dodał sekcję uwierzytelniania do pliku web.config i ustanowił domyślną stronę logowania.

<authentication mode="Forms">
      <forms loginUrl="~/Account/Login.aspx" timeout="2880" />
    </authentication>

Musimy zmodyfikować kod Login.aspx za plikiem, aby przeprowadzić migrację anonimowego koszyka zakupów po zalogowaniu się użytkownika. Zmień zdarzenie Page_Load w następujący sposób.

using System.Web.Security;

protected void Page_Load(object sender, EventArgs e)
{
  // If the user is not submitting their credentials
  // save refferer
  if (!Page.IsPostBack)
     {
     if (Page.Request.UrlReferrer != null)
        {
        Session["LoginReferrer"] = Page.Request.UrlReferrer.ToString();
        }
      }
           
  // User is logged in so log them out.
  if (User.Identity.IsAuthenticated)
     {
     FormsAuthentication.SignOut();
     Response.Redirect("~/");
     }
}

Następnie dodaj procedurę obsługi zdarzeń "LoggedIn", aby ustawić nazwę sesji na nowo zalogowanego użytkownika i zmienić identyfikator sesji tymczasowej w koszyku zakupów na użytkownika, wywołując metodę MigrateCart w naszej klasie MyShoppingCart. (Zaimplementowano w pliku cs)

protected void LoginUser_LoggedIn(object sender, EventArgs e)
{
  MyShoppingCart usersShoppingCart = new MyShoppingCart();
  String cartId = usersShoppingCart.GetShoppingCartId();
  usersShoppingCart.MigrateCart(cartId, LoginUser.UserName);
            
  if(Session["LoginReferrer"] != null)
    {
    Response.Redirect(Session["LoginReferrer"].ToString());
    }

  Session["UserName"] = LoginUser.UserName;
}

Zaimplementuj metodę MigrateCart() w następujący sposób.

//--------------------------------------------------------------------------------------+
public void MigrateCart(String oldCartId, String UserName)
{
  using (CommerceEntities db = new CommerceEntities())
    {
    try
      {
      var myShoppingCart = from cart in db.ShoppingCarts
                           where cart.CartID == oldCartId
                           select cart;

      foreach (ShoppingCart item in myShoppingCart)
        {
        item.CartID = UserName;                 
        }
      db.SaveChanges();
      Session[CartId] = UserName;
      }
    catch (Exception exp)
      {
      throw new Exception("ERROR: Unable to Migrate Shopping Cart - " +     
                           exp.Message.ToString(), exp);
      }
    }           
}

W pliku checkout.aspx użyjemy elementu EntityDataSource i gridView na naszej stronie wyewidencjonowania tak samo jak na naszej stronie koszyka zakupów.

<div id="CheckOutHeader" runat="server" class="ContentHead">
  Review and Submit Your Order
</div>
<span id="Message" runat="server"><br />     
   <asp:Label ID="LabelCartHeader" runat="server" 
              Text="Please check all the information below to be sure it&#39;s correct.">
   </asp:Label>
</span><br /> 
<asp:GridView ID="MyList" runat="server" AutoGenerateColumns="False" 
              DataKeyNames="ProductID,UnitCost,Quantity" 
              DataSourceID="EDS_Cart" 
              CellPadding="4" GridLines="Vertical" CssClass="CartListItem" 
              onrowdatabound="MyList_RowDataBound" ShowFooter="True">
  <AlternatingRowStyle CssClass="CartListItemAlt" />
  <Columns>
    <asp:BoundField DataField="ProductID" HeaderText="Product ID" ReadOnly="True" 
                    SortExpression="ProductID"  />
    <asp:BoundField DataField="ModelNumber" HeaderText="Model Number" 
                    SortExpression="ModelNumber" />
    <asp:BoundField DataField="ModelName" HeaderText="Model Name" 
                    SortExpression="ModelName" />
    <asp:BoundField DataField="UnitCost" HeaderText="Unit Cost" ReadOnly="True" 
                    SortExpression="UnitCost" DataFormatString="{0:c}" />
    <asp:BoundField DataField="Quantity" HeaderText="Quantity" ReadOnly="True" 
                    SortExpression="Quantity" />
    <asp:TemplateField> 
      <HeaderTemplate>Item Total</HeaderTemplate>
      <ItemTemplate>
        <%# (Convert.ToDouble(Eval("Quantity")) * Convert.ToDouble(Eval("UnitCost")))%>
      </ItemTemplate>
    </asp:TemplateField>
  </Columns>
  <FooterStyle CssClass="CartListFooter"/>
  <HeaderStyle  CssClass="CartListHead" />
</asp:GridView>   
    
<br />
<asp:imagebutton id="CheckoutBtn" runat="server" ImageURL="Styles/Images/submit.gif" 
                                  onclick="CheckoutBtn_Click">
</asp:imagebutton>
<asp:EntityDataSource ID="EDS_Cart" runat="server" 
                      ConnectionString="name=CommerceEntities" 
                      DefaultContainerName="CommerceEntities" 
                      EnableFlattening="False" 
                      EnableUpdate="True" 
                      EntitySetName="ViewCarts" 
                      AutoGenerateWhereClause="True" 
                      EntityTypeFilter="" 
                      Select="" Where="">
   <WhereParameters>
      <asp:SessionParameter Name="CartID" DefaultValue="0" 
                                          SessionField="TailSpinSpyWorks_CartID" />
   </WhereParameters>
</asp:EntityDataSource>

Należy pamiętać, że nasza kontrolka GridView określa procedurę obsługi zdarzeń "ondatabound" o nazwie MyList_RowDataBound, więc zaimplementujmy tę procedurę obsługi zdarzeń w następujący sposób.

decimal _CartTotal = 0;

//--------------------------------------------------------------------------------------+
protected void MyList_RowDataBound(object sender, GridViewRowEventArgs e)
{
  if (e.Row.RowType == DataControlRowType.DataRow)
     {
     TailspinSpyworks.Data_Access.ViewCart myCart = new Data_Access.ViewCart();
     myCart = (TailspinSpyworks.Data_Access.ViewCart)e.Row.DataItem;
     _CartTotal += myCart.UnitCost * myCart.Quantity;
     }
   else if (e.Row.RowType == DataControlRowType.Footer)
     {
     if (_CartTotal > 0)
        {
        CheckOutHeader.InnerText = "Review and Submit Your Order";
        LabelCartHeader.Text = "Please check all the information below to be sure
                                                                it&#39;s correct.";
        CheckoutBtn.Visible = true;
        e.Row.Cells[5].Text = "Total: " + _CartTotal.ToString("C");
        }
     }
}

Ta metoda utrzymuje całkowitą sumę koszyka zakupów, ponieważ każdy wiersz jest powiązany i aktualizuje dolny wiersz kontrolki GridView.

Na tym etapie zaimplementowaliśmy prezentację "przeglądu" zamówienia do złożenia.

Obsłużmy pusty scenariusz koszyka, dodając kilka wierszy kodu do naszego zdarzenia Page_Load:

protected void Page_Load(object sender, EventArgs e)
{
   CheckOutHeader.InnerText = "Your Shopping Cart is Empty";
   LabelCartHeader.Text = "";
   CheckoutBtn.Visible = false;
}

Gdy użytkownik kliknie przycisk "Prześlij", wykonamy następujący kod w procedurze obsługi zdarzeń przycisku Prześlij.

protected void CheckoutBtn_Click(object sender, ImageClickEventArgs e)
{
  MyShoppingCart usersShoppingCart = new MyShoppingCart();
  if (usersShoppingCart.SubmitOrder(User.Identity.Name) == true)
    {
    CheckOutHeader.InnerText = "Thank You - Your Order is Complete.";
    Message.Visible = false;
    CheckoutBtn.Visible = false;
    }
  else
    {
    CheckOutHeader.InnerText = "Order Submission Failed - Please try again. ";
    }
}

"mięso" procesu składania zamówienia ma zostać zaimplementowane w metodzie SubmitOrder() klasy MyShoppingCart.

Funkcja SubmitOrder będzie:

  • Weź wszystkie elementy wiersza w koszyku i użyj ich do utworzenia nowego rekordu zamówienia i skojarzonych rekordów OrderDetails.
  • Oblicz datę wysyłki.
  • Wyczyść koszyk.
//--------------------------------------------------------------------------------------+
public bool SubmitOrder(string UserName)
{
  using (CommerceEntities db = new CommerceEntities())
    {
    try
      {
      //------------------------------------------------------------------------+
      //  Add New Order Record                                                  |
      //------------------------------------------------------------------------+
      Order newOrder = new Order();
      newOrder.CustomerName = UserName;
      newOrder.OrderDate = DateTime.Now;
      newOrder.ShipDate = CalculateShipDate();
      db.Orders.AddObject(newOrder);
      db.SaveChanges();
         
      //------------------------------------------------------------------------+
      //  Create a new OderDetail Record for each item in the Shopping Cart     |
      //------------------------------------------------------------------------+
      String cartId = GetShoppingCartId();
      var myCart = (from c in db.ViewCarts where c.CartID == cartId select c);
      foreach (ViewCart item in myCart)
        {
        int i = 0;
        if (i < 1)
          {
          OrderDetail od = new OrderDetail();
          od.OrderID = newOrder.OrderID;
          od.ProductID = item.ProductID;
          od.Quantity = item.Quantity;
          od.UnitCost = item.UnitCost;
          db.OrderDetails.AddObject(od);
          i++;
          }

        var myItem = (from c in db.ShoppingCarts where c.CartID == item.CartID && 
                         c.ProductID == item.ProductID select c).FirstOrDefault();
        if (myItem != null)
          {
          db.DeleteObject(myItem);
          }
        }
      db.SaveChanges();                    
      }
    catch (Exception exp)
      {
      throw new Exception("ERROR: Unable to Submit Order - " + exp.Message.ToString(), 
                                                               exp);
      }
    } 
  return(true);
}

Na potrzeby tej przykładowej aplikacji obliczymy datę wysyłki, dodając dwa dni do bieżącej daty.

//--------------------------------------------------------------------------------------+
DateTime CalculateShipDate()
{
   DateTime shipDate = DateTime.Now.AddDays(2);
   return (shipDate);
}

Uruchomienie aplikacji pozwoli nam teraz przetestować proces zakupów od początku do końca.