How to create a RESTful API with asynchronous processing and response using C # + Angular (v5 +)

6

Hello, I'm working with C # and I have the following problem:

Imagine that I have a route for my API api/estoque/inventario where this route should calculate the inventory of the entire inventory of the application and on demand return the result. To mount this API route, I would have something similar to this:

namespace Api
{
  public class Estoque { ... }

  [Route("api/estoque/inventario")]
  [ApiController]
  public class EstoqueController: ControllerBase
  {
    [HttpGet]
    public async Task<IActionResult<ICollection<Estoque>>> GetInventario()
    {
      try
      {
        // pega os produtos
        // faz todas as regras de negócio (síncronas e assíncronas)
        // retorna o estoque com o inventário (como?)
      }
      catch(Exception ex)
      {
        throw;
      }
    }
  }

}

The problem is that even though I have an asynchronous task in my API, the response to my client is not asynchronous. On the Customer side, I use the RxJS library along with Angular and the service that makes the requests to the API has the return of an observable object, Thus:

export const API_URL = '...';

export interface IEstoque { ... }

@Injectable({
  providedIn: 'root',
})
export class EstoqueService {

  private baseUrl: string = "/api/estoque";

  constructor(public http: HttpClient) { }

  public getInventario(): Observable<IEstoque[]> {
    return this.http.get('${API_URL}${this.baseUrl}/inventario');
  }

}

My intention is to use the list of items processed in stock together with the AsyncPipe pipe that exists in the angle, which makes with which I can loop asynchronously based on the return of the service, thus:

<div *ngFor='let estoque of estoqueList | async'>
  {{estoque | json}}
</div>
@Component({
  selector: 'estoque-list',
  templateUrl: 'estoque-list.component.html',
})
export class EstoqueList {

  public estoqueList: Observable<IEstoque[]>;

  constructor(public estoqueService: EstoqueService) {
    this.estoqueList = estoqueService.getInventario();
  }

}

What do I need to do to have my api/estoque/inventario route send the inventory list asynchronously and continuously to my frontend based on processing?

    
asked by anonymous 04.09.2018 / 15:35

1 answer

2

Leandro, your API signature is asynchronous. This means that in this part (signature of the method) it is correct. But let's review some things here that may be interfering with what you want.

1 - All flow must be asynchronous.

When working synchronously, everything must be synchronous. Also, when working asynchronously, everything should be asynchronous. This is because mixing behaviors brings unwanted results. Therefore, make sure that all code, data fetching flow and all processing are asynchronous, not just the signature of your method. PS: Playing everything inside Task.Factory.StartNew is not a path ...

2 - The async keyword in your API does not make HTTP asynchronous.

Remember that HTTP itself is not asynchronous. That is, your client (JavaScript) will always wait for the response from the server and even if your method in the API is asynchronous, its interface will not necessarily be. Using async in the method signature is related to request / response internals, call-in and I / O thread allocation, and not to allow your interface to be more responsive. Basically, using async makes your API lighter and more scalable, especially when it needs to "follow" for slower tasks (like fetching data from a database).

This means that the client (JavaScript) will wait for all processing to finally receive the response. The difference is that as long as the client waits, the I / O thread that would be locked waiting for the response (and this thread belongs to a group with finite amount) will not be locked and can be used for other requests (faster, for example ). But the client will remain waiting until all processing is complete and the result is returned by its API.

3 - So how to improve the experience?

There are several methods and there is no definitive answer because each scenario has a different output. In some cases, a "loading" screen is enough. In others it may be necessary to use SignalR or SocketIO to send data as it becomes available. In other scenarios you can create a processing task and return a kind of ID that you can check later (from time to time) to see if it has already been completed. It all depends on what exactly you want, the project (and customer) requirements and the expected total processing time. Just as it is intolerable to leave a "loading ..." locked for 4 hours to process the data, it is unrealistic to create an entire SignalR structure for a task that takes 1 second to process.     

24.12.2018 / 12:47