I've created a new ASP.NET Core Web API and have several controllers such as this one:
[Route("api/[controller]")]
public class DoctorRevenueController : Controller
{
private IDoctorRevenueRepository DoctorRevenueRepository;
public DoctorRevenueController(IDoctorRevenueRepository repository)
{
DoctorRevenueRepository = repository;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
[HttpGet("GetDoctorRevenues")]
//[Route("DoctorRevenue")]
public async Task<IActionResult> GetDoctorRevenues(Int32? pageSize = 10, Int32? pageNumber = 1, String DoctorName = null)
{
var response = new ListModelResponse<DocRevViewModel>() as IListModelResponse<DocRevViewModel>;
try
{
response.PageSize = (Int32)pageSize;
response.PageNumber = (Int32)pageNumber;
response.Model = await Task.Run(() =>
{
return DoctorRevenueRepository
.GetDocRevenues(response.PageNumber, response.PageSize, DoctorName)
.Select(item => item.ToViewModel())
.ToList();
});
response.Message = String.Format("Total Records {0}", response.Model.Count());
}
catch (Exception ex)
{
response.DidError = true;
response.Message = ex.Message;
}
return response.ToHttpResponse();
}
//GET DoctorRevenues/Doctor
[HttpGet("GetDoctorRevenue/{DoctorId}")]
//[Route("DoctorRevenue")]
public async Task<IActionResult> GetDoctorRevenue(int DoctorId)
{
var response = new SingleModelResponse<DocRevViewModel>() as ISingleModelResponse<DocRevViewModel>;
try
{
response.Model = await Task.Run(() =>
{
return DoctorRevenueRepository.GetDocRevenue(DoctorId).ToViewModel();
});
}
catch (Exception ex)
{
response.DidError = true;
response.Message = ex.Message;
}
return response.ToHttpResponse();
}
//POST DoctorRevenues/Doctor
[HttpPost("CreateDoctorRevenue/{DoctorId}")]
//[Route("DoctorRevenue")]
public async Task<IActionResult> CreateDoctorRevenue([FromBody]DocRevViewModel value)
{
var response = new SingleModelResponse<DocRevViewModel>() as ISingleModelResponse<DocRevViewModel>;
try
{
var entity = await Task.Run(() =>
{
return DoctorRevenueRepository.AddDocRevenue(value.ToEntity());
});
response.Model = entity.ToViewModel();
response.Message = "The invoices and revenue for this doctor have been successfully saved.";
}
catch(Exception ex)
{
response.DidError = true;
response.Message = ex.Message;
}
return response.ToHttpResponse();
}
//PUT DoctorRevenues/Doctor/5
[HttpPut("UpdateDoctorRevenue/{RecordId}")]
//[Route("DoctorRevenue/{RecordId}")]
public async Task<IActionResult> UpdateDoctorRevenue(int RecordId, [FromBody]DocRevViewModel value)
{
var response = new SingleModelResponse<DocRevViewModel>() as ISingleModelResponse<DocRevViewModel>;
try
{
var entity = await Task.Run(() =>
{
return DoctorRevenueRepository.UpdateDocRevenue(RecordId, value.ToEntity());
});
response.Model = entity.ToViewModel();
response.Message = "The invoices and revenue for this doctor were successfully updated.";
}
catch(Exception ex)
{
response.DidError = true;
response.Message = ex.Message;
}
return response.ToHttpResponse();
}
//DELETE DoctorRevenue/5
[HttpDelete]
[Route("DoctorRevenue/{RecordId}")]
public async Task<IActionResult> DeleteDoctorRevenue(int RecordId)
{
var response = new SingleModelResponse<DocRevViewModel>() as ISingleModelResponse<DocRevViewModel>;
try
{
var entity = await Task.Run(() =>
{
return DoctorRevenueRepository.DeleteDocRevenue(RecordId);
});
response.Message = "This doctor's invoices and revenue have been deleted";
}
catch(Exception ex)
{
response.DidError = true;
response.Message = ex.Message;
}
return response.ToHttpResponse();
}
}
My Startup.cs includes:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IDoctorMasterRepository, DoctorMasterRepository>();
services.AddScoped<IDoctorRevenueRepository, DoctorRevenueRepository>();
services.AddScoped<IFacilityMasterRepository, FacilityMasterRepository>();
services.AddScoped<IFacilityRevenueRepository, FacilityRevenueRepository>();
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
services.AddOptions();
services.AddLogging();
services.AddSingleton<IDoctorMasterRepository, DoctorMasterRepository>();
services.AddSingleton<IFacilityMasterRepository, FacilityMasterRepository>();
services.AddSingleton<IDoctorRevenueRepository, DoctorRevenueRepository>();
services.AddSingleton<IFacilityRevenueRepository, FacilityRevenueRepository>();
services.AddSwaggerGen();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseStaticFiles();
app.UseDeveloperExceptionPage();
app.UseApplicationInsightsRequestTelemetry();
app.UseApplicationInsightsExceptionTelemetry();
app.UseMvc();
app.UseSwagger();
app.UseSwaggerUi();
}
After successfully building the project, a Debug produces an error:
localhost refused to connect. ERR_CONNECTION_REFUSED
How do I remedy this such that I can view my API documentation page?
I found that dotnet run needed to be executed at the command line to start the local IISExpress platform. After doing so, Postman served my API routes as expected.
Related
I throw my custom exception when my StatusCode is 404 the browser get 200 and gets no content but with the other StatusCode things work fine
how to fix this problem and get the 404 in my response not 200
this is my middleware
public static class ExceptionHandlingMiddlewareExtensions
{
public static IApplicationBuilder UseNativeGlobalExceptionHandler(this IApplicationBuilder app)
{
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
try
{
var errorFeature = context.Features.Get<IExceptionHandlerFeature>();
var exception = errorFeature.Error;
// Log exception and/or run some other necessary code...
var errorResponse = new ErrorResponse();
if (exception is HttpException httpException)
{
errorResponse.StatusCode = httpException.StatusCode;
errorResponse.Message = httpException.Message;
}
context.Response.StatusCode = (int) errorResponse.StatusCode;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(errorResponse.ToJsonString());
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
});
});
return app;
}
}
}
this is my Controller where I throw 404 StatusCodeenter image description here
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
throw new HttpException(HttpStatusCode.NotFound, "Not found");
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();
}
Try to return the not found method from ControllerBase class.
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
return NotFound();
}
Extend your controller to use base class. It should be like this:
public class WeatherForecastController : ControllerBase
{
....
}
I have this ErrorHandlingMiddleware that looks like this:
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
this._next = next;
}
public async Task Invoke(HttpContext context /* other dependencies */)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception ex)
{
var statusCode = (int)HttpStatusCode.InternalServerError;
if (ex is NotFoundError) statusCode = (int)HttpStatusCode.NotFound;
//else if (ex is MyUnauthorizedException) code = HttpStatusCode.Unauthorized;
//else if (ex is MyException) code = HttpStatusCode.BadRequest;
var error = new AttemptError(statusCode, ex.Message, ex);
context.Response.ContentType = "application/json";
context.Response.StatusCode = statusCode;
return context.Response.WriteAsync(error.ToString());
}
}
And I have added this to my Startup class:
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<ErrorHandlingMiddleware>();
app.SeedIdentityServerDatabase();
app.UseDeveloperExceptionPage();
app.UseIdentityServer();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "r3plica Identity Server v1");
c.OAuthClientId("swagger");
c.OAuthAppName("Swagger Api UI");
});
app.UseMvc();
}
I would expect that if I was anywhere in my application and I throw an exception, it would be caught and it would execute this line:
await HandleExceptionAsync(context, ex);
So, I set up a test:
throw new Exception();
Which is thrown in my controller. When I run my application and then call the endpoint that has that exception thrown, it does indeed get to the Invoke method of my ErrorHandlingMiddleware, but instead of an exception being caught, it just goes to the await _next(context)....
Does anyone know what I am doing wrong?
Your middleware will work if you place the call to use it after app.UseDeveloperExceptionPage(); in Startup.
Handle errors in ASP.NET Core says
Place the call to UseDeveloperExceptionPage before any middleware that you want to catch exceptions.
I fixed this by creating a filter:
public class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter
{
public int Order { get; set; } = int.MaxValue - 10;
public void OnActionExecuting(ActionExecutingContext context) { }
public void OnActionExecuted(ActionExecutedContext context)
{
if (context.Exception == null) return;
var attempt = Attempt<string>.Fail(context.Exception);
if (context.Exception is AttemptException exception)
{
context.Result = new ObjectResult(attempt)
{
StatusCode = exception.StatusCode,
};
}
else
{
context.Result = new ObjectResult(attempt)
{
StatusCode = (int)HttpStatusCode.InternalServerError,
};
}
context.ExceptionHandled = true;
}
}
And registering it like this:
services.AddControllers(options => options.Filters.Add(new HttpResponseExceptionFilter()));
When I run my app, I keep getting the following: ConnectionID required
So I can't seem to be able to connect my ASP.NET Core API server to my asp.net uwp client.
I have installed the Microsoft.AspNetCore.SignalR 1.1.0 nuget package on my server side and the Microsoft.AspNet.SignalR.Client 2.4.1 nuget package on my client side.
This is my Startup.cs class:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("FlightAppContext"));
});
services.AddSession();
services.AddOpenApiDocument(c =>
{
c.DocumentName = "apidocs";
c.Title = "FlightApp API";
c.Version = "v1";
c.Description = "The FlightApp API documentation description.";
c.DocumentProcessors.Add(new SecurityDefinitionAppender("JWT Token", new SwaggerSecurityScheme
{
Type = SwaggerSecuritySchemeType.ApiKey,
Name = "Authorization",
In = SwaggerSecurityApiKeyLocation.Header,
Description = "Copy 'Bearer' + valid JWT token into field"
}));
c.OperationProcessors.Add(new OperationSecurityScopeProcessor("JWT Token"));
}); //for OpenAPI 3.0 else AddSwaggerDocument();
services.AddSignalR();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddMvc().AddXmlSerializerFormatters();
services.AddScoped<IEntertainmentRepository, EntertainmentRepository>();
services.AddScoped<IOrderlineRepository, OrderlineRepository>();
services.AddScoped<IPassengerRepository, PassengerRepository>();
services.AddScoped<IPersonnelRepository, PersonnelRepository>();
services.AddScoped<IProductRepository, ProductRepository>();
services.AddScoped<IPassengerGroupRepository, PassengerGroupRepository>();
services.AddScoped<ApplicationDataInitializer>();
services.AddCors(options => options.AddPolicy("AllowAllOrigins", builder => builder.AllowAnyOrigin()));
}
//This method gets called by the runtime.Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ApplicationDataInitializer dataInitializer)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
//The default HSTS value is 30 days.You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseAuthentication();
//app.UseHttpsRedirection();
app.UseSignalR(route => route.MapHub<ChatHub>("/api/Chat/GetAllChatMessages"));
app.UseMvc();
app.UseSwaggerUi3();
app.UseSwagger();
dataInitializer.InitializeData();
}
}
This is my ChatHub.cs class:
public class ChatHub : Hub
{
public Task SendMessage(int PassengergroupId, string userName, string message, DateTime sendTime)
{
return Clients.All.SendAsync("ReceiveMessage", userName, message, sendTime);
//Groups.AddToGroupAsync(Context.ConnectionId, Convert.ToString(PassengergroupId));
//return Clients.Groups(Convert.ToString(PassengergroupId)).SendAsync("ReceiveMessage", userName, message, sendTime);
}
}
this is my ChatController:
/// <summary>
/// Adds a message to a passengroup
/// </summary>
/// <param name="message">message to post</param>
[HttpPost("ChatMessages")]
public async Task<IActionResult> CreateMessage(MessageDTO message)
{
PassengerGroup passengerGroup = _passengerGroupRepository.GetById(message.PassengergroupId);
Passenger currentUser = _passengerRepository.GetById(message.PassengerId);
Message messageToCreate = new Message(currentUser, passengerGroup, message.Content);
await _dbContext.Messages.AddAsync(messageToCreate);
await _dbContext.SaveChangesAsync();
return Ok();
}
/// <summary>
/// Get all messages from passengergroup
/// </summary>
[HttpGet("GetAllChatMessagesFromPassengerGroup/{id}")]
public async Task<IEnumerable<MessageDTO>> GetAllChatMessagesFromPassengerGroup(int id)
{
var messages = await _dbContext.Messages.Include(m => m.Passenger)
.Include(m=>m.PassengerGroup).Where(e => e.PassengroupId == id).ToListAsync();
ICollection<MessageDTO> messagesDTO = new List<MessageDTO>();
foreach (var m in messages)
{
MessageDTO message = new MessageDTO(m);
messagesDTO.Add(message);
}
return messagesDTO;
}
/// <summary>
/// Get all messages
/// </summary>
[HttpGet("GetAllChatMessages")]
public async Task<IEnumerable<MessageDTO>> GetAllChatMessages()
{
var messages = await _dbContext.Messages.Include(m => m.Passenger)
.Include(m => m.PassengerGroup).ToListAsync();
ICollection<MessageDTO> messagesDTO = new List<MessageDTO>();
foreach (var m in messages)
{
MessageDTO message = new MessageDTO(m);
messagesDTO.Add(message);
}
return messagesDTO;
}
}
and this is my Chat.xaml.cs class:
public sealed partial class Chat : Page
{
public HubConnection hubConnection;
public IHubProxy hubProxy;
public ObservableCollection<Message> MessagesList { get; set; }
private HttpClient client = new HttpClient();
public Chat()
{
InitializeComponent();
}
public int CurrentPassengerId;
private Passenger current;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var currentPassengerId = e.Parameter.ToString();
this.CurrentPassengerId = Int32.Parse(currentPassengerId);
base.OnNavigatedTo(e);
GetCurrentUser(CurrentPassengerId);
LoadMessages(current.PassengerGroupId);
InitilizeHub(current.PassengerGroupId);
}
private async void GetCurrentUser(int id)
{
var json = await client.GetStringAsync(new Uri($"http://localhost:53385/api/Passenger/{id}"));
current = JsonConvert.DeserializeObject<Passenger>(json);
this.userTextBlock.Text = current.FirstName + current.LastName;
}
private async void InitilizeHub(int id)
{
hubConnection = new HubConnection($"http://localhost:53385/api/Chat/GetAllChatMessages");
hubProxy = hubConnection.CreateHubProxy("ChatHub");
hubProxy.On<string, string, DateTime>("receiveMessage", GetMessage);
await hubConnection.Start();
if (hubConnection.State != ConnectionState.Connected)
{ await hubConnection.Start(); }
}
private async void GetMessage(string userName, string message, DateTime sendTime)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
this.MessageListbox.Items.Add($"{sendTime.ToString("HH:mm")}\n{userName}: {message}")
);
}
private async void SendButton_Click(object sender, RoutedEventArgs e)
{
await hubProxy?.Invoke("sendMessage", current.PassengerGroupId, userTextBlock.Text, messageTextBox.Text, DateTime.Now);
var message = new Message(messageTextBox.Text, userTextBlock.Text, current.PassengerGroupId);
var messageJson = JsonConvert.SerializeObject(message);
var res = await client.PostAsync("http://localhost:53385/api/Chat/ChatMessages",
new StringContent(messageJson, System.Text.Encoding.UTF8, "application/json"));
messageTextBox.Text = string.Empty;
}
public async void LoadMessages(int id)
{
var json = await client.GetStringAsync(new Uri($"http://localhost:53385/api/Chat/GetAllChatMessagesFromPassengerGroup/{id}"));
var lst = JsonConvert.DeserializeObject<ObservableCollection<Message>>(json);
foreach (var message in lst)
{
MessagesList.Add(message);
this.MessageListbox.Items.Add($"{message.TimeSend.ToString("HH:mm")}\n{message.Name}: {message.Content}");
}
}
}
I am using App Insights in my web api 2 project, which is being called form a React Front End.
When something wrong happens I would like to show the user a generic error like: Please contact the admin, and show them a Guid, or error number.
Then with that error number I could check in App Insights what the real exception was.
Is this possible?
My web api code is below
namespace LuloWebApi.Controllers
{
[Authorize]
public class ClientController : ApiController
{
[HttpGet]
public async Task<List<Client>> GetClients()
{
//var telemetry = new TelemetryClient();
//try
//{
var clientStore = CosmosStoreHolder.Instance.CosmosStoreClient;
return await clientStore.Query().ToListAsync();
//}
//catch (System.Exception ex)
//{
// telemetry.TrackException(ex);
//}
}
[HttpGet]
public async Task<IHttpActionResult> GetClient(string clientId)
{
var telemetry = new TelemetryClient();
try
{
var clientStore = CosmosStoreHolder.Instance.CosmosStoreClient;
var client = await clientStore.Query().FirstOrDefaultAsync(x => x.Id == clientId);
if (client == null)
{
return NotFound();
}
return Ok(client);
}
catch (System.Exception ex)
{
telemetry.TrackException(ex);
return BadRequest("Unknown error");
}
}
[HttpPut]
public async Task<IHttpActionResult> UpdateClient(string id,[FromBody]Client client)
{
var telemetry = new TelemetryClient();
try
{
var clientStore = CosmosStoreHolder.Instance.CosmosStoreClient;
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await clientStore.UpdateAsync(client);
return Ok(result);
}
catch (System.Exception ex)
{
telemetry.TrackException(ex);
return BadRequest("Unknown error");
}
}
[HttpPost]
public async Task<IHttpActionResult> AddCLient([FromBody]Client Client)
{
var telemetry = new TelemetryClient();
try
{
var clientStore = CosmosStoreHolder.Instance.CosmosStoreClient;
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var added = await clientStore.AddAsync(Client);
return StatusCode(HttpStatusCode.NoContent);
}
catch (System.Exception ex)
{
telemetry.TrackException(ex);
return BadRequest("Unknown error");
}
}
public async Task<IHttpActionResult> DeleteClient(string clientId)
{
var telemetry = new TelemetryClient();
try
{
var clientStore = CosmosStoreHolder.Instance.CosmosStoreClient;
await clientStore.RemoveByIdAsync(clientId);
return Ok(clientId);
}
catch (System.Exception ex)
{
telemetry.TrackException(ex);
return BadRequest("Unknown error");
}
}
}
}
Please correct me if I misunderstand you.
I think it's as easy as manually creating a guid, and add to the exception telemetry as well to the BadRequest().
try
{
//some code here
}
catch(Exception ex)
{
string guid = Guid.NewGuid().ToString();
Dictionary<string,string> dt = new Dictionary<string, string>();
dt.Add("my error number1", guid);
telemetryClient.TrackException(ex,dt);
return BadRequest("Unknown error:"+guid);
}
And when you get the guid, you can search the related error in azure portal:
I have an application in ASP.NET Core MVC (dnx46) RC1 with an AuthorizationHandler:
public class AppSumAuthAuthorizationHandler : AuthorizationHandler<AppSumAuthRequirement>
{
private readonly IUserRepository _userRepository;
private readonly IUserRoleRepository _userRoleRepository;
public AppSumAuthAuthorizationHandler(IUserRepository userRepository, IUserRoleRepository userRoleRepository)
{
_userRepository = userRepository;
_userRoleRepository = userRoleRepository;
}
protected override async void Handle(AuthorizationContext context, AppSumAuthRequirement requirement)
{
await HandleAsync(context,requirement);
}
protected override async Task HandleAsync(AuthorizationContext context, AppSumAuthRequirement requirement)
{
var currentUserName = context.User.Identity.Name;
var currentUser = await _userRepository.GetAsync(u => u.UserName == context.User.Identity.Name);
// Create user that does not yet exist
if(currentUser == null)
{
var user = new User(currentUserName);
/* Temporary add SysAdmin role */
using(new CreatedBySystemProvider(_userRepository))
{
_userRepository.Add(user);
await _userRepository.SaveChangesAsync();
if (string.Equals(currentUserName, #"BIJTJES\NilsG", StringComparison.CurrentCultureIgnoreCase))
{
user.AddRole(1);
}
currentUser = await _userRepository.GetAsync(u => u.Id == user.Id);
}
}
var resource = (Microsoft.AspNet.Mvc.Filters.AuthorizationContext) context.Resource;
var controllerActionDescriptor = resource.ActionDescriptor as ControllerActionDescriptor;
var controllerName = controllerActionDescriptor.ControllerName;
var actionName = controllerActionDescriptor.Name;
string moduleName;
try
{
// Get the name of the module
moduleName = ((ModuleAttribute)controllerActionDescriptor.ControllerTypeInfo.GetCustomAttributes(false).First(a => a.GetType().Name == "ModuleAttribute")).ModuleName;
}
catch(InvalidOperationException ex)
{
context.Fail();
throw new InvalidOperationException($"The Module Attribute is required on basecontroller {controllerName}.", ex);
}
var access = new Access(moduleName, controllerName, actionName);
if (await currentUser.HasPermissionTo(UrlAccessLevel.Access).OnAsync(access))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
}
}
The requirement class is empty:
public interface IAppSumAuthRequirement : IAuthorizationRequirement
{
}
public class AppSumAuthRequirement : IAppSumAuthRequirement
{
}
The Module attribute is also nothing special:
public class ModuleAttribute : Attribute
{
public string ModuleName { get; private set; }
public ModuleAttribute(string moduleName)
{
ModuleName = moduleName;
}
public override string ToString()
{
return ModuleName;
}
}
The exception filter:
public class JsonExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var exception = context.Exception;
context.HttpContext.Response.StatusCode = 500;
context.Result = new JsonResult(new Error
{
Message = exception.Message,
InnerException = exception.InnerException?.InnerException?.Message,
Data = exception.Data,
ErrorCode = exception.HResult,
Source = exception.Source,
Stacktrace = exception.StackTrace,
ErrorType = exception.GetType().ToString()
});
}
}
and policy are configured in my Startup.cs:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(new JsonExceptionFilterAttribute());
options.ModelBinders.Insert(0, new NullableIntModelBinder());
}).AddJsonOptions(options => {
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
});
// Security
services.AddAuthorization(options =>
{
options.AddPolicy("AppSumAuth",
policy => policy.Requirements.Add(new AppSumAuthRequirement()));
});
}
and the policy is set on all controllers, by inheriting BaseController:
[Authorize(Policy = "AppSumAuth")]
public class BaseController : Controller
{
public BaseController()
{
}
}
So, in my handler, I get the controllername, actionname and modulename (from the attribute set on the controllers):
[Module("Main")]
When this attribute is not set on a controller, I would like to catch the exception and report this back to the developer calling the controller and deny access. To do this, I've added:
catch(InvalidOperationException ex)
{
context.Fail();
throw new InvalidOperationException($"The Module Attribute is required on basecontroller {controllerName}.", ex);
}
The JsonExceptionFilter is called perfectly when there is an exception in the controllers. It is however not called when there is an error in the AuthorizationHandler.
So the question:
How can I get the Exceptions to be caught by the JsonExceptionFilter?
What am I doing wrong?
Solution:
Startup.cs:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// For Windows Auth!
app.UseIISPlatformHandler();
app.UseStaticFiles();
app.UseExceptionHandler(AppSumExceptionMiddleware.JsonHandler());
app.UseMvc();
}
And my middleware:
public class AppSumExceptionMiddleware
{
public static Action<IApplicationBuilder> JsonHandler()
{
return errorApp =>
{
errorApp.Run(async context =>
{
var exception = context.Features.Get<IExceptionHandlerFeature>();
if (exception != null)
{
var exceptionJson = Encoding.UTF8.GetBytes(
JsonConvert.SerializeObject(new AppSumException(exception.Error),
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
})
);
context.Response.ContentType = "application/json";
await context.Response.Body.WriteAsync(exceptionJson, 0, exceptionJson.Length);
}
});
};
}
}
Action filter can be used as a method filter, controller filter, or global filter only for MVC HTTP requests. In your case you need to use a middleware, as
Middleware is component that "sit" on the HTTP pipeline and examine
all requests and responses.
As you want to works with exception, you may use ready-to-use ExceptionHandler middleware:
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = 500; // for example
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
var ex = error.Error;
// custom logic
}
});
});