I have a standard ASP.NET Core 2 Web App acting as a REST/WebApi. For one of my endpoints I return an HTTP 400
when the user provides bad search/filter querystring arguments.
Works great with POSTMAN. But when I try and test this with my SPA app (which in effect is now crossing domains and thus doing a CORS request), I get a failure in Chrome.
When doing a CORS request to an endpoint that returns an HTTP 200
response, all works fine.
It looks like my error handling is NOT taking into consideration the CORS stuff (i.e. not adding any CORS headers) and isn't including that.
I'm guessing I'm messing up the response payload pipeline stuff.
Q: Is there a way to correct return any CORS header information in a custom Error Handling without hardcoding the header but instead using the headers stuff that was setup in the Configure/ConfigureServices
methods in Startup.cs
?
Pseduo code..
public void ConfigureServices(IServiceCollection services)
{
... snip ...
services.AddMvcCore()
.AddAuthorization()
.AddFormatterMappings()
.AddJsonFormatters(options =>
{
options.ContractResolver = new CamelCasePropertyNamesContractResolver();
options.Formatting = Formatting.Indented;
options.DateFormatHandling = DateFormatHandling.IsoDateFormat;
options.NullValueHandling = NullValueHandling.Ignore;
options.Converters.Add(new StringEnumConverter());
})
.AddCors(); // REF: https://docs.microsoft.com/en-us/aspnet/core/security/cors#setting-up-cors
... snip ...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
... snip ...
app.UseExceptionHandler(options => options.Run(async httpContext => await ExceptionResponseAsync(httpContext, true)));
app.UseCors(builder => builder//.WithOrigins("http://localhost:52383", "http://localhost:49497")
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
... snip ...
}
private static async Task ExceptionResponseAsync(HttpContext httpContext, bool isDevelopmentEnvironment)
{
var exceptionFeature = httpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionFeature == null)
{
// An unknow and unhandled exception occured. So this is like a fallback.
exceptionFeature = new ExceptionHandlerFeature
{
Error = new Exception("An unhandled and unexpected error has occured. Ro-roh :~(.")
};
}
await ConvertExceptionToJsonResponseAsyn(exceptionFeature,
httpContext.Response,
isDevelopmentEnvironment);
}
private static Task ConvertExceptionToJsonResponseAsyn(IExceptionHandlerPathFeature exceptionFeature,
HttpResponse response,
bool isDevelopmentEnvironment)
{
if (exceptionFeature == null)
{
throw new ArgumentNullException(nameof(exceptionFeature));
}
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
var exception = exceptionFeature.Error;
var includeStackTrace = false;
var statusCode = HttpStatusCode.InternalServerError;
var error = new ApiError();
if (exception is ValidationException)
{
statusCode = HttpStatusCode.BadRequest;
foreach(var validationError in ((ValidationException)exception).Errors)
{
error.AddError(validationError.PropertyName, validationError.ErrorMessage);
}
}
else
{
// Final fallback.
includeStackTrace = true;
error.AddError(exception.Message);
}
if (includeStackTrace &&
isDevelopmentEnvironment)
{
error.StackTrace = exception.StackTrace;
}
var json = JsonConvert.SerializeObject(error, JsonSerializerSettings);
response.StatusCode = (int)statusCode;
response.ContentType = JsonContentType;
// response.Headers.Add("Access-Control-Allow-Origin", "*"); <-- Don't want to hard code this.
return response.WriteAsync(json);
}
Cheers!