Nas profundezas do ASP.Net

Nas profundezas do ASP.Net

Este artigo busca mostrar como funcionam as aplicações escritas com Asp.Net desde um ponto de vista mais profundo e fundamental, como o funcionamento dos protocolos TCP e HTTP até os mais superficiais, ou seja, o funcionamento das requisições, o view engine e como interpretado pelo navegador.

Isso pode parecer básico ou essencial, mas o desenvolvedor médio de Asp.Net muitas vezes desconhece esses conceitos, porque os IDEs e a estrutura .Net tendem a abstrair isso para facilitar o desenvolvimento de aplicativos da web e aumentar a velocidade de trabalho. aplicativos com .Net, você não necessariamente estudou ou pesquisou esses fundamentos.

Com isso não quero dizer que não saber totalmente como funcionam os aplicativos da web .Net seja errado, mas saber disso fará diferença na qualidade dos aplicativos que você fizer, ao mesmo tempo em que entenderá mais rapidamente os erros que o levariam a investigar mais do que uma tarde.

Se você já cria aplicativos .Net há algum tempo, notará que, ao iniciar um novo projeto, ele solicita que você especifique o tipo de projeto que deseja criar: ASP.NET Core Empty, MVC, Web API, Forms, Testes, etc (Você pode vê-lo com o comando dotnet new --list.) Embora possa ser óbvio – muitos podem não saber – todos e cada um deles são iguais: são chamados de templates e são programas pré-codificados, prontos para incorporar a lógica que precisamos para criar um produto de software.

Vamos focar nos templates ASP.NET Core e afins para entender a fundo como é o fluxo de requisições do seu servidor para seus clientes, abordando desde o mais fundamental até o que você mais usa no seu dia a dia como desenvolvedor.de .Net.

Problema de completude

Normalmente, mesmo depois de desenvolvermos aplicativos ASP.NET por anos, nosso entendimento de como os aplicativos da Web funcionam é bom, mas incompleto. Então, vamos reduzir o nível de complexidade de onde você está até termos uma visão holística. Uma aplicação web é basicamente uma aplicação do tipo TCP (Transmission Control Protocol) com um ou vários eventos esperando por mensagens.

O que isso envolve? Que eles continuem esperando em um IP e em uma porta por tudo o que recebem, porque suas ações posteriores dependem disso. Grosso modo, um aplicativo TCP pode esperar todos os tipos de dados e responder a todos os tipos de dados, o que implica que o que eles recebem são bytes e o que retornam são bytes. O acima é mobilizado por meio de um fluxo de rede, semelhante aos fluxos de arquivos e fluxos de memória. Streams são APIs que permitem, por meio de código, enviar bytes por meio de um barramento (componente do PC que comunica eletricamente diferentes dispositivos de hardware) de um lado para o outro.

No caso dos fluxos de rede, eles vão da memória para a rede, enquanto a memória vai do espaço da memória para o espaço da memória e os fluxos de arquivos vão da memória para o disco. Basicamente, uma web app é um programa como outro qualquer, por isso segmentações como Web API, Web Apps Backend ou Frontend (entre outras) perdem o sentido neste nível de complexidade.

De TCP para HTTP

Vamos aumentar a complexidade. A única coisa que um aplicativo TCP entende é um endereço IP e uma porta. No entanto, outro protocolo foi inserido sobre esse protocolo, o HTTP. Portanto, um aplicativo HTTP é um programa como qualquer outro com eventos baseados em uma string (o URL), embora realmente aceite tudo como o TCP. A única diferença é que dentro dele tem um switch case com strings específicas (URIs) e dentro de cada case outro switch case adicional com verbos (GET, POST, PUT, PATCH, DELETE, entre outros).

Vamos elevar ainda mais o nível falando sobre como os frameworks da Web (neste caso, .NET) funcionam. A Microsoft nos abstrai dos 2 níveis de complexidade acima criando bibliotecas e modelos de código para criar programas que se comportam como programas TCP, mas usando o protocolo HTTP. Para conseguir isso, existem muitas maneiras e há muito tempo atrás havia um debate sobre como desenvolver esses aplicativos, seja por configuração (declarar explicitamente as URLs e outras funções) ou por convenção (o framework tem comportamento geral implícito e você apenas declara o que você precisa).

