diff --git a/ApiService/ApiService.csproj b/ApiService/ApiService.csproj index d12c450..41a42b3 100644 --- a/ApiService/ApiService.csproj +++ b/ApiService/ApiService.csproj @@ -4,5 +4,10 @@ netcoreapp3.1 + + + + + diff --git a/ApiService/Controllers/RepositoryController.cs b/ApiService/Controllers/RepositoryController.cs new file mode 100644 index 0000000..4b7c816 --- /dev/null +++ b/ApiService/Controllers/RepositoryController.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RepositoryLibrary; + +namespace ApiService.Controllers +{ + [ApiController] + [Route("/api")] + public class RepositoryController : ControllerBase + { + private readonly IRepository _repsitory; + public RepositoryController(IRepository repository) + { + _repsitory = repository; + } + [HttpGet("lookup")] + public async Task Lookup() + { + return await _repsitory.Lookup(); + } + } +} diff --git a/ApiService/Controllers/WeatherForecastController.cs b/ApiService/Controllers/WeatherForecastController.cs deleted file mode 100644 index 88605a3..0000000 --- a/ApiService/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace ApiService.Controllers -{ - [ApiController] - [Route("[controller]")] - public class WeatherForecastController : ControllerBase - { - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet] - public IEnumerable Get() - { - var rng = new Random(); - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateTime.Now.AddDays(index), - TemperatureC = rng.Next(-20, 55), - Summary = Summaries[rng.Next(Summaries.Length)] - }) - .ToArray(); - } - } -} diff --git a/ApiService/Properties/launchSettings.json b/ApiService/Properties/launchSettings.json index b5d60ea..4ce3917 100644 --- a/ApiService/Properties/launchSettings.json +++ b/ApiService/Properties/launchSettings.json @@ -1,5 +1,4 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", +{ "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, @@ -8,23 +7,21 @@ "sslPort": 0 } }, + "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { "IIS Express": { "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "weatherforecast", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "ApiService": { "commandName": "Project", - "launchBrowser": true, - "launchUrl": "weatherforecast", - "applicationUrl": "http://localhost:5000", + "launchUrl": "api/lookup", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "applicationUrl": "http://localhost:56143" } } -} +} \ No newline at end of file diff --git a/ApiService/Startup.cs b/ApiService/Startup.cs index 0b6f12a..1b405b7 100644 --- a/ApiService/Startup.cs +++ b/ApiService/Startup.cs @@ -1,14 +1,21 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Net; +using System.Runtime.Serialization.Json; +using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using RepositoryLibrary; +using SharedLibrary.Models; namespace ApiService { @@ -24,6 +31,7 @@ namespace ApiService // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + services.AddScoped(); services.AddControllers(); } @@ -37,6 +45,30 @@ namespace ApiService app.UseRouting(); + app.Use(async (context, next) => + { + try + { + await next(); + } + catch (Exception ex) + { + var error = new RepositoryError + { + Information = ex.Message, + StackTrack = ex.StackTrace + }; + var dc = new DataContractJsonSerializer(typeof(RepositoryError)); + using (var ms = new MemoryStream()) + { + dc.WriteObject(ms, error); + var response = new StringBuilder(Encoding.UTF8.GetString(ms.ToArray())); + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + context.Response.ContentType = "application/json"; + await context.Response.WriteAsync(response.ToString()); + } + } + }); app.UseAuthorization(); app.UseEndpoints(endpoints => diff --git a/ApiService/WeatherForecast.cs b/ApiService/WeatherForecast.cs deleted file mode 100644 index 58a71f3..0000000 --- a/ApiService/WeatherForecast.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace ApiService -{ - public class WeatherForecast - { - public DateTime Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string Summary { get; set; } - } -} diff --git a/RepositoryLibrary/HostedRepository.cs b/RepositoryLibrary/HostedRepository.cs new file mode 100644 index 0000000..39909e0 --- /dev/null +++ b/RepositoryLibrary/HostedRepository.cs @@ -0,0 +1,69 @@ +using SharedLibrary.Models; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace RepositoryLibrary +{ + public class HostedRepository : IRepository + { + private readonly string _backendUrl; + public static ConcurrentBag ApiClient = new ConcurrentBag(); + public HostedRepository() + { + _backendUrl = "http://localhost:56143"; + } + public async Task Lookup() + { + return await GetAsync(); + } + + private async Task GetAsync([CallerMemberName] string request = "") + { + string uri = GetUriString(request); + var client = GetHttpClient(); + using (var response = await client.GetAsync(uri)) + { + ReturnHttpClient(client); + if (response.IsSuccessStatusCode) + { + var model = await response.Content.ReadAsAsync(); + if (model != null) + { + return model; + } + } + var err = await response.Content.ReadAsAsync(); + if (err != null) + { + throw new Exception(err.Information); + } + var str = await response.Content.ReadAsStringAsync(); + throw new Exception(str); + + + } + } + private HttpClient GetHttpClient() + { + if (!ApiClient.TryTake(out HttpClient client)) + { + client = new HttpClient(); + } + return client; + } + private void ReturnHttpClient(HttpClient client) + { + ApiClient.Add(client); + } + private string GetUriString(string request) + { + return $"{_backendUrl}/api/{request}"; + } + } +} diff --git a/RepositoryLibrary/IRepository.cs b/RepositoryLibrary/IRepository.cs new file mode 100644 index 0000000..a79537e --- /dev/null +++ b/RepositoryLibrary/IRepository.cs @@ -0,0 +1,10 @@ +using System; +using System.Threading.Tasks; + +namespace RepositoryLibrary +{ + public interface IRepository + { + Task Lookup(); + } +} diff --git a/RepositoryLibrary/LocalRepository.cs b/RepositoryLibrary/LocalRepository.cs new file mode 100644 index 0000000..2d66458 --- /dev/null +++ b/RepositoryLibrary/LocalRepository.cs @@ -0,0 +1,17 @@ +using SharedLibrary.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RepositoryLibrary +{ + public class LocalRepository : IRepository + { + public async Task Lookup() + { + throw new RepositoryException(); + } + } +} diff --git a/RepositoryLibrary/RepositoryLibrary.csproj b/RepositoryLibrary/RepositoryLibrary.csproj new file mode 100644 index 0000000..e2b272d --- /dev/null +++ b/RepositoryLibrary/RepositoryLibrary.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp3.1 + + + + + + + + + + + diff --git a/ServicesCallsService.sln b/ServicesCallsService.sln index c97b6f2..8a653ad 100644 --- a/ServicesCallsService.sln +++ b/ServicesCallsService.sln @@ -3,9 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30611.23 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebMVC", "WebMVC\WebMVC.csproj", "{FDAFEB96-62CA-46ED-8DC5-0D15F13CBF1B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebMVC", "WebMVC\WebMVC.csproj", "{FDAFEB96-62CA-46ED-8DC5-0D15F13CBF1B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiService", "ApiService\ApiService.csproj", "{381379E2-15DC-4835-A260-7B1E52ADE46D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiService", "ApiService\ApiService.csproj", "{381379E2-15DC-4835-A260-7B1E52ADE46D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RepositoryLibrary", "RepositoryLibrary\RepositoryLibrary.csproj", "{79A0D2A7-A633-41DE-A67A-EAAA8BFD46EF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedLibrary", "SharedLibrary\SharedLibrary.csproj", "{4E226D1F-35AA-4C4E-B4E5-55818F98B554}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,6 +25,14 @@ Global {381379E2-15DC-4835-A260-7B1E52ADE46D}.Debug|Any CPU.Build.0 = Debug|Any CPU {381379E2-15DC-4835-A260-7B1E52ADE46D}.Release|Any CPU.ActiveCfg = Release|Any CPU {381379E2-15DC-4835-A260-7B1E52ADE46D}.Release|Any CPU.Build.0 = Release|Any CPU + {79A0D2A7-A633-41DE-A67A-EAAA8BFD46EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {79A0D2A7-A633-41DE-A67A-EAAA8BFD46EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {79A0D2A7-A633-41DE-A67A-EAAA8BFD46EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {79A0D2A7-A633-41DE-A67A-EAAA8BFD46EF}.Release|Any CPU.Build.0 = Release|Any CPU + {4E226D1F-35AA-4C4E-B4E5-55818F98B554}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4E226D1F-35AA-4C4E-B4E5-55818F98B554}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E226D1F-35AA-4C4E-B4E5-55818F98B554}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4E226D1F-35AA-4C4E-B4E5-55818F98B554}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SharedLibrary/Models/RepositoryError.cs b/SharedLibrary/Models/RepositoryError.cs new file mode 100644 index 0000000..ef41b4e --- /dev/null +++ b/SharedLibrary/Models/RepositoryError.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharedLibrary.Models +{ + public class RepositoryError + { + public string Information { get; set; } + public string StackTrack { get; set; } + } +} diff --git a/SharedLibrary/Models/RepositoryException.cs b/SharedLibrary/Models/RepositoryException.cs new file mode 100644 index 0000000..85a13a2 --- /dev/null +++ b/SharedLibrary/Models/RepositoryException.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharedLibrary.Models +{ + public class RepositoryException : Exception + { + + } +} diff --git a/SharedLibrary/SharedLibrary.csproj b/SharedLibrary/SharedLibrary.csproj new file mode 100644 index 0000000..cb63190 --- /dev/null +++ b/SharedLibrary/SharedLibrary.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp3.1 + + + diff --git a/WebMVC/Controllers/ApiController.cs b/WebMVC/Controllers/ApiController.cs index a968727..11d7227 100644 --- a/WebMVC/Controllers/ApiController.cs +++ b/WebMVC/Controllers/ApiController.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; +using RepositoryLibrary; using System; using System.Collections.Generic; using System.Linq; @@ -10,10 +11,21 @@ namespace WebMVC.Controllers { public class ApiController : Controller { + private readonly IRepository _repository; + public ApiController(IRepository repository) + { + _repository = repository; + } public override void OnActionExecuted(ActionExecutedContext context) { - // This is where I capture the error from the ApiService + // This is where I capture the error from when calling the ApiService base.OnActionExecuted(context); } + + public async Task Lookup() + { + var lookup = await _repository.Lookup(); + return Ok(lookup); + } } } diff --git a/WebMVC/Controllers/HomeController.cs b/WebMVC/Controllers/HomeController.cs index 73740a9..470c91d 100644 --- a/WebMVC/Controllers/HomeController.cs +++ b/WebMVC/Controllers/HomeController.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; +using RepositoryLibrary; using WebMVC.Models; namespace WebMVC.Controllers @@ -12,10 +13,12 @@ namespace WebMVC.Controllers public class HomeController : Controller { private readonly ILogger _logger; + private readonly IRepository _repository; - public HomeController(ILogger logger) + public HomeController(ILogger logger, IRepository repository) { _logger = logger; + _repository = repository; } public IActionResult Index() @@ -23,8 +26,13 @@ namespace WebMVC.Controllers return View(); } - public IActionResult Privacy() + public async Task Privacy() { + // To get this working, I have make a call here in the Privacy page. + // Making the call in the Home/Index page was always returning a 404 Not Found. + // The second project was not starting quickly enough. + + var lookup = await _repository.Lookup(); return View(); } diff --git a/WebMVC/Startup.cs b/WebMVC/Startup.cs index ae6a8d2..5e3d46d 100644 --- a/WebMVC/Startup.cs +++ b/WebMVC/Startup.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using RepositoryLibrary; namespace WebMVC { @@ -19,13 +20,19 @@ namespace WebMVC public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); + if (false) + { + services.AddScoped(); + } + else + { + services.AddScoped(); + } } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) @@ -39,7 +46,21 @@ namespace WebMVC app.UseStaticFiles(); app.UseRouting(); - + app.Use(async (context, next) => + { + try + { + await next(); + } + catch (Exception ex) + { + // Here I want to log the exception with the StackTrace. + // The StackTrace when local is what I want + // The StackTrace when Hosted is only to the Hosted. I'd + // like to see the StackTrace returned from ApiService. + return; + } + }); app.UseAuthorization(); app.UseEndpoints(endpoints => diff --git a/WebMVC/WebMVC.csproj b/WebMVC/WebMVC.csproj index 92605c5..28f6631 100644 --- a/WebMVC/WebMVC.csproj +++ b/WebMVC/WebMVC.csproj @@ -4,4 +4,9 @@ netcoreapp3.1 + + + + +