Tutorial: chamar uma API Web do ASP.NET Core com o JavaScript

De Rick Anderson

Este tutorial mostra como chamar uma API Web do ASP.NET Core com JavaScript, usando a API Fetch.

Pré-requisitos

Chamar a API Web com o JavaScript

Nesta seção, você adicionará uma página HTML que contém formulários para criar e gerenciar itens de tarefas pendentes. Manipuladores de eventos são anexados aos elementos na página. Os manipuladores de eventos resultam em solicitações HTTP para os métodos de ação da API Web. A função fetch da API Fetch inicia cada solicitação HTTP.

A função fetch retorna um objeto Promise, que contém uma resposta HTTP representada como um objeto Response. Um padrão comum é extrair o corpo da resposta JSON ao invocar a função json no objeto Response. O JavaScript atualiza a página com os detalhes da resposta da API Web.

A chamada fetch mais simples aceita um parâmetro único que representa a rota. Um segundo parâmetro, conhecido como objeto init, é opcional. init é usado para configurar a solicitação HTTP.

  1. Configure o aplicativo para fornecer arquivos estáticos e habilitar o mapeamento de arquivo padrão. O código realçado a seguir é necessário em Program.cs:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
    opt.UseInMemoryDatabase("TodoList"));

var app = builder.Build();

if (builder.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.UseDefaultFiles();
app.UseStaticFiles();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
  1. Crie uma pasta wwwroot na raiz do projeto.

  2. Crie uma pasta css dentro da pasta wwwroot.

  3. Crie uma pasta js dentro da pasta wwwroot.

  4. Adicione um arquivo HTML chamado index.html à pasta wwwroot. Substitua o conteúdo de index.html pela marcação a seguir:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>To-do CRUD</title>
        <link rel="stylesheet" href="css/site.css" />
    </head>
    <body>
        <h1>To-do CRUD</h1>
        <h3>Add</h3>
        <form action="javascript:void(0);" method="POST" onsubmit="addItem()">
            <input type="text" id="add-name" placeholder="New to-do">
            <input type="submit" value="Add">
        </form>
    
        <div id="editForm">
            <h3>Edit</h3>
            <form action="javascript:void(0);" onsubmit="updateItem()">
                <input type="hidden" id="edit-id">
                <input type="checkbox" id="edit-isComplete">
                <input type="text" id="edit-name">
                <input type="submit" value="Save">
                <a onclick="closeInput()" aria-label="Close">&#10006;</a>
            </form>
        </div>
    
        <p id="counter"></p>
    
        <table>
            <tr>
                <th>Is Complete?</th>
                <th>Name</th>
                <th></th>
                <th></th>
            </tr>
            <tbody id="todos"></tbody>
        </table>
    
        <script src="js/site.js" asp-append-version="true"></script>
        <script type="text/javascript">
            getItems();
        </script>
    </body>
    </html>
    
  5. Adicione um arquivo CSS chamado site.css à pasta wwwroot/css. Substitua o conteúdo de site.css pelos estilos a seguir:

    input[type='submit'], button, [aria-label] {
        cursor: pointer;
    }
    
    #editForm {
        display: none;
    }
    
    table {
        font-family: Arial, sans-serif;
        border: 1px solid;
        border-collapse: collapse;
    }
    
    th {
        background-color: #f8f8f8;
        padding: 5px;
    }
    
    td {
        border: 1px solid;
        padding: 5px;
    }
    
  6. Adicione um arquivo JavaScript chamado site.js à pasta wwwroot/js. Substitua o conteúdo de site.js pelo seguinte código:

    const uri = 'api/todoitems';
    let todos = [];
    
    function getItems() {
      fetch(uri)
        .then(response => response.json())
        .then(data => _displayItems(data))
        .catch(error => console.error('Unable to get items.', error));
    }
    
    function addItem() {
      const addNameTextbox = document.getElementById('add-name');
    
      const item = {
        isComplete: false,
        name: addNameTextbox.value.trim()
      };
    
      fetch(uri, {
        method: 'POST',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(item)
      })
        .then(response => response.json())
        .then(() => {
          getItems();
          addNameTextbox.value = '';
        })
        .catch(error => console.error('Unable to add item.', error));
    }
    
    function deleteItem(id) {
      fetch(`${uri}/${id}`, {
        method: 'DELETE'
      })
      .then(() => getItems())
      .catch(error => console.error('Unable to delete item.', error));
    }
    
    function displayEditForm(id) {
      const item = todos.find(item => item.id === id);
      
      document.getElementById('edit-name').value = item.name;
      document.getElementById('edit-id').value = item.id;
      document.getElementById('edit-isComplete').checked = item.isComplete;
      document.getElementById('editForm').style.display = 'block';
    }
    
    function updateItem() {
      const itemId = document.getElementById('edit-id').value;
      const item = {
        id: parseInt(itemId, 10),
        isComplete: document.getElementById('edit-isComplete').checked,
        name: document.getElementById('edit-name').value.trim()
      };
    
      fetch(`${uri}/${itemId}`, {
        method: 'PUT',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(item)
      })
      .then(() => getItems())
      .catch(error => console.error('Unable to update item.', error));
    
      closeInput();
    
      return false;
    }
    
    function closeInput() {
      document.getElementById('editForm').style.display = 'none';
    }
    
    function _displayCount(itemCount) {
      const name = (itemCount === 1) ? 'to-do' : 'to-dos';
    
      document.getElementById('counter').innerText = `${itemCount} ${name}`;
    }
    
    function _displayItems(data) {
      const tBody = document.getElementById('todos');
      tBody.innerHTML = '';
    
      _displayCount(data.length);
    
      const button = document.createElement('button');
    
      data.forEach(item => {
        let isCompleteCheckbox = document.createElement('input');
        isCompleteCheckbox.type = 'checkbox';
        isCompleteCheckbox.disabled = true;
        isCompleteCheckbox.checked = item.isComplete;
    
        let editButton = button.cloneNode(false);
        editButton.innerText = 'Edit';
        editButton.setAttribute('onclick', `displayEditForm(${item.id})`);
    
        let deleteButton = button.cloneNode(false);
        deleteButton.innerText = 'Delete';
        deleteButton.setAttribute('onclick', `deleteItem(${item.id})`);
    
        let tr = tBody.insertRow();
        
        let td1 = tr.insertCell(0);
        td1.appendChild(isCompleteCheckbox);
    
        let td2 = tr.insertCell(1);
        let textNode = document.createTextNode(item.name);
        td2.appendChild(textNode);
    
        let td3 = tr.insertCell(2);
        td3.appendChild(editButton);
    
        let td4 = tr.insertCell(3);
        td4.appendChild(deleteButton);
      });
    
      todos = data;
    }
    

