Compartilhar via


Desbloqueio e aprovação de contas de usuário (C#)

por Scott Mitchell

Observação

Desde que este artigo foi escrito, os provedores de associação de ASP.NET foram substituídos por ASP.NET Identity. É altamente recomendável atualizar aplicativos para usar a plataforma ASP.NET Identity em vez dos provedores de associação apresentados no momento em que este artigo foi escrito. ASP.NET Identity tem várias vantagens sobre o sistema de associação ASP.NET, incluindo :

  • Melhor desempenho
  • Extensibilidade e testabilidade aprimoradas
  • Suporte para OAuth, OpenID Connect e autenticação de dois fatores
  • Suporte à identidade baseada em declarações
  • Melhor interoperabilidade com ASP.Net Core

Este tutorial mostra como criar uma página da Web para que os administradores gerenciem os status bloqueados e aprovados dos usuários. Também veremos como aprovar novos usuários somente depois que eles verificarem seu endereço de email.

Introdução

Juntamente com um nome de usuário, senha e email, cada conta de usuário tem dois campos status que determinam se o usuário pode fazer logon no site: bloqueado e aprovado. Um usuário será bloqueado automaticamente se fornecer credenciais inválidas um número especificado de vezes em um número especificado de minutos (as configurações padrão bloqueiam um usuário após cinco tentativas de logon inválidas dentro de 10 minutos). O status aprovado é útil em cenários em que alguma ação deve ocorrer antes que um novo usuário possa fazer logon no site. Por exemplo, um usuário pode precisar primeiro verificar seu endereço de email ou ser aprovado por um administrador antes de poder fazer logon.

Como um usuário bloqueado ou não aprovado não pode fazer logon, é natural se perguntar como esses status podem ser redefinidos. ASP.NET não inclui nenhuma funcionalidade interna ou controles da Web para gerenciar os status bloqueados e aprovados dos usuários, em parte porque essas decisões precisam ser tratadas site a site. Alguns sites podem aprovar automaticamente todas as novas contas de usuário (o comportamento padrão). Outros fazem com que um administrador aprove novas contas ou não aprove os usuários até que eles visitem um link enviado para o endereço de email fornecido quando eles se inscreveram. Da mesma forma, alguns sites podem bloquear usuários até que um administrador redefina seus status, enquanto outros sites enviam um email para o usuário bloqueado com uma URL que podem visitar para desbloquear sua conta.

Este tutorial mostra como criar uma página da Web para que os administradores gerenciem os status bloqueados e aprovados dos usuários. Também veremos como aprovar novos usuários somente depois que eles verificarem seu endereço de email.

Etapa 1: Gerenciar os status bloqueados e aprovados dos usuários

No tutorial Criando uma interface para selecionar uma conta de usuário de muitos, construímos uma página que listava cada conta de usuário em um GridView paginado e filtrado. A grade lista o nome e o email de cada usuário, seus status aprovados e bloqueados, se eles estão online no momento e quaisquer comentários sobre o usuário. Para gerenciar os status aprovados e bloqueados dos usuários, poderíamos tornar essa grade editável. Para alterar a status aprovada de um usuário, o administrador primeiro localizaria a conta de usuário e editaria a linha gridView correspondente, verificando ou desmarcando a caixa de seleção aprovada. Como alternativa, poderíamos gerenciar os status aprovados e bloqueados por meio de uma página ASP.NET separada.

Para este tutorial, vamos usar duas páginas ASP.NET: ManageUsers.aspx e UserInformation.aspx. A ideia aqui é que ManageUsers.aspx lista as contas de usuário no sistema, ao mesmo UserInformation.aspx tempo que permite que o administrador gerencie os status aprovados e bloqueados para um usuário específico. Nossa primeira ordem de negócios é aumentar o GridView em ManageUsers.aspx para incluir um HyperLinkField, que é renderizado como uma coluna de links. Queremos que cada link aponte para UserInformation.aspx?user=UserName, em que UserName é o nome do usuário a ser editado.

Observação

Se você baixou o código para o tutorial Recuperando e alterando senhas, talvez tenha notado que a ManageUsers.aspx página já contém um conjunto de links "Gerenciar" e a UserInformation.aspx página fornece uma interface para alterar a senha do usuário selecionado. Decidi não replicar essa funcionalidade no código associado a este tutorial porque funcionou contornando a API de Associação e operando diretamente com o banco de dados SQL Server para alterar a senha de um usuário. Este tutorial começa do zero com a UserInformation.aspx página.

Abra a ManageUsers.aspx página e adicione um HyperLinkField ao UserAccounts GridView. Defina a propriedade do Text HyperLinkField como "Gerenciar" e suas DataNavigateUrlFields propriedades e DataNavigateUrlFormatString como UserName e "UserInformation.aspx?user={0}", respectivamente. Essas configurações configuram o HyperLinkField de modo que todos os hiperlinks exibam o texto "Gerenciar", mas cada link passa o valor de UserName apropriado para a cadeia de caracteres de consulta.

Depois de adicionar o HyperLinkField ao GridView, reserve um momento para exibir a ManageUsers.aspx página por meio de um navegador. Como mostra a Figura 1, cada linha GridView agora inclui um link "Gerenciar". O link "Gerenciar" para Bruce aponta para UserInformation.aspx?user=Bruce, enquanto o link "Gerenciar" para Dave aponta para UserInformation.aspx?user=Dave.

O HyperLinkField adiciona um

Figura 1: o HyperLinkField adiciona um link "Gerenciar" para cada conta de usuário (clique para exibir a imagem em tamanho real)

Criaremos a interface do usuário e o código para a UserInformation.aspx página em um momento, mas primeiro vamos falar sobre como alterar programaticamente os status bloqueados e aprovados de um usuário. A MembershipUser classe tem IsLockedOutpropriedades eIsApproved . A propriedade IsLockedOut é somente leitura. Não há mecanismo para bloquear programaticamente um usuário; para desbloquear um usuário, use o MembershipUser método da UnlockUserclasse. A IsApproved propriedade é legível e gravável. Para salvar quaisquer alterações nessa propriedade, precisamos chamar o Membership método da UpdateUserclasse, passando o objeto modificadoMembershipUser.

Como a IsApproved propriedade é legível e gravável, um controle CheckBox é provavelmente o melhor elemento de interface do usuário para configurar essa propriedade. No entanto, uma CheckBox não funcionará para a IsLockedOut propriedade porque um administrador não pode bloquear um usuário, ela só pode desbloquear um usuário. Uma interface do usuário adequada para a IsLockedOut propriedade é um Botão que, quando clicado, desbloqueia a conta de usuário. Esse Botão só deverá ser habilitado se o usuário estiver bloqueado.

Criando aUserInformation.aspxpágina

Agora estamos prontos para implementar a interface do usuário no UserInformation.aspx. Abra esta página e adicione os seguintes controles da Web:

  • Um controle HyperLink que, quando clicado, retorna o administrador para a ManageUsers.aspx página.
  • Um controle Da Web de Rótulo para exibir o nome do usuário selecionado. Defina esse Rótulo ID como UserNameLabel e desmarque sua Text propriedade.
  • Um controle CheckBox chamado IsApproved. Defina sua AutoPostBack propriedade como true.
  • Um controle Rótulo para exibir a última data bloqueada do usuário. Nomeie este Rótulo LastLockedOutDateLabel e desmarque sua Text propriedade.
  • Um Botão para desbloquear o usuário. Nomeie este Botão UnlockUserButton e defina sua Text propriedade como "Desbloquear Usuário".
  • Um controle Rótulo para exibir mensagens status, como "A status aprovada pelo usuário foi atualizada". Nomeie esse controle StatusMessagecomo , desmarque sua Text propriedade e defina sua CssClass propriedade como Important. (O Important A classe CSS é definida no Styles.css arquivo de folha de estilos; ela exibe o texto correspondente em uma fonte grande e vermelha.)

Depois de adicionar esses controles, a exibição Design no Visual Studio deve ser semelhante à captura de tela na Figura 2.

Criar a interface do usuário para UserInformation.aspx

Figura 2: Criar a Interface do Usuário para UserInformation.aspx (Clique para exibir imagem em tamanho real)

Com a interface do usuário concluída, nossa próxima tarefa é definir o IsApproved CheckBox e outros controles com base nas informações do usuário selecionado. Crie um manipulador de eventos para o evento da Load página e adicione o seguinte código:

protected void Page_Load(object sender, EventArgs e)
{
     if (!Page.IsPostBack)
     {

          // If querystring value is missing, send the user to ManageUsers.aspx
          string userName = Request.QueryString["user"];
          if (string.IsNullOrEmpty(userName))
               Response.Redirect("ManageUsers.aspx");

          // Get information about this user
          MembershipUser usr = Membership.GetUser(userName);
          if (usr == null)
               Response.Redirect("ManageUsers.aspx");

          UserNameLabel.Text = usr.UserName;
          IsApproved.Checked = usr.IsApproved;
          if (usr.LastLockoutDate.Year < 2000)

               LastLockoutDateLabel.Text = string.Empty;
          else
               LastLockoutDateLabel.Text = usr.LastLockoutDate.ToShortDateString();

          UnlockUserButton.Enabled = usr.IsLockedOut;
     }
}

O código acima começa garantindo que essa seja a primeira visita à página e não um postback subsequente. Em seguida, ele lê o nome de usuário passado pelo user campo querystring e recupera informações sobre essa conta de usuário por meio do Membership.GetUser(username) método . Se nenhum nome de usuário tiver sido fornecido por meio da querystring ou se o usuário especificado não puder ser encontrado, o administrador será enviado de volta para a ManageUsers.aspx página.

O MembershipUser valor do UserName objeto é exibido no UserNameLabel e o IsApproved CheckBox é verificado com base no valor da IsApproved propriedade.

A MembershipUser propriedade do LastLockoutDate objeto retorna um DateTime valor que indica quando o usuário foi bloqueado pela última vez. Se o usuário nunca tiver sido bloqueado, o valor retornado dependerá do provedor de associação. Quando uma nova conta é criada, o define o SqlMembershipProvideraspnet_Membership campo da LastLockoutDate tabela como 1754-01-01 12:00:00 AM. O código acima exibe uma cadeia de caracteres vazia no LastLockoutDateLabel se a LastLockoutDate propriedade ocorrer antes do ano 2000; caso contrário, a parte de data da LastLockoutDate propriedade será exibida no Rótulo. A UnlockUserButton' propriedade do é Enabled definida como a status bloqueada do usuário, o que significa que esse Botão só será habilitado se o usuário estiver bloqueado.

Reserve um momento para testar a UserInformation.aspx página por meio de um navegador. É claro que você precisará começar e ManageUsers.aspx selecionar uma conta de usuário para gerenciar. Ao chegar em UserInformation.aspx, observe que a Caixa de Seleção IsApproved só será verificada se o usuário for aprovado. Se o usuário tiver sido bloqueado, a última data bloqueada será exibida. O botão Desbloquear Usuário só será habilitado se o usuário estiver bloqueado no momento. Verificar ou desmarcar a IsApproved Caixa de Seleção ou clicar no botão Desbloquear Usuário causa um postback, mas nenhuma modificação é feita na conta de usuário porque ainda não criamos manipuladores de eventos para esses eventos.

Retorne ao Visual Studio e crie manipuladores de eventos para o IsApproved evento checkbox CheckedChanged e o UnlockUser evento do Click Botão. CheckedChanged No manipulador de eventos, defina a propriedade do IsApproved usuário como a Checked propriedade da CheckBox e salve as alterações por meio de uma chamada para Membership.UpdateUser. No manipulador de Click eventos, basta chamar o MembershipUser método do UnlockUser objeto. Em ambos os manipuladores de eventos, exiba uma mensagem adequada no StatusMessage Rótulo.

protected void IsApproved_CheckedChanged(object sender, EventArgs e)
{
     // Toggle the user's approved status
     string userName = Request.QueryString["user"];
     MembershipUser usr = Membership.GetUser(userName);
     usr.IsApproved = IsApproved.Checked;
     Membership.UpdateUser(usr);
     StatusMessage.Text = "The user's approved status has been updated.";
}

protected void UnlockUserButton_Click(object sender, EventArgs e)
{
     // Unlock the user account
     string userName = Request.QueryString["user"];
     MembershipUser usr = Membership.GetUser(userName);

     usr.UnlockUser();
     UnlockUserButton.Enabled = false;
     StatusMessage.Text = "The user account has been unlocked.";
}

Testando aUserInformation.aspxpágina

Com esses manipuladores de eventos em vigor, reveja a página e cancele a aprovação de um usuário. Como mostra a Figura 3, você deve ver uma breve mensagem na página indicando que a propriedade do IsApproved usuário foi modificada com êxito.

Chris não foi aprovado.

Figura 3: Chris não foi aprovado (clique para exibir a imagem em tamanho real)

Em seguida, faça logoff e tente fazer logon como o usuário cuja conta acabou de ser aprovada. Como o usuário não foi aprovado, ele não pode fazer logon. Por padrão, o controle Logon exibirá a mesma mensagem se o usuário não puder fazer logon, independentemente do motivo. No entanto, no tutorial Validando credenciais do usuário no Repositório de Usuários associados, examinamos como aprimorar o controle de logon para exibir uma mensagem mais apropriada. Como mostra a Figura 4, Chris recebe uma mensagem explicando que não pode fazer logon porque sua conta ainda não foi aprovada.

Chris não pode fazer logon porque sua conta não foi aprovada

Figura 4: Chris não pode fazer logon porque sua conta não foi aprovada (clique para exibir a imagem em tamanho real)

Para testar a funcionalidade bloqueada, tente fazer logon como um usuário aprovado, mas use uma senha incorreta. Repita esse processo o número necessário de vezes até que a conta do usuário seja bloqueada. O controle De logon também foi atualizado para mostrar uma mensagem personalizada se estiver tentando fazer logon de uma conta bloqueada. Você sabe que uma conta foi bloqueada depois que você começa a ver a seguinte mensagem na página de logon: "Sua conta foi bloqueada devido a muitas tentativas de logon inválidas. Entre em contato com o administrador para que sua conta seja desbloqueada."

Retorne à ManageUsers.aspx página e clique no link Gerenciar para o usuário bloqueado. Como mostra a Figura 5, você deve ver um valor no LastLockedOutDateLabel botão Desbloquear Usuário deve ser habilitado. Clique no botão Desbloquear Usuário para desbloquear a conta de usuário. Depois de desbloquear o usuário, ele poderá fazer logon novamente.

Dave foi bloqueado fora do sistema

Figura 5: Dave foi bloqueado fora do sistema (clique para exibir a imagem em tamanho real)

Etapa 2: Especificando o status aprovado de novos usuários

A status aprovada é útil em cenários em que você deseja que alguma ação seja executada antes que um novo usuário possa fazer logon e acessar os recursos específicos do usuário do site. Por exemplo, você pode estar executando um site privado em que todas as páginas, exceto as páginas de logon e inscrição, são acessíveis somente para usuários autenticados. Mas o que acontece se um estranho chegar ao seu site, encontrar a página de inscrição e criar uma conta? Para evitar que isso aconteça, você pode mover a página de inscrição para uma Administration pasta e exigir que um administrador crie manualmente cada conta. Como alternativa, você pode permitir que qualquer pessoa se inscreva, mas proibir o acesso ao site até que um administrador aprove a conta de usuário.

Por padrão, o controle CreateUserWizard aprova novas contas. Você pode configurar esse comportamento usando a propriedade do DisableCreatedUsercontrole. Defina essa propriedade como true para não aprovar novas contas de usuário.

Observação

Por padrão, o controle CreateUserWizard faz logon automaticamente na nova conta de usuário. Esse comportamento é ditado pela propriedade do LoginCreatedUsercontrole. Como os usuários não aprovados não podem fazer logon no site, quando DisableCreatedUser é true que a nova conta de usuário não está conectada ao site, independentemente do valor da LoginCreatedUser propriedade.

Se você estiver criando programaticamente novas contas de usuário por meio do Membership.CreateUser método , para criar uma conta de usuário não aprovada, use uma das sobrecargas que aceitam o valor da propriedade do IsApproved novo usuário como um parâmetro de entrada.

Etapa 3: aprovar usuários verificando seu endereço de Email

Muitos sites que dão suporte a contas de usuário não aprovam novos usuários até que verifiquem o endereço de email fornecido ao se registrarem. Esse processo de verificação geralmente é usado para impedir bots, spammers e outros ne'er-do-wells, pois requer um endereço de email exclusivo e verificado e adiciona uma etapa extra no processo de inscrição. Com esse modelo, quando um novo usuário se inscreve, ele recebe uma mensagem de email que inclui um link para uma página de verificação. Ao visitar o link, o usuário provou que recebeu o email e, portanto, que o endereço de email fornecido é válido. A página de verificação é responsável por aprovar o usuário. Isso pode acontecer automaticamente, aprovando assim qualquer usuário que chega a esta página ou somente depois que o usuário fornece algumas informações adicionais, como um CAPTCHA.

Para acomodar esse fluxo de trabalho, precisamos primeiro atualizar a página de criação da conta para que novos usuários não sejam aprovados. Abra a EnhancedCreateUserWizard.aspx página na Membership pasta e defina a propriedade do DisableCreatedUser controle CreateUserWizard como true.

Em seguida, precisamos configurar o controle CreateUserWizard para enviar um email ao novo usuário com instruções sobre como verificar sua conta. Em particular, incluiremos um link no email para a Verification.aspx página (que ainda não criamos), passando o novo UserId usuário por meio da querystring. A Verification.aspx página pesquisará o usuário especificado e o marcará como aprovado.

Enviando um Email de verificação para novos usuários

Para enviar um email do controle CreateUserWizard, configure sua MailDefinition propriedade adequadamente. Conforme discutido no tutorial anterior, os controles ChangePassword e PasswordRecovery incluem uma MailDefinition propriedade que funciona da mesma maneira que o do controle CreateUserWizard.

Observação

Para usar a MailDefinition propriedade , você precisa especificar opções de entrega de email no Web.config. Para obter mais informações, consulte Enviando Email em ASP.NET.

Comece criando um novo modelo de email chamado CreateUserWizard.txt na EmailTemplates pasta . Use o seguinte texto para o modelo:

Hello <%UserName%>! Welcome aboard.

Your new account is almost ready, but before you can login you must first visit:
<%VerificationUrl%>

Once you have visited the verification URL you will be redirected to the login page.

If you have any problems or questions, please reply to this email.

Thanks!

Defina a MailDefinition' propriedade s BodyFileName como "~/EmailTemplates/CreateUserWizard.txt" e sua Subject propriedade como "Bem-vindo ao Meu Site! Ative sua conta."

Observe que o CreateUserWizard.txt modelo de email inclui um <%VerificationUrl%> espaço reservado. É aqui que a URL da Verification.aspx página será colocada. O CreateUserWizard substitui automaticamente os <%UserName%> espaços reservados e <%Password%> pelo nome de usuário e senha da nova conta, mas não há nenhum espaço reservado interno <%VerificationUrl%> . Precisamos substituí-lo manualmente pela URL de verificação apropriada.

Para fazer isso, crie um manipulador de eventos para o evento createUserWizard SendingMail e adicione o seguinte código:

protected void NewUserWizard_SendingMail(object sender, MailMessageEventArgs e)
{
     // Get the UserId of the just-added user
     MembershipUser newUser = Membership.GetUser(NewUserWizard.UserName);

     Guid newUserId = (Guid)newUser.ProviderUserKey;

     // Determine the full verification URL (i.e., http://yoursite.com/Verification.aspx?ID=...)
     string urlBase = Request.Url.GetLeftPart(UriPartial.Authority) + 
          Request.ApplicationPath;

     string verifyUrl = "/Verification.aspx?ID=" + newUserId.ToString();
     string fullUrl = urlBase + verifyUrl;

     // Replace <%VerificationUrl%> with the appropriate URL and querystring
     e.Message.Body = e.Message.Body.Replace("<%VerificationUrl%>", fullUrl);
}

O SendingMail evento é acionado após o CreatedUser evento, o que significa que, quando o manipulador de eventos acima executa, a nova conta de usuário já foi criada. Podemos acessar o valor do UserId novo usuário chamando o Membership.GetUser método , passando o UserName inserido para o controle CreateUserWizard. Em seguida, a URL de verificação é formada. A instrução Request.Url.GetLeftPart(UriPartial.Authority) retorna a http://yourserver.com parte da URL; Request.ApplicationPath retorna o caminho em que o aplicativo está com raiz. A URL de verificação é definida como Verification.aspx?ID=userId. Essas duas cadeias de caracteres são concatenadas para formar a URL completa. Por fim, o corpo da mensagem de email (e.Message.Body) tem todas as ocorrências de <%VerificationUrl%> substituídas pela URL completa.

O efeito líquido é que novos usuários não são aprovados, o que significa que eles não podem fazer logon no site. Além disso, eles recebem automaticamente um email com um link para a URL de verificação (consulte a Figura 6).

O novo usuário recebe um Email com um link para a URL de verificação

Figura 6: O novo usuário recebe um Email com um Link para a URL de verificação (clique para exibir a imagem em tamanho real)

Observação

A etapa CreateUserWizard padrão do controle CreateUserWizard exibe uma mensagem informando ao usuário que sua conta foi criada e exibe um botão Continuar. Clicar nisso leva o usuário para a URL especificada pela propriedade do ContinueDestinationPageUrl controle. O CreateUserWizard no EnhancedCreateUserWizard.aspx é configurado para enviar novos usuários para o ~/Membership/AdditionalUserInfo.aspx, que solicita ao usuário sua cidade natal, a URL da home page e a assinatura. Como essas informações só podem ser adicionadas por usuários conectados, faz sentido atualizar essa propriedade para enviar os usuários de volta à home page do site (~/Default.aspx). Além disso, a EnhancedCreateUserWizard.aspx página ou a etapa CreateUserWizard deve ser aumentada para informar ao usuário que ele recebeu um email de verificação e que sua conta não será ativada até que siga as instruções neste email. Deixo essas modificações como um exercício para o leitor.

Criando a página de verificação

Nossa tarefa final é criar a Verification.aspx página. Adicione esta página à pasta raiz, associando-a à Site.master página master. Como fizemos com a maioria das páginas de conteúdo anteriores adicionadas ao site, remova o controle Conteúdo que faz referência ao LoginContent ContentPlaceHolder para que a página de conteúdo use o conteúdo padrão da página master.

Adicione um controle Da Web rótulo à página, defina-o Verification.aspxIDStatusMessage como e limpe sua propriedade de texto. Em seguida, crie o Page_Load manipulador de eventos e adicione o seguinte código:

protected void Page_Load(object sender, EventArgs e)
{
     if (string.IsNullOrEmpty(Request.QueryString["ID"]))
          StatusMessage.Text = "The UserId was not included in the querystring...";
     else
     {
          Guid userId;
          try
          {
               userId = new Guid(Request.QueryString["ID"]);
          }

          catch
          {
               StatusMessage.Text = "The UserId passed into the querystring is not in the
                    proper format...";
               return;
          }

          MembershipUser usr = Membership.GetUser(userId);
          if (usr == null)
               StatusMessage.Text = "User account could not be found...";
          else
          {
               // Approve the user
               usr.IsApproved = true;

               Membership.UpdateUser(usr);
               StatusMessage.Text = "Your account has been approved. 
                    Please <a href=\"Login.aspx\">login</a> to the site.";
          }
     }
}

A maior parte do código acima verifica se o UserId fornecido por meio da querystring existe, se é um valor válido Guid e que faz referência a uma conta de usuário existente. Se todas essas verificações forem aprovadas, a conta de usuário será aprovada; caso contrário, uma mensagem de status adequada será exibida.

A Figura 7 mostra a Verification.aspx página quando visitada por meio de um navegador.

A conta do novo usuário agora está aprovada

Figura 7: A conta do novo usuário agora está aprovada (clique para exibir a imagem em tamanho real)

Resumo

Todas as contas de usuário de associação têm dois status que determinam se o usuário pode fazer logon no site: IsLockedOut e IsApproved. Ambas as propriedades devem ser true para o usuário fazer logon.

O status bloqueado do usuário é usado como medida de segurança para reduzir a probabilidade de um hacker invadir um site por meio de métodos de força bruta. Especificamente, um usuário será bloqueado se houver um determinado número de tentativas de logon inválidas em uma determinada janela de tempo. Esses limites são configuráveis por meio das configurações do provedor de associação no Web.config.

O status aprovado é comumente usado como um meio de proibir novos usuários de fazer logon até que alguma ação tenha ocorrido. Talvez o site exija que as novas contas sejam aprovadas primeiro pelo administrador ou, como vimos na Etapa 3, verificando seu endereço de email.

Programação feliz!

Sobre o autor

Scott Mitchell, autor de vários livros do ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Microsoft Web desde 1998. Scott trabalha como consultor independente, treinador e escritor. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 Horas. Scott pode ser contatado em mitchell@4guysfromrolla.com ou através de seu blog em http://ScottOnWriting.NET.

Agradecimentos Especiais a...

Esta série de tutoriais foi revisada por muitos revisores úteis. Interessado em revisar meus próximos artigos do MSDN? Nesse caso, deixe-me uma linha em mitchell@4GuysFromRolla.com