Added Error Handling
11
API/Controllers/BaseApiController.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace API.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class BaseApiController : ControllerBase
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
47
API/Controllers/BuggyController.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using API.Errors;
|
||||||
|
using Infrastructure.Data;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace API.Controllers
|
||||||
|
{
|
||||||
|
public class BuggyController : BaseApiController
|
||||||
|
{
|
||||||
|
private readonly StoreContext _context;
|
||||||
|
public BuggyController(StoreContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("notfound")]
|
||||||
|
public ActionResult GetNotFoundRequest()
|
||||||
|
{
|
||||||
|
var thing = _context.Products.Find(42);
|
||||||
|
if (thing == null)
|
||||||
|
{
|
||||||
|
return NotFound(new ApiResponse(404));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("servererror")]
|
||||||
|
public ActionResult GetServerError()
|
||||||
|
{
|
||||||
|
var thing = _context.Products.Find(42);
|
||||||
|
var thingToReturn = thing.ToString();
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("badrequest")]
|
||||||
|
public ActionResult GetBadRequest()
|
||||||
|
{
|
||||||
|
return BadRequest(new ApiResponse(400));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("badrequest/{id}")]
|
||||||
|
public ActionResult GetNotFoundRequest(int id)
|
||||||
|
{
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
API/Controllers/ErrorController.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using API.Errors;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace API.Controllers
|
||||||
|
{
|
||||||
|
[Route("errors/{code}")]
|
||||||
|
public class ErrorController : BaseApiController
|
||||||
|
{
|
||||||
|
public IActionResult Error(int code)
|
||||||
|
{
|
||||||
|
return new ObjectResult(new ApiResponse(code));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,9 +7,7 @@ using AutoMapper;
|
|||||||
|
|
||||||
namespace API.Controllers
|
namespace API.Controllers
|
||||||
{
|
{
|
||||||
[ApiController]
|
public class ProductsController : BaseApiController
|
||||||
[Route("api/[controller]")]
|
|
||||||
public class ProductsController : ControllerBase
|
|
||||||
{
|
{
|
||||||
private readonly IGenericRepository<Product> _productsRepo;
|
private readonly IGenericRepository<Product> _productsRepo;
|
||||||
private readonly IGenericRepository<ProductBrand> _productBrandRepo;
|
private readonly IGenericRepository<ProductBrand> _productBrandRepo;
|
||||||
|
12
API/Errors/ApiException.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace API.Errors
|
||||||
|
{
|
||||||
|
public class ApiException : ApiResponse
|
||||||
|
{
|
||||||
|
public ApiException(int statusCode, string message = null, string details = null) : base(statusCode, message)
|
||||||
|
{
|
||||||
|
Details = details;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Details { get; set; }
|
||||||
|
}
|
||||||
|
}
|
31
API/Errors/ApiResponse.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace API.Errors
|
||||||
|
{
|
||||||
|
public class ApiResponse
|
||||||
|
{
|
||||||
|
public ApiResponse(int statusCode, string message = null)
|
||||||
|
{
|
||||||
|
StatusCode = statusCode;
|
||||||
|
Message = message ?? GetDefaultMessageForStatusCode(statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int StatusCode { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
private string GetDefaultMessageForStatusCode(int statusCode)
|
||||||
|
{
|
||||||
|
return statusCode switch
|
||||||
|
{
|
||||||
|
400 => "A bad request, you have made",
|
||||||
|
401 => "Authorized, you are not",
|
||||||
|
404 => "Resource found, it was not",
|
||||||
|
500 => "Errors are the path to the dark side. Errors lead to anger. Anger leads to hate. Hate leads to career change",
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
API/Middleware/ExceptionMiddleware.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System.Net;
|
||||||
|
using System.Text.Json;
|
||||||
|
using API.Errors;
|
||||||
|
|
||||||
|
namespace API.Middleware
|
||||||
|
{
|
||||||
|
public class ExceptionMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly ILogger<ExceptionMiddleware> _logger;
|
||||||
|
private readonly IHostEnvironment _env;
|
||||||
|
public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger, IHostEnvironment env)
|
||||||
|
{
|
||||||
|
_env = env;
|
||||||
|
_logger = logger;
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InvokeAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _next(context);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, ex.Message);
|
||||||
|
context.Response.ContentType = "application/json";
|
||||||
|
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||||
|
var response = _env.IsDevelopment()
|
||||||
|
? new ApiException((int)HttpStatusCode.InternalServerError, ex.Message, ex.StackTrace.ToString())
|
||||||
|
: new ApiException((int)HttpStatusCode.InternalServerError);
|
||||||
|
var options = new JsonSerializerOptions{PropertyNamingPolicy = JsonNamingPolicy.CamelCase};
|
||||||
|
var json = JsonSerializer.Serialize(response, options);
|
||||||
|
await context.Response.WriteAsync(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
|
using API.Middleware;
|
||||||
using Core.Interfaces;
|
using Core.Interfaces;
|
||||||
using Infrastructure.Data;
|
using Infrastructure.Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@ -33,16 +34,20 @@ namespace API
|
|||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
{
|
{
|
||||||
|
app.UseMiddleware<ExceptionMiddleware>();
|
||||||
|
|
||||||
if (env.IsDevelopment())
|
if (env.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.UseDeveloperExceptionPage();
|
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebAPIv5 v1"));
|
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebAPIv5 v1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.UseStatusCodePagesWithReExecute("/errors/{0}");
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
app.UseStaticFiles();
|
||||||
|
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
BIN
API/wwwroot/images/products/boot-ang1.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
API/wwwroot/images/products/boot-ang2.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
API/wwwroot/images/products/boot-core1.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
API/wwwroot/images/products/boot-core2.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
API/wwwroot/images/products/boot-redis1.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
API/wwwroot/images/products/glove-code1.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
API/wwwroot/images/products/glove-code2.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
API/wwwroot/images/products/glove-react1.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
API/wwwroot/images/products/glove-react2.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
API/wwwroot/images/products/hat-core1.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
API/wwwroot/images/products/hat-react1.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
API/wwwroot/images/products/hat-react2.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
API/wwwroot/images/products/sb-ang1.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
API/wwwroot/images/products/sb-ang2.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
API/wwwroot/images/products/sb-core1.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
API/wwwroot/images/products/sb-core2.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
API/wwwroot/images/products/sb-react1.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
API/wwwroot/images/products/sb-ts1.png
Normal file
After Width: | Height: | Size: 50 KiB |