Uma alteração nas configurações de inicialização do projeto ASP.NET Core pode ser necessária para testar a página HTML localmente:

  1. Abra Properties\launchSettings.json.
  2. Remova a propriedade launchUrl para forçar o aplicativo a abrir em index.html – o arquivo padrão do projeto.

Esta amostra chama todos os métodos CRUD da API Web. A seguir, são apresentadas explicações sobre as solicitações de API Web.

Obter uma lista de itens pendentes

No código a seguir, uma solicitação HTTP GET é enviada para a rota de api/todoitems:

fetch(uri)
  .then(response => response.json())
  .then(data => _displayItems(data))
  .catch(error => console.error('Unable to get items.', error));

Quando a API Web retorna um código de status bem-sucedido, a função _displayItems é invocada. Cada item de tarefas pendentes no parâmetro de matriz aceito por _displayItems é adicionado a uma tabela com os botões Editar e Excluir. Se a solicitação da API Web falhar, um erro será registrado no console do navegador.

Adicionar um item pendente

No seguinte código:

  • Uma variável item é declarada para construir uma representação literal de objeto do item de tarefas pendentes.
  • Uma solicitação Fetch é configurada com as seguintes opções:
    • method – especifica o verbo de ação HTTP POST.
    • body – especifica a representação JSON do corpo da solicitação. O JSON é produzido passando o literal de objeto armazenado em item para a função JSON.stringify.
    • headers – especifica os cabeçalhos de solicitação HTTP Accept e Content-Type. Ambos os cabeçalhos são definidos como application/json para especificar o tipo de mídia que está sendo recebido e enviado, respectivamente.
  • Uma solicitação HTTP POST é enviada para a rota api/todoitems.