Se você perceber, a "API mínima" é um exemplo de configuração, ou seja, você deve obrigatoriamente declarar a rota desejada para cada callback, enquanto o famoso "MVC" é um exemplo de convenção: você usa um formulário previamente desenhado por outras pessoas (a equipe da Microsoft) e com base em uma metodologia amplamente aceita para seguir suas instruções (por exemplo, roteamento é controlador/ação).

Grosso modo, ambos são os mesmos programas HTTP que:

- Eles escutam um evento para executar o código.
- Eles trocam casos com a string que segue seu host (um DNS ou endereço IP com porta).
- Eles fazem outra troca com o verbo contido nos bytes da recepção.
- Eles executam de acordo com o que foram programados.

No final das contas, um site apenas abre um fluxo de rede e, se os bytes chegarem, ele executa os eventos.

O fluxo no protocolo HTTP

Como mencionei, tudo que é aplicado ao TCP funciona de forma muito parecida: espera bytes de um fluxo de rede, executa o que for necessário e pronto, mas não é o suficiente. É aí que entra a arquitetura cliente-servidor. Nessa arquitetura, o que se espera é que um servidor, além de executar ações ao receber bytes (em pacotes), também os envie de volta pelo mesmo fluxo. Chamamos isso de requisição/resposta: quando os bytes chegam é chamado de requisição, a execução do evento é chamada de processo ou execução e quando o programa devolve os bytes é chamado de resposta.

HTTP não é o único protocolo que funciona assim. Na verdade, outros como o POP (funcionário do correio) funcionam da mesma maneira. A questão é que o motivo dessa arquitetura foi a necessidade de centralizar as informações e isso só foi possível quando os computadores pessoais ficaram mais potentes e menores. Esses programas, chamados de clientes (como o navegador ou programas que usam bibliotecas para se comunicar), abrem um fluxo de rede na direção do programa (que chamaremos agora de servidor). O servidor aceita esses bytes, executa o que está em questão e envia os bytes de volta. Simples assim.

Esses bytes são usados ​​para executar o processo, e quando o protocolo HTTP foi criado, foi decidida a forma como esses bytes serão ordenados: o URI, o verbo, os cabeçalhos e o corpo (existem mais elementos, mas os anteriores são os mais usados). O programa servidor, já desenhado para trabalhar com HTTP é este:

  • Receba esses bytes.
  • Ele pega a área associada ao URI e faz um switch case.
  • Se o URI estiver dentro dos casos, verifique o verbo e faça outra troca de caso.
  • Se o verbo estiver dentro dos casos, ele executa o que está programado para fazer usando os cabeçalhos e o corpo.
  • Em seguida, envie para o fluxo na direção oposta os bytes que chamaremos de resposta.

Os modelos .NET por dentro

Vamos aumentar ainda mais a complexidade para os mesmos padrões da equipe .NET. Se você olhar de perto, notará que os termos Razor page, web API, MVC, API+React e API+Angular (você pode verificar isso com o comando dotnet new --list) são praticamente os mesmos, mas em um formato diferente. Vejamos, por exemplo, uma API mínima, onde uma requisição é simplesmente uma função (digo function ao invés de method porque é mais um paradigma funcional do que um poo) onde seu nome é Map + Verb, cujos parâmetros são a URI e um retorno de chamada que define o comportamento e a resposta desse URI.

Por outro lado, MVC são classes que servirão como coleções de métodos para definir comportamentos e respostas, cujas URIs são definidas por convenção com base no nome da classe e no método e o verbo é definido por um atributo (aquele elemento descrito com quadrado colchetes acima do método por exemplo [HttpGet]).

