32
Parte 8: Carrinho de compras com atualizações de Ajax Por Jon Galloway |21 De abril de 2011 A loja de música do MVC é um aplicativo tutorial que apresenta e explica passo a passo como usar o ASP.NET MVC e Visual Studio para desenvolvimento web. A loja de música do MVC é um leve exemplo de implementação de loja que vende álbuns de música on-line e implementa a administração do site básico, entrar usuário e funcionalidade do carrinho de compras. Esta série de tutoriais detalha todos os passos tomados para construir o aplicativo de exemplo de ASP.NET MVC Music Store. Parte 8 aborda o carrinho de compras com atualizações de Ajax. Nós vamos permitir que os usuários coloquem álbuns em seus carrinhos sem o registro, mas eles precisarão se registrar como convidados para concluir o check-out. O processo de compras e checkout será separado em dois controladores: um controlador ShoppingCart que permite anonimamente, adicionando itens a um carrinho e um controlador de Checkout que lida com o processo de checkout. Nós vamos começar com o carrinho de compras nesta seção e, em seguida, criar o processo de check-out na seção a seguir. Adding the Cart, Order, and OrderDetail model classes Our Shopping Cart and Checkout processes will make use of some new classes. Right-click the Models folder and add a Cart class (Cart.cs) with the following code. using System.ComponentModel.DataAnnotations; namespace MvcMusicStore.Models { public class Cart { [Key] public int RecordId { get; set; } public string CartId { get; set; } public int AlbumId { get; set; } public int Count { get; set; } public System.DateTime DateCreated { get; set; } public virtual Album Album { get; set; } } } This class is pretty similar to others we’ve used so far, with the exception of the [Key] attribute for the RecordId property. Our Cart items will have a string identifier named CartID to allow anonymous shopping, but the table includes an integer primary key named RecordId. By convention, Entity Framework Code-First expects that the primary key for a table named Cart will be either CartId or ID, but we can easily override that via annotations or code if we want. This is an example of how we can use the simple conventions in Entity Framework Code-First when they suit us, but we’re not constrained by them when they don’t. Next, add an Order class (Order.cs) with the following code.

Carrinho de Compras

Embed Size (px)

Citation preview

Page 1: Carrinho de Compras

Parte 8: Carrinho de compras com atualizações de AjaxPor Jon Galloway|21 De abril de 2011

A loja de música do MVC é um aplicativo tutorial que apresenta e explica passo a passo como usar o ASP.NET MVC e Visual Studio para desenvolvimento web.

A loja de música do MVC é um leve exemplo de implementação de loja que vende álbuns de música on-line e implementa a administração do site básico, entrar usuário e funcionalidade do carrinho de compras.

Esta série de tutoriais detalha todos os passos tomados para construir o aplicativo de exemplo de ASP.NET MVC Music Store. Parte 8 aborda o carrinho de compras com atualizações de Ajax.

Nós vamos permitir que os usuários coloquem álbuns em seus carrinhos sem o registro, mas eles precisarão se registrar como convidados para concluir o check-out. O processo de compras e checkout será separado em dois controladores: um controlador ShoppingCart que permite anonimamente, adicionando itens a um carrinho e um controlador de Checkout que lida com o processo de checkout. Nós vamos começar com o carrinho de compras nesta seção e, em seguida, criar o processo de check-out na seção a seguir.

Adding the Cart, Order, and OrderDetail model classesOur Shopping Cart and Checkout processes will make use of some new classes. Right-click the Models folder and add a Cart class (Cart.cs) with the following code.

using System.ComponentModel.DataAnnotations; namespace MvcMusicStore.Models{ public class Cart { [Key] public int RecordId { get; set; } public string CartId { get; set; } public int AlbumId { get; set; } public int Count { get; set; } public System.DateTime DateCreated { get; set; } public virtual Album Album { get; set; } }}

This class is pretty similar to others we’ve used so far, with the exception of the [Key] attribute for the RecordId property. Our Cart items will have a string identifier named CartID to allow anonymous shopping, but the table includes an integer primary key named RecordId. By convention, Entity Framework Code-First expects that the primary key for a table named Cart will be either CartId or ID, but we can easily override that via annotations or code if we want. This is an example of how we can use the simple conventions in Entity Framework Code-First when they suit us, but we’re not constrained by them when they don’t.

Next, add an Order class (Order.cs) with the following code.

using System.Collections.Generic; namespace MvcMusicStore.Models

Page 2: Carrinho de Compras

{ public partial class Order { public int OrderId { get; set; } public string Username { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } public string City { get; set; } public string State { get; set; } public string PostalCode { get; set; } public string Country { get; set; } public string Phone { get; set; } public string Email { get; set; } public decimal Total { get; set; } public System.DateTime OrderDate { get; set; } public List<OrderDetail> OrderDetails { get; set; } }}