function addItem() {
  const addNameTextbox = document.getElementById('add-name');

  const item = {
    isComplete: false,
    name: addNameTextbox.value.trim()
  };

  fetch(uri, {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(item)
  })
    .then(response => response.json())
    .then(() => {
      getItems();
      addNameTextbox.value = '';
    })
    .catch(error => console.error('Unable to add item.', error));
}

Quando a API Web retorna um código de status de êxito, a função getItems é invocada para atualizar a tabela HTML. Se a solicitação da API Web falhar, um erro será registrado no console do navegador.

Atualizar um item pendente

A atualização de um item de tarefas pendentes é semelhante à adição de um; no entanto, há duas diferenças significativas:

  • A rota é sufixada com o identificador exclusivo do item a ser atualizado. Por exemplo, api/todoitems/1.
  • O verbo de ação HTTP é PUT, conforme indicado pela opção method.
fetch(`${uri}/${itemId}`, {
  method: 'PUT',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(item)
})
.then(() => getItems())
.catch(error => console.error('Unable to update item.', error));

Excluir um item pendente

Para excluir um item de tarefas pendentes, defina a opção method da solicitação como DELETE e especifique o identificador exclusivo do item na URL.

fetch(`${uri}/${id}`, {
  method: 'DELETE'
})
.then(() => getItems())
.catch(error => console.error('Unable to delete item.', error));

Avance para o próximo tutorial para saber como gerar páginas de ajuda da API Web:

Este tutorial mostra como chamar uma API Web do ASP.NET Core com JavaScript, usando a API Fetch.

Pré-requisitos

Chamar a API Web com o JavaScript

Nesta seção, você adicionará uma página HTML que contém formulários para criar e gerenciar itens de tarefas pendentes. Manipuladores de eventos são anexados aos elementos na página. Os manipuladores de eventos resultam em solicitações HTTP para os métodos de ação da API Web. A função fetch da API Fetch inicia cada solicitação HTTP.

A função fetch retorna um objeto Promise, que contém uma resposta HTTP representada como um objeto Response. Um padrão comum é extrair o corpo da resposta JSON ao invocar a função json no objeto Response. O JavaScript atualiza a página com os detalhes da resposta da API Web.

