#desenvolvimento

Resolvendo o erro: Error calling method on NPObject


Olá pessoal.

Este é um daqueles post com soluções para os problemas do dia a dia. Recentemente eu precisei resolver o erro Uncaught Error: Error calling method on NPObject ocorrido em um projeto web desenvolvido com ASP.NET MVC3.

O tal erro acontecia ao submeter um determinado formulário. Mas só acontecia se em algum dos campos contivessem um & em seu conteúdo.

Este formulário continha um upload de imagem utilizando o Uploadify. E o erro acontecia no momento de executar a função uploadifyUpload() do plugin. Mais especificamente na linha abaixo:

document.getElementById(jQuery(this).attr('id') + 'Uploader').startFileUpload(ID, checkComplete);

Pesquisando e tentando solucionar este erro nada descritivo, identifiquei que o conteúdo do formulário era serializado antes de ser submetido. E quando isso acontecia o & era convertido para %26.

Então, resolvi substituir o & pelo unicode equivalente. Após serializar o objeto eu troquei o & pelo seu unicode \u0026 antes de chamar a função uploadifyUpload(). E o código ficou assim:

...
if ($('.uploadifyQueueItem').length > 0) {
   var conteudoForm= $("#id_form").serializeObject();
   var campo = conteudoForm['id_campo'];
   conteudoForm['id_campo'] = campo.replace('&', '\\u0026');
   $('.upload').uploadifySettings('scriptData', conteudoForm);
   $('.upload').uploadifyUpload();
}

Erro resolvido e formulário submetido normalmente. Tudo lindo, ou melhor, quase. O valor do campo está chegando com o unicode e precisamos voltar para o &.

No meu caso utilizando o ASP.NET MVC 3 recebo o conteúdo do formulário no método Create e faço a conversão lá mesmo:

[HttpPost]
public ActionResult Create(ModelQualquer modelQualquer, HttpPostedFileBase Filedata) {

   modelQualquer.Atributo = modelQualquer.Atributo.Replace("\\u0026", "&");
   ...
}

Agora sim, ta certo.

A solução foi bem idiota boba e deve ter umas 1000, ou mais, maneiras melhores de se resolver. O mais chatinho foi encontrar onde realmente estava o problema, já que o erro não diz nada muita coisa.

É isso, galera. Espero que possa ser útil e fiquem a vontade para postar outras soluções nos comentários.

Até breve 😉

Padrão
#desenvolvimento

Como utilizar jQuery + AJAX + Handler?


Olá leitores.

Continuando a ~onda~ de posts sobre jQuery dessa vez iremos fazer algo um pouco além do código HTML. Neste post veremos como é possível implementar uma comunicação entre HTML e C# sem utilizar ASP.NET WebForms nem ASP.NET MVC. Para isto utilizaremos os Handlers, que no final das contas é uma classe C#. E para ficar ainda melhor iremos utilizar AJAX e Twitter Bootstrap também.

Então vamos lá.

No Visual Studio, crie um novo projeto ASP.NET Empty Web Application. Com o projeto criado será preciso adicionar:

  • um arquivo HTML denominado index.html.
  • um arquivo Generic Handler denominado Handler.ashx.
  • uma pasta denominada javascript.
  • uma pasta denominada twitter-bootstrap.

Na pasta javascript deverá conter os arquivos:
jquery-1.7.2.min.js
less-1.3.0.min.js
jquery.form.js

E na pasta twitter-bootstrap deve conter apenas as pastas img, js e less contidas nos fontes do Twitter Bootstrap.

No arquivo index.html vamos adicionar as referências aos arquivos CSS:

<link rel="stylesheet/less" type="text/css" href="twitter-bootstrap/less/bootstrap.less">
<link rel="stylesheet/less" type="text/css" href="twitter-bootstrap/less/responsive.less">

E as referências aos arquivos JS:

<script src="javascript/less-1.3.0.min.js"></script>
<script src="javascript/jquery-1.7.2.min.js"></script>
<script src="javascript/jquery.form.js"></script>
<script src="twitter-bootstrap/js/bootstrap-modal.js"></script>

O código abaixo cria um formulário com apenas um campo e um botão. Adicione-o dentro da tag body do arquivo index.html:

<div class="span5 form-horizontal">
   <!-- Formulário -->
   <form id="form-enviar-email">
      <fieldset>
         <legend>Formulário</legend>
         <!-- Campo de e-mail -->
         <div class="control-group">
            <label for="txt-email" class="control-label">E-mail</label>
            <div class="controls">
               <div class="input-prepend">
                  <span class="add-on"><i class="icon-envelope"></i></span><input id="email" required style="text-transform: lowercase;" class="input-xlarge" placeholder="Digite seu e-mail" name="txt-email" type="email">
               </div>
            </div>
         </div>
         <!-- Botão para enviar -->
         <div class="span5 form-actions">
            <button id="btn-adicionar" type="submit" class="btn btn-primary">Enviar</button>
         </div>
      </fieldset>
   </form>
</div>

Vamos adicionar uma div com um modal para exibir o valor digitado no campo:

<!-- Modal -->
<div class="modal hide" id="div-modal">
   <div class="modal-header">
      <button type="button" class="close" data-dismiss="modal">×</button>
      <h3>Mensagem</h3>
   </div>
   <div class="modal-body">
      <!-- E-mail digitado no formulário -->
      <span id="span-email"></span>
   </div>
   <div class="modal-footer">
      <a href="#" class="btn" data-dismiss="modal">Fechar</a>
   </div>
</div>

E para finalizar o conteúdo do arquivo index.html é necessário acrescentar o código responsável pela comunicação com o Handler utilizando o jQuery Form Plugin.

<script type="text/javascript">
   $(document).ready(function () {

      // Adicionar evento de click ao botão "Enviar".
      $('#btn-enviar').click(function () {

         // Cria um conjunto de opções para envio do formulário.
         var opcoes = {

            type: 'POST',        // Define o método de envio como POST.
            url: 'Handler.ashx', // Define qual Handler será chamado.

            // Função executada se a comunicação acontecer corretamente.
            success: function (data) {

               // Adiciona o retorno no Handler para o span contido no modal.
               $('#span-email').html(data);

               // Exibe o modal.
               $('#div-modal').modal();
            },
            // Trata o erro caso aconteça alguma falha na comunicação.
            error: function (data) {
               alert("Ocorreu um erro ao executar a operação.");
            }
         };
         // Prepara o formulário para submissão via AJAX.
         $('#form-enviar-email').ajaxForm(opcoes);
      });
   });
</script>

Falta ainda o código para o Handler funcionar. Precisamos de dois métodos:
ProcessRequest – que é invocado quando o Handler for chamado pelo formulário.

public void ProcessRequest(HttpContext context)
{
   // Obtém o formulário.
   NameValueCollection formulario = context.Request.Form;
   context.Response.ContentType = "text/html";

   // Escreve o retorno do método para a saída do HTTP atual.
   context.Response.Write(ObterEmailInformado(formulario));
}

ObterEmailInformado – que captura o valor informado no campo.

protected string ObterEmailInformado(NameValueCollection formulario)
{
   // Obtém o valor do campo "txt-email".
   string email = formulario["txt-email"];

   // Cria a string de retorno com o e-mail digitado.
   StringBuilder conteudoHtml = new StringBuilder(string.Format("Email: {0}", email));

   return conteudoHtml.ToString();
}

É isso! A príncipio parece algo difícil de entender, mas tentei fazer com que o código ficasse bem simples e espero que seja útil para quem precisar fazer esse tipo de implementação.

Baixe o código completo.

Até breve 😉

Padrão
#desenvolvimento

C# – Como obter o usuário logado no AD?


Olá pessoal.

Não é novidade precisar capturar alguns dados diretamente do Active Directory através do usuário logado, sem que seja necessário a realização de uma nova autenticação. Eu já tinha feito isto uma vez utilizando Java e achei bem trabalhoso, pensei então que em C# seria mais natural acreditando em uma possível integração da plataforma .NET.

Comecei a pesquisar sobre uma melhor forma para fazer isto, e não encontrei muitas alternativas. De qualquer forma irei postar uma solução aqui, deixando os comentários sempre abertos para que outras soluções possam ser compartilhadas.

Para começar adicione as referências System.DirectoryServices e System.DirectoryServices.AccountManagement ao seu projeto.

Com isto é possível utilizar os namespaces que possuem as classes associadas ao Active Directory:

using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;

O método abaixo cria os objetos das classes referentes ao Active Directory necessários para a busca dos dados do usuário logado.