This class tracks summary and delivery information for an order. It won’t compile yet, because it has an OrderDetails navigation property which depends on a class we haven’t created yet. Let’s fix that now by adding a class named OrderDetail.cs, adding the following code.

namespace MvcMusicStore.Models{ public class OrderDetail { public int OrderDetailId { get; set; } public int OrderId { get; set; } public int AlbumId { get; set; } public int Quantity { get; set; } public decimal UnitPrice { get; set; } public virtual Album Album { get; set; } public virtual Order Order { get; set; } }}

We’ll make one last update to our MusicStoreEntities class to include DbSets which expose those new Model classes, also including a DbSet<Artist>. The updated MusicStoreEntities class appears as below.

using System.Data.Entity; namespace MvcMusicStore.Models{ public class MusicStoreEntities : DbContext { public DbSet<Album> Albums { get; set; } public DbSet<Genre> Genres { get; set; } public DbSet<Artist> Artists {get; set; } public DbSet<Cart> Carts { get; set; } public DbSet<Order> Orders{ get; set; } public DbSet<OrderDetail>OrderDetails { get; set; }

Page 3: Carrinho de Compras

}}

Gerenciando a lógica de negócios do carrinho de comprasEm seguida, criaremos a classe ShoppingCart na pasta modelos. O modelo ShoppingCart lida com acesso a dados para a tabela de carrinho. Além disso, ele vai lidar com a lógica de negócios para adicionar e remover itens do carrinho de compras.Desde que nós não queremos exigir que os usuários se inscrever para uma conta apenas adicionar itens ao seu carrinho de compras, vamos atribuir usuários um identificador exclusivo temporário (usando um GUID, ou um identificador globalmente exclusivo) quando eles acessam o carrinho de compras. Nós vai armazenar este ID usando a classe de sessão do ASP.NET.Nota: A sessão do ASP.NET é um local conveniente para armazenar informações específicas do usuário que irão expirar depois de saírem do local. Enquanto o uso indevido de estado da sessão pode ter implicações de desempenho em sites maiores, nosso uso claro vai funcionar bem para fins de demonstração.A classe ShoppingCart expõe os seguintes métodos:

AddToCart leva um álbum como um parâmetro e o adiciona ao carrinho do usuário. Desde que a tabela de carrinho controla a quantidade para cada álbum, inclui a lógica para criar uma nova linha, se necessário, ou apenas incrementar a quantidade, se o usuário já encomendou uma cópia do álbum.RemoveFromCart leva um ID do álbum e a remove do carrinho do usuário. Se o usuário tivesse uma cópia do álbum no seu carrinho, a linha é removida.EmptyCart remove todos os itens do carrinho de compras do usuário.GetCartItems recupera uma lista de CartItems para visualização ou processamento.GetCount recupera um número total de álbuns, um usuário tem em seu carrinho de compras.Total calcula o custo total de todos os itens no carrinho.CreateOrder converte o carrinho de compras para uma ordem durante a fase de checkout.GetCart é um método estático que permite que nossos controladores obter um objeto de carrinho. Ele usa o método GetCartId para lidar com a leitura o CartId da sessão do usuário. O método GetCartId requer o HttpContextBase para que ele possa ler CartId do usuário da sessão do usuário.Aqui é a completa classe ShoppingCart:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcMusicStore.Models{ public partial class ShoppingCart { MusicStoreEntities storeDB = new MusicStoreEntities(); string ShoppingCartId { get; set; } public const string CartSessionKey = "CartId"; public static ShoppingCart GetCart(HttpContextBase context) { var cart = new ShoppingCart(); cart.ShoppingCartId = cart.GetCartId(context); return cart; } // Helper method to simplify shopping cart calls public static ShoppingCart GetCart(Controller controller) {

Page 4: Carrinho de Compras

return GetCart(controller.HttpContext); } public void AddToCart(Album album) { // Get the matching cart and album instances var cartItem = storeDB.Carts.SingleOrDefault( c => c.CartId == ShoppingCartId && c.AlbumId == album.AlbumId); if (cartItem == null) { // Create a new cart item if no cart item exists cartItem = new Cart { AlbumId = album.AlbumId, CartId = ShoppingCartId, Count = 1, DateCreated = DateTime.Now }; storeDB.Carts.Add(cartItem); } else { // If the item does exist in the cart, // then add one to the quantity cartItem.Count++; } // Save changes storeDB.SaveChanges(); } public int RemoveFromCart(int id) { // Get the cart var cartItem = storeDB.Carts.Single( cart => cart.CartId == ShoppingCartId && cart.RecordId == id); int itemCount = 0; if (cartItem != null) { if (cartItem.Count > 1) { cartItem.Count--; itemCount = cartItem.Count; } else { storeDB.Carts.Remove(cartItem); } // Save changes storeDB.SaveChanges(); } return itemCount; }

Page 5: Carrinho de Compras

public void EmptyCart() { var cartItems = storeDB.Carts.Where( cart => cart.CartId == ShoppingCartId); foreach (var cartItem in cartItems) { storeDB.Carts.Remove(cartItem); } // Save changes storeDB.SaveChanges(); } public List<Cart> GetCartItems() { return storeDB.Carts.Where( cart => cart.CartId == ShoppingCartId).ToList(); } public int GetCount() { // Get the count of each item in the cart and sum them up int? count = (from cartItems in storeDB.Carts where cartItems.CartId == ShoppingCartId select (int?)cartItems.Count).Sum(); // Return 0 if all entries are null return count ?? 0; } public decimal GetTotal() { // Multiply album price by count of that album to get // the current price for each of those albums in the cart // sum all album price totals to get the cart total decimal? total = (from cartItems in storeDB.Carts where cartItems.CartId == ShoppingCartId select (int?)cartItems.Count * cartItems.Album.Price).Sum();

return total ?? decimal.Zero; } public int CreateOrder(Order order) { decimal orderTotal = 0; var cartItems = GetCartItems(); // Iterate over the items in the cart, // adding the order details for each foreach (var item in cartItems) { var orderDetail = new OrderDetail { AlbumId = item.AlbumId, OrderId = order.OrderId, UnitPrice = item.Album.Price, Quantity = item.Count }; // Set the order total of the shopping cart

Page 6: Carrinho de Compras

orderTotal += (item.Count * item.Album.Price); storeDB.OrderDetails.Add(orderDetail); } // Set the order's total to the orderTotal count order.Total = orderTotal; // Save the order storeDB.SaveChanges(); // Empty the shopping cart EmptyCart(); // Return the OrderId as the confirmation number return order.OrderId; } // We're using HttpContextBase to allow access to cookies. public string GetCartId(HttpContextBase context) { if (context.Session[CartSessionKey] == null) { if (!string.IsNullOrWhiteSpace(context.User.Identity.Name)) { context.Session[CartSessionKey] = context.User.Identity.Name; } else { // Generate a new random GUID using System.Guid class Guid tempCartId = Guid.NewGuid(); // Send tempCartId back to client as a cookie context.Session[CartSessionKey] = tempCartId.ToString(); } } return context.Session[CartSessionKey].ToString(); } // When a user has logged in, migrate their shopping cart to // be associated with their username public void MigrateCart(string userName) { var shoppingCart = storeDB.Carts.Where( c => c.CartId == ShoppingCartId); foreach (Cart item in shoppingCart) { item.CartId = userName; } storeDB.SaveChanges(); } }}

ViewModelsNosso controlador de carrinho de compras será necessário comunicar algumas informações complexas para seus pontos de vista que não mapeiam corretamente para nossos objetos de modelo. Não queremos modificar nossos modelos para atender nossos pontos de vista; Classes de modelo devem

Page 7: Carrinho de Compras

representar nosso domínio, não a interface do usuário. Uma solução seria passar a informação para nossos pontos de vista usando a classe ViewBag, como fizemos com as informações de lista suspensa de gerente da loja, mas passar um monte de informações via ViewBag fica difícil de gerenciar.Uma solução para isso é usar o padrão ViewModel . Ao usar esse padrão, que podemos criar classes de rigidez que são otimizados para nossos cenários específicos da vista, e que expõem propriedades para o valores/conteúdo dinâmico necessário pelos nossos modelos de exibição. Nossas classes de controlador podem então preencher e passar essas classes de exibição otimizada para nosso modelo de exibição para usar. Isso permite que a segurança de tipos, verificação de tempo de compilação e editor IntelliSense dentro de modelos de exibição.Vamos criar dois modelos de exibição para uso no nosso controlador de carrinho de compras: o ShoppingCartViewModel realizará o conteúdo do carrinho de compras do usuário, e o ShoppingCartRemoveViewModel será usado para exibir informações de confirmação quando um usuário remove algo do seu carrinho.

Vamos criar uma nova pasta ViewModels na raiz de nosso projeto para manter as coisas organizadas. Botão direito do mouse o projeto, selecione Adicionar / nova pasta.

Nomeie a pasta ViewModels.

Page 8: Carrinho de Compras

Em seguida, adicione a classe ShoppingCartViewModel na pasta ViewModels. Tem duas propriedades: uma lista de itens do carrinho e um valor decimal para segurar o preço total para todos os itens no carrinho.

using System.Collections.Generic; using MvcMusicStore.Models; namespace MvcMusicStore.ViewModels{ public class ShoppingCartViewModel { public List<Cart> CartItems { get; set; } public decimal CartTotal { get; set; } }}

Agora adicione o ShoppingCartRemoveViewModel para a pasta ViewModels, com as seguintes quatro propriedades.

namespace MvcMusicStore.ViewModels{ public class ShoppingCartRemoveViewModel { public string Message { get; set; } public decimal CartTotal { get; set; } public int CartCount { get; set; } public int ItemCount { get; set; } public int DeleteId { get; set; } }}

Controlador de carrinho de comprasO controlador de carrinho de compras possui três finalidades principais: Adicionando itens a um carrinho e remover itens do carrinho de visualização de itens no carrinho. Fará uso das três classes acabamos de criar: ShoppingCartViewModel, ShoppingCartRemoveViewModel e ShoppingCart. Como no StoreController e StoreManagerController, vamos adicionar um campo para armazenar uma instância de MusicStoreEntities.Adicione um novo controlador de carrinho de compras para o projeto usando o modelo de controlador vazio.

Page 9: Carrinho de Compras

Aqui é o controlador ShoppingCart completa. As ações do índice e adicionar o controlador devem parecer muito familiares. As ações do controlador remover e CartSummary lidar com dois casos especiais, que discutiremos na seção a seguir.

using System.Linq; using System.Web.Mvc; using MvcMusicStore.Models; using MvcMusicStore.ViewModels; namespace MvcMusicStore.Controllers{ public class ShoppingCartController : Controller { MusicStoreEntities storeDB = new MusicStoreEntities(); // // GET: /ShoppingCart/ public ActionResult Index() { var cart = ShoppingCart.GetCart(this.HttpContext); // Set up our ViewModel var viewModel = new ShoppingCartViewModel { CartItems = cart.GetCartItems(), CartTotal = cart.GetTotal() }; // Return the view return View(viewModel); } // // GET: /Store/AddToCart/5 public ActionResult AddToCart(int id) { // Retrieve the album from the database

Page 10: Carrinho de Compras

var addedAlbum = storeDB.Albums .Single(album => album.AlbumId == id); // Add it to the shopping cart var cart = ShoppingCart.GetCart(this.HttpContext); cart.AddToCart(addedAlbum); // Go back to the main store page for more shopping return RedirectToAction("Index"); } // // AJAX: /ShoppingCart/RemoveFromCart/5 [HttpPost] public ActionResult RemoveFromCart(int id) { // Remove the item from the cart var cart = ShoppingCart.GetCart(this.HttpContext); // Get the name of the album to display confirmation string albumName = storeDB.Carts .Single(item => item.RecordId == id).Album.Title; // Remove from cart int itemCount = cart.RemoveFromCart(id); // Display the confirmation message var results = new ShoppingCartRemoveViewModel { Message = Server.HtmlEncode(albumName) + " has been removed from your shopping cart.", CartTotal = cart.GetTotal(), CartCount = cart.GetCount(), ItemCount = itemCount, DeleteId = id }; return Json(results); } // // GET: /ShoppingCart/CartSummary [ChildActionOnly] public ActionResult CartSummary() { var cart = ShoppingCart.GetCart(this.HttpContext); ViewData["CartCount"] = cart.GetCount(); return PartialView("CartSummary"); } }}

Atualizações de AJAX com jQueryEm seguida vamos criar uma página de índice de carrinho de compras que é fortemente tipada para o ShoppingCartViewModel e usa o modelo de exibição de lista usando o mesmo método de antes.

Page 11: Carrinho de Compras

No entanto, em vez de usar um HTML. ActionLink para remover itens do carrinho, vamos usar jQuery para "conectar" o evento click para todos os links neste modo de exibição que tem a classe HTML RemoveLink. Ao invés de enviar o formulário, este manipulador de eventos click fará apenas um retorno de chamada AJAX para nossa ação de controlador de RemoveFromCart. O RemoveFromCart retorna um resultado JSON serializado, que nosso callback jQuery, então, analisa e executa quatro atualizações rápidas para a página usando jQuery: 1. Remove o álbum excluído da lista

2. Atualiza a contagem de carrinho no cabeçalho

3. Exibe uma mensagem de atualização para o usuário

4. Atualiza o preço total do carrinho

Desde que o cenário de remover está sendo manipulado por um retorno de chamada Ajax dentro da visão de índice, não precisamos uma visão adicional para RemoveFromCart ação. Aqui está o código completo para o modo de exibição de /ShoppingCart/Index:

@model MvcMusicStore.ViewModels.ShoppingCartViewModel@{ ViewBag.Title = "Shopping Cart";}<script src="/Scripts/jquery-1.4.4.min.js"type="text/javascript"></script><script type="text/javascript"> $(function () { // Document.ready -> link up remove event handler $(".RemoveLink").click(function () { // Get the id from the link var recordToDelete = $(this).attr("data-id");

Page 12: Carrinho de Compras

if (recordToDelete != '') { // Perform the ajax post $.post("/ShoppingCart/RemoveFromCart", {"id": recordToDelete }, function (data) { // Successful requests get here // Update the page elements if (data.ItemCount == 0) { $('#row-' + data.DeleteId).fadeOut('slow'); } else { $('#item-count-' + data.DeleteId).text(data.ItemCount); } $('#cart-total').text(data.CartTotal); $('#update-message').text(data.Message); $('#cart-status').text('Cart (' + data.CartCount + ')'); }); } }); });</script><h3> <em>Review</em> your cart: </h3><p class="button"> @Html.ActionLink("Checkout>>", "AddressAndPayment", "Checkout")</p><div id="update-message"></div><table> <tr> <th> Album Name </th> <th> Price (each) </th> <th> Quantity </th> <th></th> </tr> @foreach (var item inModel.CartItems) { <tr id="[email protected]"> <td> @Html.ActionLink(item.Album.Title,"Details", "Store", new { id = item.AlbumId }, null) </td> <td> @item.Album.Price </td> <td id="[email protected]"> @item.Count </td>

Page 13: Carrinho de Compras

<td> <a href="#" class="RemoveLink"data-id="@item.RecordId">Removefrom cart</a> </td> </tr> } <tr> <td> Total </td> <td> </td> <td> </td> <td id="cart-total"> @Model.CartTotal </td> </tr></table>

Para testar isso, precisamos ser capaz de adicionar itens ao nosso carrinho de compras. Vamos atualizá-nossaLoja detalhes vista para incluir um botão "Adicionar ao carrinho". Enquanto estamos no assunto, podemos incluir algumas informações adicionais álbum que nós adicionamos desde última atualizamos esta vista: gênero, artista, preço e arte do álbum. O loja detalhes exibir código atualizado aparece como mostrado abaixo.

@model MvcMusicStore.Models.Album@{ ViewBag.Title = "Album - " + Model.Title; }<h2>@Model.Title</h2><p> <img alt="@Model.Title"src="@Model.AlbumArtUrl" /></p><div id="album-details"> <p> <em>Genre:</em> @Model.Genre.Name </p> <p> <em>Artist:</em> @Model.Artist.Name </p> <p> <em>Price:</em> @String.Format("{0:F}",Model.Price) </p> <p class="button"> @Html.ActionLink("Add tocart", "AddToCart", "ShoppingCart", new { id = Model.AlbumId }, "") </p></div>

Page 14: Carrinho de Compras

Agora podemos clicar através da loja e testar, adicionando e removendo álbuns de e para o nosso carrinho de compras. Execute o aplicativo e navegue até o índice de loja.

Em seguida, clique em um gênero para exibir uma lista de álbuns.

Clique em um título de álbum agora mostra nossa exibição de detalhes de álbum atualizado, incluindo o botão "Adicionar ao carrinho".

Page 15: Carrinho de Compras

Clique no botão "Adicionar ao carrinho" mostra a nossa visão de compras carrinho de índice com a lista de Resumo de compras carrinho.

Page 16: Carrinho de Compras

Depois de carregar o seu carrinho de compras, você pode clicar em retirar do link carrinho para ver a actualização do Ajax ao seu carrinho de compras.

Page 17: Carrinho de Compras

Nós construímos para fora de um trabalho de carrinho que permite que os utilizadores não registados para adicionar itens ao seu carrinho de compras. Na seção seguinte, nós vai permitir que registrar e completar o processo de checkout.

Parte 9: Registo e check-outPor Jon Galloway|21 De abril de 2011

A loja de música do MVC é um aplicativo tutorial que apresenta e explica passo a passo como usar o ASP.NET MVC e Visual Studio para desenvolvimento web.

A loja de música do MVC é um leve exemplo de implementação de loja que vende álbuns de música on-line e implementa a administração do site básico, entrar usuário e funcionalidade do carrinho de compras.

Esta série de tutoriais detalha todos os passos tomados para construir o aplicativo de exemplo de ASP.NET MVC Music Store. Parte 9 aborda o registro e check-out.

Nesta seção, criaremos um CheckoutController que irá recolher informações de pagamento e endereço de compras. Necessitamos que os usuários se registrem em nosso site antes do check-out, então este controlador exigirá autorização.Os usuários navegarão para o processo de check-out de seu carrinho de compras clicando no botão "Checkout".

Se o usuário não está logado, vão ser solicitado a eles.

Page 18: Carrinho de Compras

Após um logon bem-sucedido, o usuário então é mostrado o endereço e pagamento a vista.

Page 19: Carrinho de Compras

Uma vez que eles têm preenchido o formulário e enviada a ordem, eles serão mostrados a tela de confirmação de pedido.

Page 20: Carrinho de Compras

Tentando ver uma ordem inexistentes ou uma ordem que não pertence a você mostrará a exibição de erro.

Migrando o carrinho de comprasEnquanto o processo de compra é anônimo, quando o usuário clica no botão Checkout, eles deverão registar-se e faça o login. Os usuários esperam que vamos manter suas informações de carrinho de compras entre visitas, então temos de associar as informações de carrinho de compras com um usuário quando eles concluem o registo ou login.Isto é realmente muito simples de fazer, como nossa classe ShoppingCart já tem um método que irá associar todos os itens no carrinho de atual com um nome de usuário. Só precisamos de chamar esse método quando um usuário concluir o registo ou login.

Page 21: Carrinho de Compras

Abra a classe AccountController que adicionamos quando nós estávamos preparando adesão e autorização.Adicionar um usando instrução referenciando MvcMusicStore.Models, em seguida, adicione o seguinte método de MigrateShoppingCart:

private void MigrateShoppingCart(string UserName) { // Associate shopping cart items with logged-in user var cart = ShoppingCart.GetCart(this.HttpContext); cart.MigrateCart(UserName); Session[ShoppingCart.CartSessionKey] = UserName; }

Em seguida, modifica a ação de post de LogOn para chamar MigrateShoppingCart depois que o usuário foi validado, como mostrado abaixo:

//// POST: /Account/LogOn[HttpPost] public ActionResult LogOn(LogOnModel model, string returnUrl) { if (ModelState.IsValid) { if (Membership.ValidateUser(model.UserName, model.Password)) { MigrateShoppingCart(model.UserName); FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe); if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\")) { return Redirect(returnUrl); } else { return RedirectToAction("Index", "Home"); } } else { ModelState.AddModelError("", "The user name or password provided is incorrect."); } } // If we got this far, something failed, redisplay form return View(model); }

Faça a mesma alteração ao registo post ação, imediatamente depois que a conta de usuário é criada com êxito:

//// POST: /Account/Register[HttpPost]

Page 22: Carrinho de Compras

public ActionResult Register(RegisterModel model) { if (ModelState.IsValid) { // Attempt to register the user MembershipCreateStatus createStatus; Membership.CreateUser(model.UserName, model.Password, model.Email, "question", "answer", true, null, out createStatus); if (createStatus == MembershipCreateStatus.Success) { MigrateShoppingCart(model.UserName); FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */); return RedirectToAction("Index", "Home"); } else { ModelState.AddModelError("", ErrorCodeToString(createStatus)); } } // If we got this far, something failed, redisplay form return View(model); }

É isso aí - agora um carrinho de compras anônimo será automaticamente transferido para uma conta de usuário após registo ou login.

Criando o CheckoutControllerBotão direito do mouse sobre a pasta de controladores e adicionar um novo controlador para o projeto chamado CheckoutController usando o modelo de controlador vazio.

Page 23: Carrinho de Compras

Primeiro, adicione o atributo de autorizar acima da declaração de classe do controlador para exigir que os usuários se registrem antes de fazer check-out:

namespace MvcMusicStore.Controllers{ [Authorize] public class CheckoutController : Controller

Nota: Isto é semelhante para a mudança que fizemos anteriormente para o StoreManagerController, mas nesse caso, o atributo de autorizar necessário que o usuário seja em uma função de administrador. No Controller, Checkout, está exigindo o usuário estar logado, mas não estão exigindo que sejam administradores. 

Por razões de simplificação, não tratamos com informações de pagamento neste tutorial. Em vez disso, estamos a permitir que os usuários para verific para fora usando um código promocional. Vamos guardar este código promocional usando uma constante chamada PromoCode.Como o StoreController, nós vai declarar um campo para manter uma instância da classe MusicStoreEntities, chamada storeDB. A fim de fazer uso da classe MusicStoreEntities, precisamos adicionar um usando instrução para o namespace MvcMusicStore.Models. A parte superior do nosso controlador de Checkout aparece abaixo.

using System; using System.Linq; using System.Web.Mvc; using MvcMusicStore.Models; namespace MvcMusicStore.Controllers{ [Authorize] public class CheckoutController : Controller { MusicStoreEntities storeDB = new MusicStoreEntities(); const string PromoCode = "FREE";

O CheckoutController terá as seguintes ações de controlador:

AddressAndPayment (método GET) irá exibir um formulário para permitir que o usuário insira suas informações.AddressAndPayment (método POST) irá validar a entrada e processar o pedido.Complete será mostrado após um usuário terminou com êxito o processo de checkout. Essa exibição incluirá o número de ordem do usuário, como confirmação.Primeiro, vamos renomear a ação de controlador de índice (que foi gerada quando criamos o controlador) para AddressAndPayment. Esta ação de controlador só exibe o formulário de check-out, por isso não exige qualquer informação de modelo.

//// GET: /Checkout/AddressAndPaymentpublic ActionResult AddressAndPayment(){ return View();}

Nosso método de AddressAndPayment POST seguirá o mesmo padrão usado na StoreManagerController: ele vai tentar aceitar o envio do formulário e completar o pedido e re-irá exibir o formulário, se ele falhar.

Depois de validar a entrada de formulário encontra nossos requisitos de validação de uma ordem, vamos verificar o valor do formulário PromoCode diretamente. Assumindo que tudo está correto, que

Page 24: Carrinho de Compras

vamos salvar as informações atualizadas com a ordem, diz o objeto ShoppingCart para concluir o processo de ordem e redirecionar para a ação completa.

//// POST: /Checkout/AddressAndPayment[HttpPost] public ActionResult AddressAndPayment(FormCollection values) { var order = new Order(); TryUpdateModel(order); try { if (string.Equals(values["PromoCode"], PromoCode, StringComparison.OrdinalIgnoreCase) == false) { return View(order); } else { order.Username = User.Identity.Name; order.OrderDate = DateTime.Now; //Save Order storeDB.Orders.Add(order); storeDB.SaveChanges(); //Process the order var cart = ShoppingCart.GetCart(this.HttpContext); cart.CreateOrder(order); return RedirectToAction("Complete", new { id = order.OrderId }); } } catch { //Invalid - redisplay with errors return View(order); }}

Após a conclusão bem-sucedida do processo de check-out, usuários serão redirecionados para a ação de controlador completo. Esta acção irá executar uma verificação simples para validar que a ordem pertence efectivamente para o usuário que fez logon antes de mostrar o número de ordem, como uma confirmação.

//// GET: /Checkout/Completepublic ActionResult Complete(int id) { // Validate customer owns this order bool isValid = storeDB.Orders.Any( o => o.OrderId == id && o.Username == User.Identity.Name); if (isValid) { return View(id);

Page 25: Carrinho de Compras

} else { return View("Error"); }}

Nota: A exibição de erro foi criada automaticamente para nós na pasta /Views/Shared quando iniciamos o projeto.O código completo do CheckoutController é a seguinte:

using System; using System.Linq; using System.Web.Mvc; using MvcMusicStore.Models; namespace MvcMusicStore.Controllers{ [Authorize] public class CheckoutController : Controller { MusicStoreEntities storeDB = new MusicStoreEntities(); const string PromoCode = "FREE"; // // GET: /Checkout/AddressAndPayment public ActionResult AddressAndPayment() { return View(); } // // POST: /Checkout/AddressAndPayment [HttpPost] public ActionResult AddressAndPayment(FormCollection values) { var order = new Order(); TryUpdateModel(order); try { if (string.Equals(values["PromoCode"], PromoCode, StringComparison.OrdinalIgnoreCase) == false) { return View(order); } else { order.Username = User.Identity.Name; order.OrderDate = DateTime.Now; //Save Order storeDB.Orders.Add(order); storeDB.SaveChanges(); //Process the order var cart = ShoppingCart.GetCart(this.HttpContext); cart.CreateOrder(order);

Page 26: Carrinho de Compras

return RedirectToAction("Complete", new { id = order.OrderId }); } } catch { //Invalid - redisplay with errors return View(order); } } // // GET: /Checkout/Complete public ActionResult Complete(int id) { // Validate customer owns this order bool isValid = storeDB.Orders.Any( o => o.OrderId == id && o.Username == User.Identity.Name); if (isValid) { return View(id); } else { return View("Error"); } } }}

Adicionando a vista AddressAndPaymentAgora, vamos criar o modo de exibição de AddressAndPayment. Botão direito do mouse em um do as AddressAndPayment ações do controlador e adicionar uma exibição chamada AddressAndPayment que é fortemente tipado como uma ordem e usa o modelo de edição, como mostrado abaixo.

Page 27: Carrinho de Compras

Essa visão fará uso de duas das técnicas nós olhamos ao construir a visão de StoreManagerEdit:

Nós usaremos o Html.EditorForModel() para exibir os campos de formulário para o modelo de ordem

Nós irá alavancar as regras de validação usando uma classe de ordem com atributos de validação

Vamos começar, atualizando o código de formulário para usar Html.EditorForModel(), seguido por uma caixa de texto adicional para o código promocional. O código completo para o modo de exibição de AddressAndPayment é mostrado abaixo.

@model MvcMusicStore.Models.Order@{ ViewBag.Title = "Address And Payment";}<script src="@Url.Content("~/Scripts/jquery.validate.min.js")"type="text/javascript"></script><script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"type="text/javascript"></script>@using (Html.BeginForm()) { <h2>Address And Payment</h2> <fieldset> <legend>Shipping Information</legend> @Html.EditorForModel() </fieldset> <fieldset> <legend>Payment</legend> <p>We're running a promotion: all music is free

Page 28: Carrinho de Compras

with the promo code: "FREE"</p> <div class="editor-label"> @Html.Label("Promo Code") </div> <div class="editor-field"> @Html.TextBox("PromoCode") </div> </fieldset> <input type="submit" value="Submit Order" />}

Definição de regras de validação para a ordemAgora que nossa visão está configurado, definiremos as regras de validação para o nosso modelo de ordem como fizemos anteriormente para o modelo de álbum. Botão direito do mouse na pasta modelos e adicionar uma classe chamada ordem. Além dos atributos de validação que usamos anteriormente para o álbum, vamos também estar usando uma expressão Regular para validar o endereço de email do usuário.

using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Web.Mvc; namespace MvcMusicStore.Models{ [Bind(Exclude = "OrderId")] public partial class Order { [ScaffoldColumn(false)] public int OrderId { get; set; } [ScaffoldColumn(false)] public System.DateTime OrderDate { get; set; } [ScaffoldColumn(false)] public string Username { get; set; } [Required(ErrorMessage = "First Name is required")] [DisplayName("First Name")] [StringLength(160)] public string FirstName { get; set; } [Required(ErrorMessage = "Last Name is required")] [DisplayName("Last Name")] [StringLength(160)] public string LastName { get; set; } [Required(ErrorMessage = "Address is required")] [StringLength(70)] public string Address { get; set; } [Required(ErrorMessage = "City is required")] [StringLength(40)] public string City { get; set; } [Required(ErrorMessage = "State is required")] [StringLength(40)] public string State { get; set; } [Required(ErrorMessage = "Postal Code is required")] [DisplayName("Postal Code")] [StringLength(10)]

Page 29: Carrinho de Compras

public string PostalCode { get; set; } [Required(ErrorMessage = "Country is required")] [StringLength(40)] public string Country { get; set; } [Required(ErrorMessage = "Phone is required")] [StringLength(24)] public string Phone { get; set; } [Required(ErrorMessage = "Email Address is required")] [DisplayName("Email Address")] [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}", ErrorMessage = "Email is is not valid.")] [DataType(DataType.EmailAddress)] public string Email { get; set; } [ScaffoldColumn(false)] public decimal Total { get; set; } public List<OrderDetail> OrderDetails { get; set; } }}

A tentativa de enviar o formulário com falta ou informações inválidas agora irão mostrar a mensagem de erro usando a validação do lado do cliente.

Page 30: Carrinho de Compras

Ok, fizemos a maior parte do trabalho duro para o processo de check-out; Só temos algumas probabilidades e extremidades para terminar. Precisamos adicionar dois modos de exibição simples, e nós precisamos cuidar a entrega das informações durante o processo de login carrinho.

Adicionando a exibição completa de CheckoutA visão completa de Checkout é bastante simples, como ele só precisa exibir a ID de ordem. Botão direito do mouse sobre a ação de controlador completo e adicionar uma exibição chamada Complete, que é fortemente tipado como um int.

Page 31: Carrinho de Compras

Agora vamos atualizar o código de exibição para exibir a ID de ordem, como mostrado abaixo.

@model int@{ ViewBag.Title = "Checkout Complete";}<h2>Checkout Complete</h2><p>Thanks for your order! Your order number is: @Model</p><p>How about shopping for some more music in our @Html.ActionLink("store","Index", "Home")</p>

Vista o erro de atualizaçãoO modelo padrão inclui uma vista erro na pasta compartilhada de exibições para que pode ser reutilizado em outro lugar no site. Esta visão de erro contém um erro muito simples e não usa nosso site, Layout, então vamos atualizá-lo.Pois esta é uma página de erro genérica, o conteúdo é muito simples. Nós incluiremos uma mensagem e um link para navegar para a página anterior no histórico se o usuário quer tentar re-sua ação.

@{ ViewBag.Title = "Error";} <h2>Error</h2> <p>We're sorry, we've hit an unexpected error.

Page 32: Carrinho de Compras

<a href="javascript:history.go(-1)">Click here</a> if you'd like to go back and try that again.</p>

Por favor, use as discussões no http://mvcmusicstore.codeplex.com para quaisquer perguntas ou comentários.