André Alves de Lima

Talking about Software Development and more…

Expondo PDFs gerados pelo Report Viewer com Web API

A cada dia que passa, aumenta a necessidade de criarmos diferentes versões dos nossos aplicativos, cada uma voltada para uma plataforma diferente. No mundo cada vez mais conectado de hoje em dia, não é raro termos o requisito que a nossa aplicação funcione em dispositivos móveis, além do desktop (ou web). Mas, e se a nossa aplicação utiliza o Report Viewer para gerar relatórios? Como fazer com que o mecanismo de geração de relatórios seja implementado somente em um lugar? Uma das opções é expormos PDFs gerados pelo Report Viewer com Web API. E é justamente isso que você vai conferir no artigo de hoje.

Criando o projeto de Web API

A ideia de criarmos uma Web API para gerarmos PDFs do relatório faz com que toda a lógica de geração dos relatórios seja separada neste projeto de Web API. Dessa forma, não temos repetição de código, além de tornar os relatórios compatíveis com todas as plataformas, uma vez que conseguimos exibir PDFs em praticamente qualquer lugar.

Vamos começar criando um novo projeto do tipo “ASP.NET Web Application” e escolhendo o template da “Web API“. Para facilitar o processo, desabilitei a autenticação e a hospedagem no Azure:

Criando o relatório

Com o projeto criado, vamos adicionar um novo DataSet dentro da pasta “Models“, que servirá de fonte de dados para o relatório. Vamos dar o nome de “DataSetFuncionario” para esse novo DataSet e, dentro dele, vamos criar uma nova DataTable chamada “Funcionario“:

Nota: não é obrigatório o uso de DataSets com o Report Viewer. Se você já tiver o arquivo .rdlc gerado utilizando um outro tipo de fonte de dados, você pode utilizá-lo sem problema na sua Web API. Por exemplo, é possível criarmos os relatórios .rdlc utilizando as classes de domínio do nosso projeto, como mostrei neste artigo sobre o Report Viewer com o Entity Framework.

Agora que já temos o DataSet criado, vamos adicionar uma nova pasta ao nosso projeto. Daremos o nome de “Reports” para essa pasta, e é dentro dela que armazenaremos os nossos relatórios:

Uma vez criada a pasta “Reports“, adicione um novo item do tipo “Report” dentro dessa pasta, dando o nome de “ReportFuncionario“:

Feito isso, adicione um novo DataSet no relatório, escolhendo o DataSet que criamos anteriormente e a DataTable “Funcionario“:

Em seguida, adicione uma tabela e arraste os campos do DataSet para dentro das colunas da tabela, de forma que o layout fique parecido com a imagem abaixo:

Expondo o PDF do relatório

Com o relatório criado, vamos partir para a criação do controller que “servirá” o PDF do relatório. Porém, antes de criarmos um novo controller, precisamos adicionar no nosso projeto a referência à dll do Report Viewer, que fica dentro da categoria “Extensions” da tela de adição de referências:

Agora que já temos a referência para a dll do Report Viewer, vamos criar um novo controller dentro da pasta “Controllers“. Escolha o template “Web API 2 Controller – Empty” (uma vez que nós só criaremos manualmente o método “Get” dentro desse controller) e escolha o nome de “RelatorioController“:

O método para gerarmos o PDF do relatório é muito simples. Primeiramente, precisamos criar o DataSet e uma instância de “LocalReport” passando o DataSet criado. Em seguida, utilizamos o método “Render” para gerarmos o PDF, resultando em um array de bytes. Por fim, retornamos o array de bytes como conteúdo de uma HttpResponseMessage:

    // C#
    public class RelatorioController : ApiController
    {
        public HttpResponseMessage Get()
        {
            var dataSet = new Models.DataSetFuncionario();
            dataSet.Funcionario.AddFuncionarioRow("André", "Lima", DateTime.Now);
            dataSet.Funcionario.AddFuncionarioRow("Fulano", "de Tal", DateTime.Now);
            dataSet.Funcionario.AddFuncionarioRow("Beltrano", "da Silva", DateTime.Now);

            var report = new Microsoft.Reporting.WebForms.LocalReport();
            report.ReportPath = System.Web.Hosting.HostingEnvironment.MapPath("~/Reports/ReportFuncionario.rdlc");
            report.DataSources.Add(new Microsoft.Reporting.WebForms.ReportDataSource("DataSetFuncionario", (System.Data.DataTable)dataSet.Funcionario));
            report.Refresh();

            string mimeType = "";
            string encoding = "";
            string filenameExtension = "";
            string[] streams = null;
            Microsoft.Reporting.WebForms.Warning[] warnings = null;
            byte[] bytes = report.Render("PDF", null, out mimeType, out encoding, out filenameExtension, out streams, out warnings);

            HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
            result.Content = new ByteArrayContent(bytes);
            result.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(mimeType);
            return result;
        }
    }
    ' VB.NET
    Public Class RelatorioController
        Inherits ApiController

        Public Function GetValues() As Http.HttpResponseMessage
            Dim DataSet As New DataSetFuncionario()
            DataSet.Funcionario.AddFuncionarioRow("André", "Lima", DateTime.Now)
            DataSet.Funcionario.AddFuncionarioRow("Fulano", "de Tal", DateTime.Now)
            DataSet.Funcionario.AddFuncionarioRow("Beltrano", "da Silva", DateTime.Now)

            Dim Report As New Microsoft.Reporting.WebForms.LocalReport()
            Report.ReportPath = System.Web.Hosting.HostingEnvironment.MapPath("~/Reports/ReportFuncionario.rdlc")
            Report.DataSources.Add(New Microsoft.Reporting.WebForms.ReportDataSource("DataSetFuncionario", DirectCast(DataSet.Funcionario, System.Data.DataTable)))
            Report.Refresh()

            Dim MimeType As String = ""
            Dim Encoding As String = ""
            Dim FilenameExtension As String = ""
            Dim Streams As String() = Nothing
            Dim Warnings As Microsoft.Reporting.WebForms.Warning() = Nothing
            Dim Bytes As Byte() = Report.Render("PDF", Nothing, MimeType, Encoding, FilenameExtension, Streams, Warnings)

            Dim Result As Http.HttpResponseMessage = New Http.HttpResponseMessage(HttpStatusCode.OK)
            Result.Content = New Http.ByteArrayContent(Bytes)
            Result.Content.Headers.ContentType = New System.Net.Http.Headers.MediaTypeHeaderValue(MimeType)
            Return Result
        End Function
    End Class

