Como conectar o Aplicativo MAUI no Emulador Android e WEB Api na mesma máquina
Tags: maui, dotnet, mobile, csharp
Published: 2023-10-14 19:59:11

Olá devs, vocês que já estiveram na situação de precisar atualizar o backend direto no servidor para fazer com uma atualização especifica de comportamento de endpoint? E se eu te mostrar que você não precisa fazer isso no seu dia a dia caso você tenha na mesma solução (ou até em diferentes) o aplicativo MAUI e a WEB API? Então, aqui vamos nós. Detalhe: essa dica funciona até para Blazor-MAUI.

## Maquina Virtual e Hospedeira (Host)

Primeiro, vamos entender como funciona de forma simplificadíssima uma maquina virtual e o sistema hospedeiro (Host). Nós temos aqui duas entidades, a maquina **HOST** que é basicamente o seu computador, e a maquina virtual, que nesse caso é a maquina que se comunicará com os recursos de hardware do Host via software. O Emulador Android é simplesmente uma maquina virtual que está consumindo recursos de hardware do HOST via software.

## Recursos de redes Emulador Android
Agora, precisamos entender como funciona os recursos de rede do emulador android que está rodando na sua maquina. Como Apresentado na [documentação oficial do Google](https://developer.android.com/studio/run/emulator-networking):

| Network Address|Description |
|----------------|------------|
| 10.0.2.1 |	Router or gateway address |
| 10.0.2.2 |	Special alias to your host loopback interface (127.0.0.1 on your development machine) |
| 10.0.2.3 |	First DNS server |
| 10.0.2.4 / 10.0.2.5 / 10.0.2.6	| Optional second, third, and fourth DNS servers |
| 10.0.2.15 |	The emulated device network or ethernet interface |
| 127.0.0.1 |	The emulated device loopback interface |

O que nos interessa aqui endereço especial `10.0.2.2`, eles nos permite se comunicar diretamente com o endereço `127.0.0.1` da maquina HOST (O famoso e famigerado `localhost`).

## Desenvolvendo para ASPNET e acessando no emulador
Como bem sabemos, quando iniciamos um [aplicativo web para AspNet Core](https://learn.microsoft.com/pt-br/aspnet/core/getting-started/?view=aspnetcore-7.0&tabs=windows), ele sempre rodará na nossa **localhost (127.0.0.1)**. Agora tá fácil né, quando você quiser acessar localhost da sua maquina direto do emulador é só usar o `10.0.2.2`.

Criei um simples projeto padrão de web api

![Exemplo de um aplicativo aspnet core rodando na maquina host com o endereço https://localhost:7270. Aqui temos apenas uma mensagem de boas vindas que diz 'Bem vindo'](
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7i4vgwxsgke13hp2yf79.png)

Agora vamos acessar o mesmo endereço do meu host no navegador chrome do Emulador Android, observe que eu usei o endereço `https://10.0.2.2:7270` que corresponde ao meu `https://localhost:7270` da minha maquina host.

![Emulador com o chrome aberto no endereço https://10.0.2.2:7270, porém com erro de certificado, na tela tem um desenho de atenção com a mensagem escrita 'Your Connection is not private' com o botão 'Advanced' em baixo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2z0vuw0wg2bjk02s7944.png)

Note que ao acessar esse endereço, você verá um erro de 'Your Connection is not private', isso acontece porque eu estou rodando na minha maquina host uma aplicação que usa _https_ e o seu ceritificado não é compartilhado com o emulador. Então, para não adentrar muito nesse mundo do SSL e HTTPS, vamos somente ignorar essa parte clicando em `Advanced` e depois em `Proceed to 10.0.2.2 (unsafe)`.


![Navegador no endereço 10.0.2.2:7270, onde mostra a mensagem Proceed to 10.0.2.2 (unsafe) onde o usuário deve clicar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0gpsiyrw6496p77fwwt4.png)

Após clicar em `Proceed to 10.0.2.2 (unsafe)`, nossa imagem é finalmente exibida. Então voilá, você tem o seu emulador acessando o seu endereço de api direto da web api rodando no host.


![Emulador acessando o endereço 10.0.2.2:7270 que exibe uma mensagem "Bem vindo"](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2nhcb06hne54boma4fgc.png)

Agora vamos para a próxima etapa

## Executando os dois projetos simuntaneamente pelo visual studio

Essa etapa é bem simples, precisamos apenas configurar a solução para executar os dois projetos simuntaneamente. Tenha em mente que você precisa está rodando o seu aplicativo android no emulador. Primeiro vamos definir que quando rodar a solução, o projeto que será iniciado é o projeto **MAUI**

Vamos fazer essa pequena configuração.
- Selecione o projeto do **MAUI**
- Botão direito do Mouse em cima desse projeto
- Então selecione a opção "Set as Startup Project"

![bortão direito no projeto do MAUI e selecione a optão "Set as Startup Project"](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e7yfo1kqpb0d48ny9i1w.png)

Agora selecione o emulador que você irá executar o MAUI
- Clique na setinha ao lado do botão de executar
- Selecione Android Emulators
- Selecione o seu emulador

![Selecionando o emulador mostrando exatamente os textos acima](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cui3fmw9bylwwasqvd8j.png)

Agora vamos ajustar a solução para executar o Projeto de API e Projeto do MAUI para executarem simuntaneamente 

- Botão direito na solução
- Selecione `Properties`
- Selecione a opção `Multiple startup projects`
- Marque os dois projetos com a action `Start` como na imagem abaixo
- Clique no botão `OK`

![Mostrando as propriedades da solution, marcando o checkbox "Multiple startup projects", e na table abaixo marcando as opções MAUIExemple com a action Start, da mesma forma o projecto WebApiExample com a action start](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fiwg7i8zcvbr9r6ftgly.png)

Ao fazer isso, já temos nossa configuração ideal para o nosso proximo passo. Observe que o botão de executar do visual studio mudou para somente `Start`


## Adicionando a chamada do servidor no Android
aqui vamos adicionar alguns trexos de códigos bem simples no nosso aquivo `MainPage.xaml` e `MainPage.xaml.cs`

- MainPage.xaml
```xml
<ContentPage ...>
    <ScrollView>
        <VerticalStackLayout ...>
...
<!-- Adicione esse botão ao final do layout, mas antes do </VerticalStackLayout></ScrollView></ContentPage>  -->
            <Button
                x:Name="ShowWelcomeButton"
                Text="Show welcome message"
                SemanticProperties.Hint="show exacly the message welcome from the ApI"
                Clicked="ShowWelcomeButton_Clicked"
                HorizontalOptions="Center" />

<!-- somente o botão acima -->
...
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>
```

- MainPage.xaml.cs 
```csharp
/* Agora vamos adicionar essa implementação */
        private async void ShowWelcomeButton_Clicked(object sender, EventArgs e)
        {
            try
            {
// cria um httpclient que ja conecta com a nossa api 
// usando o endereço https://10.0.2.2:7270
                using var httpClient = new HttpClient()
                {
                    BaseAddress = new Uri("https://10.0.2.2:7270")
                };
// chama o endpoint inicial "/"
                var message = await httpClient.GetStringAsync("/");

                ShowWelcomeButton.Text = $"Message From Server: {message}.";
            }
            catch(Exception ex)
            {
               await DisplayAlert("error", ex.Message, "Ok");
            }
        }
```

Seu App deve rodar no emulador como na imagem abaixo.
![emulador android com os botão "Show Welcome message" abaixo dos botões padrões do projeto padrão do maui](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/httssmuyk4jiq22kyuy5.png)

Agora é só clicar no botão "Show welcome message" e... **ERROR?!**

![Alerta mostrando "java.security.cert.CertPathValidatorException: Trust anchor for certification path not found." no aplicativo android após clicar no botão "Show welcome message"](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wvwd16xu5rpvt0qdh812.png)

Esse erro ocorreu devido a um erro de certificado, lembra daquele erro do chrome que ele estava dando em relação ao certificado inválido, a página não era segura e precisaríamos "forçar" o acesso a ela. Então, aqui no app android acontece a mesma coisa. Então vamos mexer em algumas configurações do nosso aplicativo Android e API.



## Resolvendo o java.security.cert.CertPathValidatorException

Como dito antes, esse erro de certificado ocorre devido ao acesso a um endereço https onde o certificado não é encontrado ou não é válido. Então nesse momento precisaremos configurar a API para não exigir o certificado quando estivemos trabalhando em desenvolvimento. Para isso precisaremos ajustar o arquivo `program.cs` da nossa API. Para isso basta verificar se o ambiente que estamos desenvolvendo é o desenvolvimento e então desativar o redirecionamento para o https.

- program.cs (WebApiExample)
```csharp

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseHttpsRedirection();
}

```

Precisamos também mudar a maneira que nossa Api irá rodar, então selecione nossa API como projeto a ser incializado 
- botão direito na API (WebApiExample)
- Selecione Set as Startup Project
- Mude na seleção de start para o 'http'. 
- Pronto, agora nossa API estará rodando na `http://localhost:5017/`

Então na sua mente deve está passando "Ah, agora é so mudar o endereço para http e rodar o projeto!" (lembresse de reconfigurar para rodar os dois Apps juntos)

```csharp
                using var httpClient = new HttpClient()
                {
                    BaseAddress = new Uri("http://10.0.2.2:5017")
                };
```
e então... **ERROR?!** `Cleartext HTTP traffic to 10.0.2.2 not permitted`. Simplesmente o Android não permite acessar endereços HTTP por **padrão**.


![Mostrando um alerta onde exibe a mensagem no emulador android "Cleartext HTTP traffic to 10.0.2.2 not permitted"](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qtslvpxk7sr9pdvmnrd5.png)

Não se desespere, temos a solução para isso logo a seguir

## Resolvendo o Cleartext HTTP traffic to 10.0.2.2 not permitted


A gente precisa contornar esse erro atraves da classe `MainApplication.cs, precisaremos adicionar a propriedade `Debuggable` e `UsesCleartextTraffic`. dessa maneira conseguiremos contornar a verificação https. Então acesse o arquivo "Platforms/Android/MainApplication.cs"


![Exibindo as pastas da solução com o endereço "Platforms/Android/MainApplication.cs"](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ymil8aggkggdkk63a0tr.png)


Adicione as propriedades `Debuggable` e `UsesCleartextTraffic` no atributo `[Application]` ele deverá ficar assim:

```

    [Application(Debuggable = true, UsesCleartextTraffic = true)]

```

e assim ficará o código da `MainApplication.cs`.


```
    [Application(Debuggable = true, UsesCleartextTraffic = true)]
    public class MainApplication : MauiApplication
    {
        public MainApplication(IntPtr handle, JniHandleOwnership ownership)
            : base(handle, ownership)
        {
        }

        protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
       }

```

Ao executar novamente... SEM ERROS! 

![Exibindo o emulador android com o botão funcionando mostrando a mensagem "Message from server: bem vindo!"](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t32xzh2nfsah7h1rufrf.png)

## Considerações finais e sugestões

### Publicação em produção 

Quando estiver executando o aplicativo em desenvolvimento, faz sentido você definir esse atributo `[Application(Debuggable = true, UsesCleartextTraffic = true)]` na classe `MainApplication.cs`. Porém, o ideal é seu app não ser publicado na loja com isso, e for, provavelmente vai ser rejeitado por essa falha de segurança que você deixou. Para evitar isso, a gente pode usar as variaveis de Build `DEBUG` onde você consegue definir o codigo que irá somente executado quando você estiver rodando pelo visual studio ou somente rodando na maquina de desenvolvimento.

```

    #if DEBUG
      [Application(Debuggable = true, UsesCleartextTraffic = true)]
    #else
      [Application]
    #endif
      public class MainApplication : MauiApplication
    {
        public MainApplication(IntPtr handle, JniHandleOwnership ownership)
            : base(handle, ownership)
        {
        }

        protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
       }
    }
```

### E rodando para o windows?
O endereço de api que voce colocou na sua HttpClient so faz sentido  para rodar no android. Porém, MAUI é multiplataforma. Felizmente MAUI também trás outras variaveis de build que identificam a plataforma que o App esta executando. Então usaremos a `ANDROID` para esse caso. Ou seja, sempre que estivemos executando no Android um trexo de código espefico irá executar

```csharp
                using var httpClient = new HttpClient()
                {
                #if ANDROID
                    BaseAddress = new Uri("http://10.0.2.2:5017")
                #else
                    BaseAddress = new Uri("http://localhost:5017")
                #endif
                };
```
## Obrigado!

Obrigado por ter chegado até aqui. Aqui está o código fonte dessas [aplicação no Github](https://github.com/MarcosCostaDev/ConnectMAUI-WebApi). Commente aqui se você tiver dúvidas ou quiser compartilhar informações sobre o desenvolvimento no MAUI!