A chamada fetch mais simples aceita um parâmetro único que representa a rota. Um segundo parâmetro, conhecido como objeto init, é opcional. init é usado para configurar a solicitação HTTP.

  1. Configure o aplicativo para fornecer arquivos estáticos e habilitar o mapeamento de arquivo padrão. O seguinte código destacado é necessário no método Configure de Startup.cs:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseDefaultFiles();
        app.UseStaticFiles();
    
        app.UseHttpsRedirection();
    
        app.UseRouting();
    
        app.UseAuthorization();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
    
  2. Crie uma pasta wwwroot na raiz do projeto.

  3. Crie uma pasta css dentro da pasta wwwroot.

  4. Crie uma pasta js dentro da pasta wwwroot.

  5. Adicione um arquivo HTML chamado index.html à pasta wwwroot. Substitua o conteúdo de index.html pela marcação a seguir:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>To-do CRUD</title>
        <link rel="stylesheet" href="css/site.css" />
    </head>
    <body>
        <h1>To-do CRUD</h1>
        <h3>Add</h3>
        <form action="javascript:void(0);" method="POST" onsubmit="addItem()">
            <input type="text" id="add-name" placeholder="New to-do">
            <input type="submit" value="Add">
        </form>
    
        <div id="editForm">
            <h3>Edit</h3>
            <form action="javascript:void(0);" onsubmit="updateItem()">
                <input type="hidden" id="edit-id">
                <input type="checkbox" id="edit-isComplete">
                <input type="text" id="edit-name">
                <input type="submit" value="Save">
                <a onclick="closeInput()" aria-label="Close">&#10006;</a>
            </form>
        </div>
    
        <p id="counter"></p>
    
        <table>
            <tr>
                <th>Is Complete?</th>
                <th>Name</th>
                <th></th>
                <th></th>
            </tr>
            <tbody id="todos"></tbody>
        </table>
    
        <script src="js/site.js" asp-append-version="true"></script>
        <script type="text/javascript">
            getItems();
        </script>
    </body>
    </html>
    
  6. Adicione um arquivo CSS chamado site.css à pasta wwwroot/css. Substitua o conteúdo de site.css pelos estilos a seguir:

    input[type='submit'], button, [aria-label] {
        cursor: pointer;
    }
    
    #editForm {
        display: none;
    }
    
    table {
        font-family: Arial, sans-serif;
        border: 1px solid;
        border-collapse: collapse;
    }
    
    th {
        background-color: #f8f8f8;
        padding: 5px;
    }
    
    td {
        border: 1px solid;
        padding: 5px;
    }
    
  7. Adicione um arquivo JavaScript chamado site.js à pasta wwwroot/js. Substitua o conteúdo de site.js pelo seguinte código:

    const uri = 'api/todoitems';
    let todos = [];
    
    function getItems() {
      fetch(uri)
        .then(response => response.json())
        .then(data => _displayItems(data))
        .catch(error => console.error('Unable to get items.', error));
    }
    
    function addItem() {
      const addNameTextbox = document.getElementById('add-name');
    
      const item = {
        isComplete: false,
        name: addNameTextbox.value.trim()
      };
    
      fetch(uri, {
        method: 'POST',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(item)
      })
        .then(response => response.json())
        .then(() => {
          getItems();
          addNameTextbox.value = '';
        })
        .catch(error => console.error('Unable to add item.', error));
    }
    
    function deleteItem(id) {
      fetch(`${uri}/${id}`, {
        method: 'DELETE'
      })
      .then(() => getItems())
      .catch(error => console.error('Unable to delete item.', error));
    }
    
    function displayEditForm(id) {
      const item = todos.find(item => item.id === id);
      
      document.getElementById('edit-name').value = item.name;
      document.getElementById('edit-id').value = item.id;
      document.getElementById('edit-isComplete').checked = item.isComplete;
      document.getElementById('editForm').style.display = 'block';
    }
    
    function updateItem() {
      const itemId = document.getElementById('edit-id').value;
      const item = {
        id: parseInt(itemId, 10),
        isComplete: document.getElementById('edit-isComplete').checked,
        name: document.getElementById('edit-name').value.trim()
      };
    
      fetch(`${uri}/${itemId}`, {
        method: 'PUT',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(item)
      })
      .then(() => getItems())
      .catch(error => console.error('Unable to update item.', error));
    
      closeInput();
    
      return false;
    }
    
    function closeInput() {
      document.getElementById('editForm').style.display = 'none';
    }
    
    function _displayCount(itemCount) {
      const name = (itemCount === 1) ? 'to-do' : 'to-dos';
    
      document.getElementById('counter').innerText = `${itemCount} ${name}`;
    }
    
    function _displayItems(data) {
      const tBody = document.getElementById('todos');
      tBody.innerHTML = '';
    
      _displayCount(data.length);
    
      const button = document.createElement('button');
    
      data.forEach(item => {
        let isCompleteCheckbox = document.createElement('input');
        isCompleteCheckbox.type = 'checkbox';
        isCompleteCheckbox.disabled = true;
        isCompleteCheckbox.checked = item.isComplete;
    
        let editButton = button.cloneNode(false);
        editButton.innerText = 'Edit';
        editButton.setAttribute('onclick', `displayEditForm(${item.id})`);
    
        let deleteButton = button.cloneNode(false);
        deleteButton.innerText = 'Delete';
        deleteButton.setAttribute('onclick', `deleteItem(${item.id})`);
    
        let tr = tBody.insertRow();
        
        let td1 = tr.insertCell(0);
        td1.appendChild(isCompleteCheckbox);
    
        let td2 = tr.insertCell(1);
        let textNode = document.createTextNode(item.name);
        td2.appendChild(textNode);
    
        let td3 = tr.insertCell(2);
        td3.appendChild(editButton);
    
        let td4 = tr.insertCell(3);
        td4.appendChild(deleteButton);
      });
    
      todos = data;
    }
    

Uma alteração nas configurações de inicialização do projeto ASP.NET Core pode ser necessária para testar a página HTML localmente:

  1. Abra Properties\launchSettings.json.
  2. Remova a propriedade launchUrl para forçar o aplicativo a abrir em index.html – o arquivo padrão do projeto.