Execute a aplicação e, na janela do browser, adicione o final “/api/Relatorio” na URL da API para ver o PDF sendo gerado:

Possível melhoria

No exemplo que vimos até agora, criamos um controller para exibirmos um relatório. Obviamente, em um projeto com vários relatórios, isso se transformaria rapidamente em um pesadelo. Uma possível melhoria que poderíamos implementar nesse caso é termos somente um controller responsável pela geração dos PDFs e esse controller receberia o nome do relatório a ser gerado. Isso poderia ser feito adicionando um parâmetro string no método “Get” da API. Dessa forma, poderíamos hipoteticamente obter, por exemplo, o relatório de funcionários através da URL “/api/Relatorios/Funcionarios” e o relatório de clientes através da URL “/api/Relatorios/Clientes“:

        // C#
        public HttpResponseMessage Get(string id)
        {
            switch (id.ToUpper())
            {
                case "FUNCIONARIOS":
                    return GetRelatorioFuncionarios();
                case "CLIENTES":
                    return GetRelatorioClientes();
                default:
                    return null;
            }
        }
    ' VB.NET
    Public Function GetValue(ByVal id As String) As Http.HttpResponseMessage
        Select Case id.ToUpper()
            Case "FUNCIONARIOS"
                Return GetRelatorioFuncionarios()
            Case "CLIENTES"
                Return GetRelatorioClientes()
            Case Else
                Return Nothing
        End Select
    End Function

Acessando a Web API em outros projetos

Uma vez tendo a API em execução, podemos executá-la de qualquer tipo de aplicativo que suporte requisições HTTP. Por exemplo, em uma Console Application, poderíamos recuperar o PDF e exibi-lo na ferramenta padrão através deste código:

            // C#
            var request = System.Net.WebRequest.Create("http://localhost:34321/api/Relatorio");
            var response = request.GetResponse();
            using (var fileStream = System.IO.File.Create("arquivo.pdf"))
            {
                response.GetResponseStream().CopyTo(fileStream);
            }
            System.Diagnostics.Process.Start("arquivo.pdf");
        ' VB.NET
        Dim Request = System.Net.WebRequest.Create("http://localhost:34321/api/Relatorio")
        Dim Response = Request.GetResponse()
        Using fileStream = System.IO.File.Create("arquivo.pdf")
            Response.GetResponseStream().CopyTo(fileStream)
        End Using
        System.Diagnostics.Process.Start("arquivo.pdf")

Concluindo

A criação de Web APIs para expormos PDFs dos nossos relatórios do Report Viewer é um artifício que podemos utilizar para termos acesso aos nossos relatórios através de aplicações que não suportem o controle do Report Viewer (como aplicações mobile). Neste artigo você aprendeu a montar rapidamente uma Web API expondo o PDF de um relatório do Report Viewer. Vimos também como podemos acessar essa Web API através de uma Console Application, onde abrimos o PDF gerado pela API.

E você, já teve a necessidade de gerar um relatório do Report Viewer numa plataforma não suportada? Como você acabou resolvendo essa necessidade? Através de uma Web API, algum outro tipo de serviço ou alguma outra alternativa? Conte-nos mais detalhes na caixa de comentários!

Por fim, convido você a inscrever-se na minha newsletter. Ao fazer isso, você receberá um e-mail toda semana sobre o artigo publicado e ficará sabendo também em primeira mão sobre o artigo da próxima semana, além de receber dicas “bônus” que eu só compartilho por e-mail. Inscreva-se utilizando o formulário logo abaixo.

Até a próxima!

André Lima

Image by Pixabay used under Creative Commons
https://pixabay.com/en/document-agreement-documents-sign-428331/

Newsletter do André Lima

* indicates required



Powered by MailChimp

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *