Search code examples
apiasp.net-corevisual-studio-2017dbcontextdto

How to access data from API in .Net Core


I've not worked with .Net Core before but have a lot of experience with MVC and Entity Framework. My project has four distinct folders, API, DTO, Repository and WEB. The DTO folder has many model files which fits the data model. The API folder has a Controller file called ReferenceDataController and looks like this

[Route("api/[controller]")]
[ApiController]
public class ReferenceDataController : ControllerBase
{
    private readonly IReferenceDataRepository _repository;

    public ReferenceDataController(IReferenceDataRepository repository)
    {
        _repository = repository;
    }

    // GET api/values
    [HttpGet]
    public ActionResult<ReferenceData> GetReferenceData()
    {
        return _repository.GetReferenceData();
    }

I'm told that if I call this GET method it will return a data object. How do I call this method in the API folder from my HomeController in my WEB folder?


Solution

  • First, in your web project, you need to do a little setup. Add a class like the following:

    public class ReferenceDataService
    {
        private readonly HttpClient _httpClient;
    
        public ReferenceDataService(HttpClient httpClient)
        {
            _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        }
    
        public async Task<List<ReferenceData>> GetReferenceDataAsync(CancellationToken cancellationToken = default)
        {
            using (var response = await _httpClient.GetAsync("/api/referencedata", cancellationToken))
            {
                if (response.IsSuccessStatusCode())
                {
                    return await response.Content.ReadAsAsync<List<ReferenceData>>();
                }
    
                return null;
            }
        }
    }
    

    Then, in ConfigureServices in Startup.cs:

    services.AddHttpClient<ReferenceDataService>(c =>
    {
        c.BaseAddress = new Uri("https://api.example.com");
        // Use the actual URL for your API here. You also probably want to get this
        // from `Configuration` rather than hard-coding it.
    });
    

    Finally, inject ReferenceDataService into your HomeController:

    public class HomeController : Controller
    {
        private readonly ReferenceDataService _referenceDataService;
    
        public HomeController(ReferenceDataService referenceDataService)
        {
            _referenceDataService = referenceDataService ?? throw new ArgumentNullException(nameof(referenceDataService));
        }
    
        // In your action(s):
        // var data = await _referenceDataService.GetReferenceDataAsync(HttpContext.RequestAborted);
    }
    

    This is the quick and dirty code here. Things you should consider for improvement:

    1. Use an interface for your service class(es), i.e. IReferenceDataService. That will make testing easier. In ConfigureServices:

      services.AddHttpClient<IReferenceDataService, ReferenceDataService>(...);
      

      Then, inject IReferenceDataService instead.

    2. You can and should use the Polly extensions with AddHttpClient to support retry and exception handling polices. At the very least, you'd definitely want to add AddTransientHttpErrorPolicy:

      services.AddHttpClient<ReferenceDataService>(...)
          .AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]
          {
              TimeSpan.FromSeconds(1),
              TimeSpan.FromSeconds(5),
              TimeSpan.FromSeconds(10)
          }));
      

      That will handle transient errors like temporarily being unable to connect to the API because it was restarted or something. You can find more info and more advanced configuration possibilities at the docs.

    3. You should be using separate DTO classes. For brevity, I just used your (presumed) entity class ReferenceData. Instead, you should always use customized DTO classes that hold just the pieces of the data that you need to be available via the API. This way, you can control things like serialization as well as custom validation schemes, without conflicting with what's going on with your entity class. Additionally, the web project would only need to know about ReferenceDataDTO (or whatever), meaning you can share a library with your DTOs between the API and web projects and keep your DAL completely out of your web project.