Esta amostra chama todos os métodos CRUD da API Web. A seguir, são apresentadas explicações sobre as solicitações de API Web.

Obter uma lista de itens pendentes

No código a seguir, uma solicitação HTTP GET é enviada para a rota de api/todoitems:

fetch(uri)
  .then(response => response.json())
  .then(data => _displayItems(data))
  .catch(error => console.error('Unable to get items.', error));

Quando a API Web retorna um código de status bem-sucedido, a função _displayItems é invocada. Cada item de tarefas pendentes no parâmetro de matriz aceito por _displayItems é adicionado a uma tabela com os botões Editar e Excluir. Se a solicitação da API Web falhar, um erro será registrado no console do navegador.

Adicionar um item pendente

No seguinte código:

  • Uma variável item é declarada para construir uma representação literal de objeto do item de tarefas pendentes.
  • Uma solicitação Fetch é configurada com as seguintes opções:
    • method – especifica o verbo de ação HTTP POST.
    • body – especifica a representação JSON do corpo da solicitação. O JSON é produzido passando o literal de objeto armazenado em item para a função JSON.stringify.
    • headers – especifica os cabeçalhos de solicitação HTTP Accept e Content-Type. Ambos os cabeçalhos são definidos como application/json para especificar o tipo de mídia que está sendo recebido e enviado, respectivamente.
  • Uma solicitação HTTP POST é enviada para a rota api/todoitems.
function addItem() {
  const addNameTextbox = document.getElementById('add-name');

  const item = {
    isComplete: false,
    name: addNameTextbox.value.trim()
  };

  fetch(uri, {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(item)
  })
    .then(response => response.json())
    .then(() => {
      getItems();
      addNameTextbox.value = '';
    })
    .catch(error => console.error('Unable to add item.', error));
}

Quando a API Web retorna um código de status de êxito, a função getItems é invocada para atualizar a tabela HTML. Se a solicitação da API Web falhar, um erro será registrado no console do navegador.

Atualizar um item pendente

A atualização de um item de tarefas pendentes é semelhante à adição de um; no entanto, há duas diferenças significativas:

  • A rota é sufixada com o identificador exclusivo do item a ser atualizado. Por exemplo, api/todoitems/1.
  • O verbo de ação HTTP é PUT, conforme indicado pela opção method.
fetch(`${uri}/${itemId}`, {
  method: 'PUT',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(item)
})
.then(() => getItems())
.catch(error => console.error('Unable to update item.', error));

Excluir um item pendente

Para excluir um item de tarefas pendentes, defina a opção method da solicitação como DELETE e especifique o identificador exclusivo do item na URL.

fetch(`${uri}/${id}`, {
  method: 'DELETE'
})
.then(() => getItems())
.catch(error => console.error('Unable to delete item.', error));

Avance para o próximo tutorial para saber como gerar páginas de ajuda da API Web:

Este tutorial mostra como chamar uma API Web do ASP.NET Core com JavaScript, usando a API Fetch.

Pré-requisitos

Chamar a API Web com o JavaScript

Nesta seção, você adicionará uma página HTML que contém formulários para criar e gerenciar itens de tarefas pendentes. Manipuladores de eventos são anexados aos elementos na página. Os manipuladores de eventos resultam em solicitações HTTP para os métodos de ação da API Web. A função fetch da API Fetch inicia cada solicitação HTTP.

A função fetch retorna um objeto Promise, que contém uma resposta HTTP representada como um objeto Response. Um padrão comum é extrair o corpo da resposta JSON ao invocar a função json no objeto Response. O JavaScript atualiza a página com os detalhes da resposta da API Web.

A chamada fetch mais simples aceita um parâmetro único que representa a rota. Um segundo parâmetro, conhecido como objeto init, é opcional. init é usado para configurar a solicitação HTTP.

  1. Configure o aplicativo para fornecer arquivos estáticos e habilitar o mapeamento de arquivo padrão. O código realçado a seguir é necessário em Program.cs:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
    opt.UseInMemoryDatabase("TodoList"));

var app = builder.Build();