private Usuario ObterDadosUsuarioLogado()
{
   // Obtém o login do usuário na rede.
   string loginUserNetwork = Page.User.Identity.Name.Split('\\')[1];

   // Cria um contexto principal para definir a busca na base do AD.
   PrincipalContext principalContext = new PrincipalContext(ContextType.Domain);

   // Cria uma query do Active Directory Domain Services.
   DirectorySearcher directorySearcher = new DirectorySearcher(principalContext.ConnectedServer);

   // Cria um filtro para a busca com o login do usuário da rede.
   directorySearcher.Filter = "(sAMAccountName=" + loginUserNetwork + ")";

   /*
    * Cria um elemento da hierarquia do Active Directory.
    * Este elemento é retornado durante uma busca através de DirectorySearcher.
    */
   SearchResult searchResult = directorySearcher.FindOne();

   // Cria um objeto encapsulado da hierarquia de diretório serviços do AD.
   DirectoryEntry directoryEntry = searchResult.GetDirectoryEntry();

   // Cria um usuário.
   return new Usuario(directoryEntry);
}

Os atributos do Active Directory possuem uma nomenclatura pouco inteligível, por isto resolvi criar uma classe denominada Usuario e mapear alguns atributos do AD como propriedades da classe. E para facilitar criei um construtor que recebe uma coleção de atributos do AD e associa às propriedades do usuário:

public Usuario(DirectoryEntry directoryEntry)
{
   // Obtém as informações do usuário.
   FirstName = directoryEntry.Properties["givenName"][0].ToString();
   LastName = directoryEntry.Properties["sn"][0].ToString();
   DisplayName = directoryEntry.Properties["displayName"][0].ToString();
   Description = directoryEntry.Properties["description"][0].ToString();

   // Obtém as informações da conta do usuário.
   UserPrincipalName = directoryEntry.Properties["userPrincipalName"][0].ToString();
   LogonName = directoryEntry.Properties["sAMAccountName"][0].ToString();
   ScriptPath = directoryEntry.Properties["scriptPath"][0].ToString();

   // Obtém as informações referentes às funções do usuário.
   Title = directoryEntry.Properties["title"][0].ToString();
   Department = directoryEntry.Properties["department"][0].ToString();
   Company = directoryEntry.Properties["company"][0].ToString();
   Manager = directoryEntry.Properties["manager"][0].ToString().Split(',')[0].Split('=')[1];

   // Obtém as informações referente aos contatos do usuário.
   TelephoneNumber = directoryEntry.Properties["telephoneNumber"][0].ToString();
   FaxNumber = directoryEntry.Properties["facsimileTelephoneNumber"][0].ToString();
   Email = directoryEntry.Properties["mail"][0].ToString();

   // Obtém as as informações referentes ao endereço do usuário.
   StreetAddress = directoryEntry.Properties["streetAddress"][0].ToString();
   City = directoryEntry.Properties["l"][0].ToString();
   State = directoryEntry.Properties["st"][0].ToString();
   Zip = directoryEntry.Properties["postalCode"][0].ToString();
   Country = directoryEntry.Properties["co"][0].ToString();
}

Esta foi a forma mais simples que encontrei para acessar os dados. Compartilho os links que me ajudaram a chegar nesta solução:

How to get userdetails from Active Directory based on username using ASP.NET

All Operations on Active Directory (AD) using C#

Até breve 😉

[Editado]

O leitor Hugo, encontrou uma dificuldade para obter o usuário e compartilhou o código que acrescentou no arquivo web.config. Eu solicitei que ele disponibilizasse o código para que possa ajudar, se mais alguém tiver o mesmo problema. Obrigada, Hugo.


<!--
   <authentication mode="Forms">
      <forms loginUrl="~/Account/Login.<wbr />aspx" timeout="2880" />
   </authentication>
-->
<authentication mode="Windows" />
<!--
   Then add the authorization tag to deny access to anonymous users.
   If the users are using Internet Explorer and are connecting from an Intranet zone, IE automatically will login the user. 
   But if the user is connecting from the Internet zone, IE will still display a login box though for safety.
   But these are options that can be set from IE.
-->
<authorization>
   <deny users="?" />
</authorization>

[/Editado]

Padrão
#prosa

DevDay 2011


Ei pessoal.

Este é mais um post de divulgação de evento que será realizado em BH.
O DevDay 2011 acontecerá no dia 27/08 na UNA Campus Aimorés e abordará assuntos como:

  • Planejamento e acompanhamento ágil com Team Foundation
  • Qualidade de codificação
  • Windows Azure
  • TDD
  • Novidades do Rails 3.1
  • Programação funcional com C#
  • Evolução do Javascript

A inscrição pode ser feita no site http://devday.devisland.com

Bom evento pra todos 😉

Padrão