Tratar solicitações com controladores no ASP.NET Core MVC

Por Steve Smith e Scott Addie

Controladores, ações e resultados da ação são uma parte fundamental de como os desenvolvedores criam aplicativos usando o ASP.NET Core MVC.

O que é um controlador?

Um controlador é usado para definir e agrupar um conjunto de ações. Uma ação (ou método de ação) é um método em um controlador que manipula solicitações. Os controladores agrupam ações semelhantes de forma lógica. Essa agregação de ações permite que conjuntos de regras comuns, como roteamento, cache e autorização, sejam aplicados em conjunto. As solicitações são mapeadas para ações por meio de roteamento. Os controladores são ativados e descartados por solicitação.

Por convenção, as classes do controlador:

  • Reside na pasta Controladores no nível raiz do projeto.
  • Herdar de Microsoft.AspNetCore.Mvc.Controller.

Um controlador é uma classe que pode ser instanciada, normalmente pública, em que, pelo menos, uma das seguintes condições é verdadeira:

  • O nome da classe tem Controller como sufixo.
  • A classe herda de uma classe cujo nome tem Controller como sufixo.
  • O atributo [Controller] é aplicado à classe.

Uma classe de controlador não deve ter um atributo [NonController] associado.

Os controladores devem seguir o Princípio de Dependências Explícitas. Há duas abordagens para implementar esse princípio. Se várias ações do controlador exigem o mesmo serviço, considere o uso da injeção de construtor para solicitar essas dependências. Se o serviço é necessário para um único método de ação, considere o uso da Injeção de Ação para solicitar a dependência.

Dentro do padrão Model-View-Controller, um controlador é responsável pelo processamento inicial da solicitação e criação de uma instância do modelo. Em geral, as decisões de negócios devem ser tomadas dentro do modelo.

O controlador usa o resultado do processamento do modelo (se houver) e retorna a exibição correta e seus dados da exibição associada ou o resultado da chamada à API. Saiba mais em Visão geral do ASP.NET Core MVC e em Introdução ao ASP.NET Core MVC e ao Visual Studio.

O controlador é uma abstração no nível da interface do usuário. Suas responsabilidades são garantir que os dados de solicitação sejam válidos e escolher qual exibição (ou resultado de uma API) deve ser retornada. Em aplicativos bem fatorados, ele não inclui diretamente o acesso a dados ou a lógica de negócios. Em vez disso, o controlador delega essas responsabilidades a serviços.

Como definir ações

Métodos públicos em um controlador, exceto aqueles com o atributo [NonAction], são ações. Parâmetros em ações são associados aos dados de solicitação e validados usando o model binding. A validação de modelo ocorre em tudo o que é associado ao modelo. O valor da propriedade ModelState.IsValid indica se o model binding e a validação foram bem-sucedidas.

Métodos de ação devem conter uma lógica para mapear uma solicitação para um interesse de negócios. Normalmente, interesses de negócios devem ser representados como serviços acessados pelo controlador por meio da injeção de dependência. Em seguida, as ações mapeiam o resultado da ação de negócios para um estado do aplicativo.

As ações podem retornar qualquer coisa, mas frequentemente retornam uma instância de IActionResult (ou Task<IActionResult> para métodos assíncronos) que produz uma resposta. O método de ação é responsável por escolher o tipo de resposta. O resultado da ação é responsável pela resposta.

Métodos auxiliares do controlador

Os controladores geralmente herdam de Controller, embora isso não seja necessário. A derivação de Controller fornece acesso a três categorias de métodos auxiliares:

1. Métodos que resultam em um corpo de resposta vazio

Nenhum cabeçalho de resposta HTTP Content-Type é incluído, pois o corpo da resposta não tem nenhum conteúdo a ser descrito.

Há dois tipos de resultado nessa categoria: Redirecionamento e Código de Status HTTP.

  • Código de Status HTTP

    Esse tipo retorna um código de status HTTP. Alguns métodos auxiliares desse tipo são BadRequest, NotFound e Ok. Por exemplo, return BadRequest(); produz um código de status 400 quando executado. Quando métodos como BadRequest, NotFound e Ok estão sobrecarregados, eles deixam de se qualificar como respondentes do Código de Status HTTP, pois a negociação de conteúdo está em andamento.

  • Redirecionar

    Esse tipo retorna um redirecionamento para uma ação ou um destino (usando Redirect, LocalRedirect, RedirectToAction ou RedirectToRoute). Por exemplo, return RedirectToAction("Complete", new {id = 123}); redireciona para Complete, passando um objeto anônimo.

    O tipo de resultado do Redirecionamento é diferente do tipo do Código de Status HTTP, principalmente na adição de um cabeçalho de resposta HTTP Location.

2. Métodos que resultam em um corpo de resposta não vazio com um tipo de conteúdo predefinido

A maioria dos métodos auxiliares desta categoria inclui uma propriedade ContentType, permitindo que você defina o cabeçalho de resposta Content-Type para descrever o corpo da resposta.

Há dois tipos de resultado nessa categoria: Exibição e Resposta Formatada.

  • Exibir

    Esse tipo retorna uma exibição que usa um modelo para renderizar HTML. Por exemplo, return View(customer); passa um modelo para a exibição para associação de dados.

  • Resposta Formatada

    Esse tipo retorna JSON ou um formato de troca de dados semelhante para representar um objeto de uma maneira específica. Por exemplo, return Json(customer); serializa o objeto fornecido no formato JSON.

    Outros métodos comuns desse tipo incluem File e PhysicalFile. Por exemplo, return PhysicalFile(customerFilePath, "text/xml"); retorna PhysicalFileResult.

3. Métodos que resultam em um corpo de resposta não vazio formatado em um tipo de conteúdo negociado com o cliente

Essa categoria é mais conhecida como Negociação de Conteúdo. A Negociação de conteúdo aplica-se sempre que uma ação retorna um tipo ObjectResult ou algo diferente de uma implementação IActionResult. Uma ação que retorna uma implementação não IActionResult (por exemplo, object) também retorna uma Resposta Formatada.

Alguns métodos auxiliares desse tipo incluem BadRequest, CreatedAtRoute e Ok. Exemplos desses métodos incluem return BadRequest(modelState);, return CreatedAtRoute("routename", values, newobject); e return Ok(value);, respectivamente. Observe que BadRequest e Ok fazem a negociação de conteúdo apenas quando um valor é passado; sem que um valor seja passado, eles atuam como tipos de resultado do Código de Status HTTP. Por outro lado, o método CreatedAtRoute sempre faz a negociação de conteúdo, pois todas as suas sobrecargas exigem que um valor seja passado.

Interesses paralelos

Normalmente, os aplicativos compartilham partes de seu fluxo de trabalho. Exemplos incluem um aplicativo que exige autenticação para acessar o carrinho de compras ou um aplicativo que armazena dados em cache em algumas páginas. Para executar a lógica antes ou depois de um método de ação, use um filtro. A utilização de filtros em interesses paralelos pode reduzir a duplicação.

A maioria dos atributos de filtro, como [Authorize], pode ser aplicada no nível do controlador ou da ação, dependendo do nível desejado de granularidade.

O tratamento de erro e o cache de resposta costumam ser interesses paralelos:

Muitos interesses paralelos podem ser abordados com filtros ou um middleware personalizado.