if (builder.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.UseDefaultFiles();
app.UseStaticFiles();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
  1. Crie uma pasta wwwroot na raiz do projeto.

  2. Crie uma pasta css dentro da pasta wwwroot.

  3. Crie uma pasta js dentro da pasta wwwroot.

  4. Adicione um arquivo HTML chamado index.html à pasta wwwroot. Substitua o conteúdo de index.html pela marcação a seguir:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>To-do CRUD</title>
        <link rel="stylesheet" href="css/site.css" />
    </head>
    <body>
        <h1>To-do CRUD</h1>
        <h3>Add</h3>
        <form action="javascript:void(0);" method="POST" onsubmit="addItem()">
            <input type="text" id="add-name" placeholder="New to-do">
            <input type="submit" value="Add">
        </form>
    
        <div id="editForm">
            <h3>Edit</h3>
            <form action="javascript:void(0);" onsubmit="updateItem()">
                <input type="hidden" id="edit-id">
                <input type="checkbox" id="edit-isComplete">
                <input type="text" id="edit-name">
                <input type="submit" value="Save">
                <a onclick="closeInput()" aria-label="Close">&#10006;</a>
            </form>
        </div>
    
        <p id="counter"></p>
    
        <table>
            <tr>
                <th>Is Complete?</th>
                <th>Name</th>
                <th></th>
                <th></th>
            </tr>
            <tbody id="todos"></tbody>
        </table>
    
        <script src="js/site.js" asp-append-version="true"></script>
        <script type="text/javascript">
            getItems();
        </script>
    </body>
    </html>
    
  5. Adicione um arquivo CSS chamado site.css à pasta wwwroot/css. Substitua o conteúdo de site.css pelos estilos a seguir:

    input[type='submit'], button, [aria-label] {
        cursor: pointer;
    }
    
    #editForm {
        display: none;
    }
    
    table {
        font-family: Arial, sans-serif;
        border: 1px solid;
        border-collapse: collapse;
    }
    
    th {
        background-color: #f8f8f8;
        padding: 5px;
    }
    
    td {
        border: 1px solid;
        padding: 5px;
    }
    
  6. Adicione um arquivo JavaScript chamado site.js à pasta wwwroot/js. Substitua o conteúdo de site.js pelo seguinte código:

    const uri = 'api/todoitems';
    let todos = [];
    
    function getItems() {
      fetch(uri)
        .then(response => response.json())
        .then(data => _displayItems(data))
        .catch(error => console.error('Unable to get items.', error));
    }
    
    function addItem() {
      const addNameTextbox = document.getElementById('add-name');
    
      const item = {
        isComplete: false,
        name: addNameTextbox.value.trim()
      };
    
      fetch(uri, {
        method: 'POST',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(item)
      })
        .then(response => response.json())
        .then(() => {
          getItems();
          addNameTextbox.value = '';
        })
        .catch(error => console.error('Unable to add item.', error));
    }
    
    function deleteItem(id) {
      fetch(`${uri}/${id}`, {
        method: 'DELETE'
      })
      .then(() => getItems())
      .catch(error => console.error('Unable to delete item.', error));
    }
    
    function displayEditForm(id) {
      const item = todos.find(item => item.id === id);
      
      document.getElementById('edit-name').value = item.name;
      document.getElementById('edit-id').value = item.id;
      document.getElementById('edit-isComplete').checked = item.isComplete;
      document.getElementById('editForm').style.display = 'block';
    }
    
    function updateItem() {
      const itemId = document.getElementById('edit-id').value;
      const item = {
        id: parseInt(itemId, 10),
        isComplete: document.getElementById('edit-isComplete').checked,
        name: document.getElementById('edit-name').value.trim()
      };
    
      fetch(`${uri}/${itemId}`, {
        method: 'PUT',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(item)
      })
      .then(() => getItems())
      .catch(error => console.error('Unable to update item.', error));
    
      closeInput();
    
      return false;
    }
    
    function closeInput() {
      document.getElementById('editForm').style.display = 'none';
    }
    
    function _displayCount(itemCount) {
      const name = (itemCount === 1) ? 'to-do' : 'to-dos';
    
      document.getElementById('counter').innerText = `${itemCount} ${name}`;
    }
    
    function _displayItems(data) {
      const tBody = document.getElementById('todos');
      tBody.innerHTML = '';
    
      _displayCount(data.length);
    
      const button = document.createElement('button');
    
      data.forEach(item => {
        let isCompleteCheckbox = document.createElement('input');
        isCompleteCheckbox.type = 'checkbox';
        isCompleteCheckbox.disabled = true;
        isCompleteCheckbox.checked = item.isComplete;
    
        let editButton = button.cloneNode(false);
        editButton.innerText = 'Edit';
        editButton.setAttribute('onclick', `displayEditForm(${item.id})`);
    
        let deleteButton = button.cloneNode(false);
        deleteButton.innerText = 'Delete';
        deleteButton.setAttribute('onclick', `deleteItem(${item.id})`);
    
        let tr = tBody.insertRow();
        
        let td1 = tr.insertCell(0);
        td1.appendChild(isCompleteCheckbox);
    
        let td2 = tr.insertCell(1);
        let textNode = document.createTextNode(item.name);
        td2.appendChild(textNode);
    
        let td3 = tr.insertCell(2);
        td3.appendChild(editButton);
    
        let td4 = tr.insertCell(3);
        td4.appendChild(deleteButton);
      });
    
      todos = data;
    }
    