As Razor Pages usam a convenção de nomenclatura para o URI, bem como os métodos OnVerbo como definições de comportamento e resposta. Por fim, as web APIs (que nem é uma forma de organização, são utilizadas as anteriores citadas, a web api é como são configurados os bytes que chegam e são disparados ou melhor, como são configurados o pedido e a resposta.

O navegador é o cliente mais versátil

É hora de enfrentar o HTML. Até aqui sabemos que nosso programa espera e envia bytes, assim como podemos decidir na programação a forma como esses bytes são enviados, quem decide o que fazer com o que enviamos são os clientes. Olhe para esta imagem:

Agora esta imagem:

Ambas têm o mesmo corpo, mas são exibidas de forma diferente porque têm cabeçalhos diferentes. O navegador foi projetado para ser o cliente HTTP perfeito e programado de maneira diferente com base nos bytes de resposta, especificamente no que vem no cabeçalho:

  • Se vier text/plain, ele apenas coloca o texto sem nenhuma formatação (é por isso que você pode ver as tags </> html aparecerem).
  • Se houver uma imagem, basta adicionar a imagem.
  • Se vier application/octet-stream, faça o download do arquivo.
  • Se text/html estiver chegando, configure um DOM e mostre como ele se parece.
  • Se vier application/json, faça exatamente o mesmo que com text/plain.


Agora, por que não usamos apenas text/plain em vez de application/json se exatamente a mesma coisa acontecer? Bem, porque muitos clientes são configurados para desserializar automaticamente o json se lerem um cabeçalho application/json, mesmo em JavaScript.

Interfaz de usuario gráfica, Texto, Aplicación

Descripción generada automáticamente

E por falar em DOM, acontece que ele é o responsável por quando uma string é escrita em formato HTML no navegador, ele passa para o motor Javascript e este começa a criar objetos (objetos JS) para que possam ser vistos graficamente, instâncias muito parecidas com o código em Windows Forms com suas propriedades, métodos e eventos para cada uso.

Esses objetos são dispostos em uma estrutura de dados em que uma de suas propriedades possui uma coleção ou array (filhos) com os elementos abaixo de sua hierarquia, enquanto estes por sua vez também possuem esta propriedade com outros elementos e a propriedade (pai) com a referência em memória daquele objeto que o contém em sua propriedade children. O seletor de consulta e o método getElementById são APIs que usam um padrão para evitar a navegação manual entre as propriedades mencionadas.

A responsabilidade do ASP.NET termina com a resposta

Imediatamente, nosso servidor dispara nesses bytes, a responsabilidade por tudo o que acontece não é do nosso servidor. É apenas uma máquina que recebe e envia elementos, nada mais. E você vai se perguntar: no Razor, por que eu consigo escrever código c# e isso é refletido no navegador? Muito simples: a execução desse código C# é feita durante o processo, ou seja, após receber a requisição, mas antes de disparar a resposta, por algo chamado view engine. O para .NET é chamado Razor.

Toda vez que você usa Asp-for ou @Html.EditorFor(m=> m.Name), sua execução acontece em MVC com o método View() e em páginas Razor no final do método OnGet. Agora, o que acontece no final desses métodos? Na verdade, o método View() é uma resposta com código de status 200 (parte de nossos bytes de resposta), tipo de conteúdo (cabeçalho de resposta) text/html, que, antes de disparar a resposta em:

- Leia o arquivo de visualização (.cshtml).
- Executa o código C# dentro (o que significa que, se houver um loop que preencha a tabela, ele o substituirá pela string correspondente aos elementos dessa tabela, dependendo dos dados no objeto de modelo que você usar).
- Procura o seu layout e, caso não o tenha, coloca o que está por defeito.
- Coloca essa string completa em formato html dentro do corpo e junto com todos os seus cabeçalhos, cookies de código de status e outros elementos que o pacote http de resposta deve carregar de acordo com a forma como o programamos, nosso servidor o aciona para o cliente que fez o solicitar.

Por fim, se nosso cliente for um programa que usa uma biblioteca HTTP, ele só recebe a resposta em texto e depois faz com esse texto o que foi criado, enquanto se um navegador o recebe, seu mecanismo js (ou o que tiver) , se encarrega de criar os elementos na memória e estes, por sua vez, são desenhados na tela, como mencionamos na seção anterior.

Agora, com o entendimento de tudo que acontece entre seus clientes e seu servidor, ficará muito mais fácil depurar todas as suas aplicações escritas em ASP.NET.

Vejo você em um próximo artigo.

⚠️
As opiniões e comentários expressos neste artigo são de propriedade exclusiva de seu autor e não representam necessariamente o ponto de vista da Revelo.

A Revelo Content Network acolhe todas as raças, etnias, nacionalidades, credos, gêneros, orientações, pontos de vista e ideologias, desde que promovam diversidade, equidade, inclusão e crescimento na carreira dos profissionais de tecnologia.