Uma alteração nas configurações de inicialização do projeto ASP.NET Core pode ser necessária para testar a página HTML localmente:

  1. Abra Properties\launchSettings.json.
  2. Remova a propriedade launchUrl para forçar o aplicativo a abrir em index.html – o arquivo padrão do projeto.

Esta amostra chama todos os métodos CRUD da API Web. A seguir, são apresentadas explicações sobre as solicitações de API Web.

Obter uma lista de itens pendentes

No código a seguir, uma solicitação HTTP GET é enviada para a rota de api/todoitems:

fetch(uri)
  .then(response => response.json())
  .then(data => _displayItems(data))
  .catch(error => console.error('Unable to get items.', error));

Quando a API Web retorna um código de status bem-sucedido, a função _displayItems é invocada. Cada item de tarefas pendentes no parâmetro de matriz aceito por _displayItems é adicionado a uma tabela com os botões Editar e Excluir. Se a solicitação da API Web falhar, um erro será registrado no console do navegador.

Adicionar um item pendente

No seguinte código:

  • Uma variável item é declarada para construir uma representação literal de objeto do item de tarefas pendentes.
  • Uma solicitação Fetch é configurada com as seguintes opções:
    • method – especifica o verbo de ação HTTP POST.
    • body – especifica a representação JSON do corpo da solicitação. O JSON é produzido passando o literal de objeto armazenado em item para a função JSON.stringify.
    • headers – especifica os cabeçalhos de solicitação HTTP Accept e Content-Type. Ambos os cabeçalhos são definidos como application/json para especificar o tipo de mídia que está sendo recebido e enviado, respectivamente.
  • Uma solicitação HTTP POST é enviada para a rota api/todoitems.
function addItem() {
  const addNameTextbox = document.getElementById('add-name');

  const item = {
    isComplete: false,
    name: addNameTextbox.value.trim()
  };

  fetch(uri, {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(item)
  })
    .then(response => response.json())
    .then(() => {
      getItems();
      addNameTextbox.value = '';
    })
    .catch(error => console.error('Unable to add item.', error));
}

Quando a API Web retorna um código de status de êxito, a função getItems é invocada para atualizar a tabela HTML. Se a solicitação da API Web falhar, um erro será registrado no console do navegador.

Atualizar um item pendente

A atualização de um item de tarefas pendentes é semelhante à adição de um; no entanto, há duas diferenças significativas:

  • A rota é sufixada com o identificador exclusivo do item a ser atualizado. Por exemplo, api/todoitems/1.
  • O verbo de ação HTTP é PUT, conforme indicado pela opção method.
fetch(`${uri}/${itemId}`, {
  method: 'PUT',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(item)
})
.then(() => getItems())
.catch(error => console.error('Unable to update item.', error));

Excluir um item pendente

Para excluir um item de tarefas pendentes, defina a opção method da solicitação como DELETE e especifique o identificador exclusivo do item na URL.

fetch(`${uri}/${id}`, {
  method: 'DELETE'
})
.then(() => getItems())
.catch(error => console.error('Unable to delete item.', error));

Avance para o próximo tutorial para saber como gerar páginas de ajuda da API Web: