MVC 6 HttpResponseException - c#

I am trying to return status codes in the following way
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized) { ReasonPhrase = "invalid username/password" });
now this is not supported in MVC6, which sucks because using IActionResult seems really silly to me, and way less intuitive.
I have found the following posts one and two.
the first leads to a broken link, and the second applies to an MVC application.
I did realize that I need to create a middleware to address this issue, but I am not sure where to start, and since this is pretty useful stuff, I would expect there to be some open source solution I could use or maybe a code snippet.
For the record, I am using ASP.net 5 rc1 update 1 MVC 6

I think this change is due to .NET Core - where ASP.NET tries to do everything out of the box, ASP.NET Core only does what you specifically tell it to (which is a big part of why it's so much quicker and portable).
If you want this behaviour in Core you need to add it, either as a package that someone has written for you or by rolling your own.
It's fairly simple. First you need a custom exception to check for:
public class StatusCodeException : Exception
{
public StatusCodeException(HttpStatusCode statusCode)
{
StatusCode = statusCode;
}
public HttpStatusCode StatusCode { get; set; }
}
Then you need a RequestDelegate handler that checks for the new exception and converts it to the HTTP response status code:
public class StatusCodeExceptionHandler
{
private readonly RequestDelegate request;
public StatusCodeExceptionHandler(RequestDelegate next)
{
this.request = next;
}
public Task Invoke(HttpContext context) => this.InvokeAsync(context); // Stops VS from nagging about async method without ...Async suffix.
async Task InvokeAsync(HttpContext context)
{
try
{
await this.request(context);
}
catch (StatusCodeException exception)
{
context.Response.StatusCode = (int)exception.StatusCode;
context.Response.Headers.Clear();
}
}
}
Then you register this middleware in your startup:
public class Startup
{
...
public void Configure(IApplicationBuilder app)
{
...
app.UseMiddleware<StatusCodeExceptionHandler>();
Finally you can throw the HTTP status code exception in your actions, while still returning an explicit type that you can easily unit test:
public Thing Get(int id) {
Thing thing = GetThingFromDB();
if (thing == null)
throw new StatusCodeException(HttpStatusCode.NotFound);
return thing;
}
This is fairly simple and someone out there has probably written a more complete one, but I haven't been able to find one or get a clear answer as to why this was dropped.

Related

EF Core - API Site: A second operation was started on this context before a previous operation completed

I know there are plenty of other threads covering this topic, but none of the solutions appear to have helped. Hence the creation of this one. Sorry for the lengthy Post!
The problem:
I am running an .Net Core API Website using EF Core. Naturally I am ensuring the context is Transient and created per request. Passing the Context through classes to ensure it's not recreated or executing two queries at the same time. I can debug and follow my code through without error. But once in production environment, I still run into this problem, and I've exhausted all of the top google result solutions...
The actual error:
A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
services.AddDbContext<APIContext>((serviceprovider, options) =>
{
options.UseSqlServer(Configuration.GetConnectionString("DatabaseConnection"));
options.UseInternalServiceProvider(serviceprovider);
});
I've tried:
Several different ways of registering my context, using all the different constructors like below. I've even tried creating as a DbContextPool instead with no luck.
services.AddDbContext<APIContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DatabaseConnection")), ServiceLifetime.Transient, ServiceLifetime.Transient);
Every one of my controllers or middleware, have the DBContext passed via the constructor as DI. Despite a few Async calls (all using await). I can not find any area that may cause this error. I've added a code sample below following a recent error, code removed for brevity.
To provide some context, every request enters my middleware for security, it's the catch in here that notifies me of the error, so I'm not sure if that's the cause? Am I registering the Interface /Middleware correctly?
//Register Request logger
services.AddTransient<IRequestLogger, RequestLoggerConcrete>();
//Constructor for Request Logger
public RequestLoggerConcrete(APIContext context)
{
_context = context;
}
//Method Called in Middleware
public async void InsertLoggingData(LoggerEntity loggerTB)
{
try
{
loggerTB.LoggerID = 0;
_context.Loggers.Add(loggerTB);
await _context.SaveChangesAsync();
}
catch (Exception)
{
throw;
}
}
//The Middleware Constructor
public ApiKeyValidatorsMiddleware(RequestDelegate next, IValidateRequest ivalidaterequest, IRequestLogger irequestlogger)
{
_next = next;
_IValidateRequest = ivalidaterequest;
_IRequestLogger = irequestlogger;
}
//Middleware Execution
public async Task Invoke(HttpContext httpContext)
{
try
{
..... Validation - API Code Code Removed ....
_IRequestLogger.InsertLoggingData(loggertb);
await _next.Invoke(httpContext);
}
catch(Exception ex) {
...Notify Email Removed...
}
}
//Controller Init
public SMSController(APIContext db)
{
_db = db;
}
//Sample Method
public ActionResult GetMessages()
{
return Json(SMSMessages.GetMessages(_db));
}
//Class Code
public static SMSMessageSimple GetMessages(APIContext _db)
{
var lst = (from sms in _db.SMSs
where sms.SentResultCode == null
&& sms.SentToDevice == false
select new SMSMessage()
{
message = sms.Message,
recepient = sms.ToNumber,
id = sms.SMSID.ToString()
}).ToArray();
var DistinctGuids = lst.Select(s => Convert.ToInt32(s.id)).Distinct();
DateTime dtNow = System.DateTime.Now.ToGMT();
_db.SMSs.Where(w => DistinctGuids.Contains(w.SMSID)).Update(u => new SMSEntity()
{
SentToDevice = true,
ModifiedBy = "API",
ModifiedDate = dtNow
});
return new SMSMessageSimple(lst);
}
I get this error on methods that don't even make any calls to the Database, yet on some, the log is inserted but the error persists.. Could it be the app pool? If two separate requests are made at the same time?
Any guidance is appreciated!!

Return String or 404 exception in asp .net web api core 3 [duplicate]

ASP.NET Core API controllers typically return explicit types (and do so by default if you create a new project), something like:
[Route("api/[controller]")]
public class ThingsController : Controller
{
// GET api/things
[HttpGet]
public async Task<IEnumerable<Thing>> GetAsync()
{
//...
}
// GET api/things/5
[HttpGet("{id}")]
public async Task<Thing> GetAsync(int id)
{
Thing thingFromDB = await GetThingFromDBAsync();
if(thingFromDB == null)
return null; // This returns HTTP 204
// Process thingFromDB, blah blah blah
return thing;
}
// POST api/things
[HttpPost]
public void Post([FromBody]Thing thing)
{
//..
}
//... and so on...
}
The problem is that return null; - it returns an HTTP 204: success, no content.
This is then regarded by a lot of client side Javascript components as success, so there's code like:
const response = await fetch('.../api/things/5', {method: 'GET' ...});
if(response.ok)
return await response.json(); // Error, no content!
A search online (such as this question and this answer) points to helpful return NotFound(); extension methods for the controller, but all these return IActionResult, which isn't compatible with my Task<Thing> return type. That design pattern looks like this:
// GET api/things/5
[HttpGet("{id}")]
public async Task<IActionResult> GetAsync(int id)
{
var thingFromDB = await GetThingFromDBAsync();
if (thingFromDB == null)
return NotFound();
// Process thingFromDB, blah blah blah
return Ok(thing);
}
That works, but to use it the return type of GetAsync must be changed to Task<IActionResult> - the explicit typing is lost, and either all the return types on the controller have to change (i.e. not use explicit typing at all) or there will be a mix where some actions deal with explicit types while others. In addition unit tests now need to make assumptions about the serialisation and explicitly deserialise the content of the IActionResult where before they had a concrete type.
There are loads of ways around this, but it appears to be a confusing mishmash that could easily be designed out, so the real question is: what is the correct way intended by the ASP.NET Core designers?
It seems that the possible options are:
Have a weird (messy to test) mix of explicit types and IActionResult depending on expected type.
Forget about explicit types, they're not really supported by Core MVC, always use IActionResult (in which case why are they present at all?)
Write an implementation of HttpResponseException and use it like ArgumentOutOfRangeException (see this answer for an implementation). However, that does require using exceptions for program flow, which is generally a bad idea and also deprecated by the MVC Core team.
Write an implementation of HttpNoContentOutputFormatter that returns 404 for GET requests.
Something else I'm missing in how Core MVC is supposed to work?
Or is there a reason why 204 is correct and 404 wrong for a failed GET request?
These all involve compromises and refactoring that lose something or add what seems to be unnecessary complexity at odds with the design of MVC Core. Which compromise is the correct one and why?
This is addressed in ASP.NET Core 2.1 with ActionResult<T>:
public ActionResult<Thing> Get(int id) {
Thing thing = GetThingFromDB();
if (thing == null)
return NotFound();
return thing;
}
Or even:
public ActionResult<Thing> Get(int id) =>
GetThingFromDB() ?? NotFound();
I'll update this answer with more detail once I've implemented it.
Original Answer
In ASP.NET Web API 5 there was an HttpResponseException (as pointed out by Hackerman) but it's been removed from Core and there's no middleware to handle it.
I think this change is due to .NET Core - where ASP.NET tries to do everything out of the box, ASP.NET Core only does what you specifically tell it to (which is a big part of why it's so much quicker and portable).
I can't find a an existing library that does this, so I've written it myself. First we need a custom exception to check for:
public class StatusCodeException : Exception
{
public StatusCodeException(HttpStatusCode statusCode)
{
StatusCode = statusCode;
}
public HttpStatusCode StatusCode { get; set; }
}
Then we need a RequestDelegate handler that checks for the new exception and converts it to the HTTP response status code:
public class StatusCodeExceptionHandler
{
private readonly RequestDelegate request;
public StatusCodeExceptionHandler(RequestDelegate pipeline)
{
this.request = pipeline;
}
public Task Invoke(HttpContext context) => this.InvokeAsync(context); // Stops VS from nagging about async method without ...Async suffix.
async Task InvokeAsync(HttpContext context)
{
try
{
await this.request(context);
}
catch (StatusCodeException exception)
{
context.Response.StatusCode = (int)exception.StatusCode;
context.Response.Headers.Clear();
}
}
}
Then we register this middleware in our Startup.Configure:
public class Startup
{
...
public void Configure(IApplicationBuilder app)
{
...
app.UseMiddleware<StatusCodeExceptionHandler>();
Finally actions can throw the HTTP status code exception, while still returning an explicit type that can easily be unit tested without conversion from IActionResult:
public Thing Get(int id) {
Thing thing = GetThingFromDB();
if (thing == null)
throw new StatusCodeException(HttpStatusCode.NotFound);
return thing;
}
This keeps the explicit types for the return values and allows easy distinction between successful empty results (return null;) and an error because something can't be found (I think of it like throwing an ArgumentOutOfRangeException).
While this is a solution to the problem it still doesn't really answer my question - the designers of the Web API build support for explicit types with the expectation that they would be used, added specific handling for return null; so that it would produce a 204 rather than a 200, and then didn't add any way to deal with 404? It seems like a lot of work to add something so basic.
You can actually use IActionResult or Task<IActionResult> instead of Thing or Task<Thing> or even Task<IEnumerable<Thing>>. If you have an API that returns JSON then you can simply do the following:
[Route("api/[controller]")]
public class ThingsController : Controller
{
// GET api/things
[HttpGet]
public async Task<IActionResult> GetAsync()
{
}
// GET api/things/5
[HttpGet("{id}")]
public async Task<IActionResult> GetAsync(int id)
{
var thingFromDB = await GetThingFromDBAsync();
if (thingFromDB == null)
return NotFound();
// Process thingFromDB, blah blah blah
return Ok(thing); // This will be JSON by default
}
// POST api/things
[HttpPost]
public void Post([FromBody] Thing thing)
{
}
}
Update
It seems as though the concern is that being explicit in the return of an API is somehow helpful, while it is possible to be explicit it is in fact not very useful. If you're writing unit tests that exercise the request / response pipeline you are typically going to verify the raw return (which would most likely be JSON, i.e.; a string in C#). You could simply take the returned string and convert it back to the strongly typed equivalent for comparisons using Assert.
This seems to be the only shortcoming with using IActionResult or Task<IActionResult>. If you really, really want to be explicit and still want to set the status code there are several ways to do this - but it is frowned upon as the framework already has a built-in mechanism for this, i.e.; using the IActionResult returning method wrappers in the Controller class. You could write some custom middleware to handle this however you'd like, however.
Finally, I would like to point out that if an API call returns null according to W3 a status code of 204 is actually accurate. Why on earth would you want a 404?
204
The server has fulfilled the request but does not need to return an
entity-body, and might want to return updated metainformation. The
response MAY include new or updated metainformation in the form of
entity-headers, which if present SHOULD be associated with the
requested variant.
If the client is a user agent, it SHOULD NOT change its document view
from that which caused the request to be sent. This response is
primarily intended to allow input for actions to take place without
causing a change to the user agent's active document view, although
any new or updated metainformation SHOULD be applied to the document
currently in the user agent's active view.
The 204 response MUST NOT include a message-body, and thus is always
terminated by the first empty line after the header fields.
I think the first sentence of the second paragraph says it best, "If the client is a user agent, it SHOULD NOT change its document view from that which caused the request to be sent". This is the case with an API. As compared to a 404:
The server has not found anything matching the Request-URI. No
indication is given of whether the condition is temporary or
permanent. The 410 (Gone) status code SHOULD be used if the server
knows, through some internally configurable mechanism, that an old
resource is permanently unavailable and has no forwarding address.
This status code is commonly used when the server does not wish to
reveal exactly why the request has been refused, or when no other
response is applicable.
The primary difference being one is more applicable for an API and the other for the document view, i.e.; the page displayed.
In order to accomplish something like that(still, I think that the best approach should be using IActionResult), you can follow, where you can throw an HttpResponseException if your Thing is null:
// GET api/things/5
[HttpGet("{id}")]
public async Task<Thing> GetAsync(int id)
{
Thing thingFromDB = await GetThingFromDBAsync();
if(thingFromDB == null){
throw new HttpResponseException(HttpStatusCode.NotFound); // This returns HTTP 404
}
// Process thingFromDB, blah blah blah
return thing;
}
From ASP.NET Core 7, a action controller can return a HttpResults type. Then you can :
public async Task<Results<Ok<Product>, NotFound>> GetAsync(int id)
{
Thing thingFromDB = await GetThingFromDBAsync();
if(thingFromDB == null)
return TypedResults.NotFound();
...
return TypedResults.Ok(thingFromDB);
}
I love this syntax, because it's explicitly indicate that return the API. But actually, the openAPI specification generator don't manage this. You can follow the progress from this Github ticket :
TypedResults metadata are not inferred for API Controllers
I too looked high and low for an answer to what to do about strongly typed responses when I wanted to return an 400 response for bad data sent into the request. My project is in ASP.NET Core Web API (.NET5.0). The solution I found was basically set the status code and return default version of the object. Here is your example with the change to set the status code to 404 and return the default object when the db object is null.
[Route("api/[controller]")]
public class ThingsController : Controller
{
// GET api/things
[HttpGet]
public async Task<IEnumerable<Thing>> GetAsync()
{
//...
}
// GET api/things/5
[HttpGet("{id}")]
public async Task<Thing> GetAsync(int id)
{
Thing thingFromDB = await GetThingFromDBAsync();
if(thingFromDB == null)
{
this.Response.StatusCode = Microsoft.AspNetCore.Http.StatusCodes.Status404NotFound;
return default(Thing);
}
// Process thingFromDB, blah blah blah
return thing;
}
// POST api/things
[HttpPost]
public void Post([FromBody]Thing thing)
{
//..
}
//... and so on...
}
ASP.NET Core 3.1 introduced filter.
Filters in ASP.NET Core allow code to run before or after specific stages in the request processing pipeline.
You can define a result filter that transform null ok result to not found result :
public class NullAsNotFoundResultFilter : IResultFilter
{
public void OnResultExecuted(ResultExecutedContext context)
{ }
public void OnResultExecuting(ResultExecutingContext context)
{
if(context.Result is ObjectResult result && result.Value == null)
{
context.Result = new NotFoundResult();
}
}
}
Finally, you need to add the filter in the MVC pipeline :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(o => o.Filters.Add<NullAsNotFoundResultFilter>());
Had another problem with same behavior - all methods return 404. The problem was in missing code block
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});

Asp.net Core: Can not access a disposed object (different errors)

I know that this problem is here a lot, but I have to say I read everything I could found for like two days and don't get my error.
I created a ASP.net Core REST API and get always different errors:
"Can not access a disposed object.."
"An exception occurred while iterating over the results of a query
for context type.."
"A second operation started on this context before a previous
operation completed"..
Maybe someone of you sees my error or can explain to me, what I'm doing wrong.
Rest-API:
// POST api/events
[HttpPost("create")]
public async Task<IActionResult> CreateAsync([FromBody] EventDTO eventDTO)
{
var newEvent = _mapper.Map<Event>(eventDTO);
try
{
await _eventService.CreateEventAsync(newEvent);
return Ok(newEvent);
}
catch (AppException ex)
{
return BadRequest(new { message = ex.Message });
}
}
Interface:
public interface IEventService
{
Task<IEnumerable<Event>> GetAllEventsAsync();
Task<Event> GetEventByIDAsync(int id);
Task<IEnumerable<Event>> GetEventByCityAsync(string city);
Task<Event> CreateEventAsync(Event newEvent);
void UpdateEventAsync(Event newEvent, Event existing, int eventId);
void DeleteEventAsync(Event existing);
}
Eventservice:
public class EventService : IEventService
{
private MeMeContext _dbContext;
public EventService(MeMeContext dbContext)
{
_dbContext = dbContext;
}
public async Task<Event> CreateEventAsync(Event newEvent)
{
_dbContext.Events.Add(newEvent);
await _dbContext.SaveChangesAsync();
return newEvent;
}
...
}
Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddMvc().
SetCompatibilityVersion(CompatibilityVersion.Version_2_2).
AddJsonOptions(opts => opts.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
services.AddDbContext<MeMeContext>(opts => opts.UseNpgsql(Configuration.GetConnectionString(DATABASE)));
services.AddScoped<MeMeContext>();
// configure DI for application services
services.AddScoped<IUserService, UserService>();
services.AddScoped<IEventService, EventService>();
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new AutoMapperProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
...
}
One thing that I don't understand also, is that I get different errors, when I start my application with Visual Studio or with "dotnet run". One thing that also happens from time to time is, that sometimes my code works, when I do other things on the REST API.
When you need more information, just ask. I'm happy with every hint that you can give me :)
Thanks in advance!
You're not awaiting an async method. As such, the code in the action moves on while that CreateEventAsync logic is running. When the response returns, the context goes away, since its lifetime is that scope.
In other words, you have essentially a race condition. If the CreateEventAsync logic happens to finish before the response returns, everything is fine. However, if it takes longer than returning the response, then the context is gone (along with your other scoped services), and you start throwing exceptions.
Long and short, use the await keyword:
await _eventService.CreateEventAsync(newEvent);
Async is not the same as running something in the background. If you want the action to be able to return before this logic completes, then you should schedule this to run on a background service instead. See: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2&tabs=visual-studio

How to integrate existing OwinMiddleware into a .Net Core 2.0 app pipeline?

I have a handful of existing Owin middleware classes, originally written for .Net 4.6 apps, that follow this pattern:
public class MyMiddleware : OwinMiddleware
{
public MyMiddleware(OwinMiddleware next) : base(next)
{ }
public override async Task Invoke(IOwinContext context)
{
// do stuff
await Next.Invoke(context)
}
}
I'm writing a new .Net Core 2.0 app that I'd like to use those middlewares in (as well as some "new style" middlewares that aren't based on Owin). I'm having trouble finding good documentation or examples of doing this, at least ones that are similar to what I have (for example, this)
The one promising thing I have found is that the Microsoft.AspNetCore.Owin package includes an extension method, UseOwin() that seems to be the path forward, but I'm unable to decipher how to use it to invoke my "legacy" middlewares above. I realize that I should use UseOwin() when configuring my IApplicationBuilder but I'm not smart enough to get the details from what I'm reading online.
What's the most straightforward way to do this?
It seems that the most straightforward way to do this is through the UseOwin extension method.
I was able to get this to work:
public class MyTestMiddleware : OwinMiddleware
{
public MyTestMiddleware(OwinMiddleware next) : base(next) {}
public override async Task Invoke(IOwinContext context)
{
System.Diagnostics.Debug.WriteLine("INVOKED");
await Next.Invoke(context);
}
}
app.UseOwin(setup => setup(next =>
{
var owinAppBuilder = new AppBuilder();
// set the DefaultApp to be next so that the ASP.NET Core pipeline runs
owinAppBuilder.Properties["builder.DefaultApp"] = next;
// add your middlewares here as Types
owinAppBuilder.Use(typeof(MyTestMiddleware));
return owinAppBuilder.Build<Func<IDictionary<string, object>, Task>>();
}));
Notice that Microsoft seems to have pushed hard against OwinMiddleware classes, as this feels very hacky.

Exception handling in asp net core

I'm looking for some guidance on how to best handle exceptions in asp net core. Based on these docs from microsoft I've setup the UseStatusCodePagesWithRedirects middleware. This works for things such as 404's. However for API requests within my code that return exceptions this doesn't work. So following this doc I've setup an exception filter. This kinda works and this is where I'm looking for help.
I have the following code in a class library so it can be re used elsewhere. A mini wrapper for my api. I'm using Flurl to construct the request. My viewModelBuilder calls GetAll.
public async Task<List<TableDto>> GetAll(int branchId)
{
var result = await _baseUrl.AppendPathSegment("/v1/Table/GetAll").WithOAuthBearerToken(await _authenticationManager.GetToken()).GetJsonAsync<List<TableDto>>();
return result;
}
If GetAll throws an exception then the following exception filter comes into play.
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
// ex.Call.Response.StatusCode // This works with the original exception but not here
if (context.HttpContext.Response.StatusCode == (int)HttpStatusCode.Forbidden)
{
// Always 200
}
}
}
I would like to do different things based on the exception status code. So for a 401 or 404 I would like to show an access denied screen or a login screen and then for other errors maybe just a general error screen. However context.HttpContext.Response.StatusCode is always 200.
Is this the right way to go about doing this? The error handing documentation suggests that middleware is better but I'm not sure if its referring to this use case as I could only get it working for 404s.
How do I get the correct status code in a exception filter?
I know that if I put a try catch around the original GetAll as below
try
{
var result = await _baseUrl.AppendPathSegment("/v1/Table/GetAll").WithOAuthBearerToken(await _authenticationManager.GetToken()).GetJsonAsync<List<TableDto>>();
return result;
}
catch (FlurlHttpException ex)
{
if (ex.Call.Response != null)
{
if (ex.Call.Response.StatusCode == HttpStatusCode.Forbidden)
{
throw new ForbiddenException();
}
}
}
then in the exception filter I can do the following
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
if (context.Exception is ForbiddenException)
{
}
}
}
and then I can do specific things for specific exceptions however thing means I have to try catch every call and voids the point of the global exception filter. Any advice would be appreciated.
Thanks
M
EDIT
Based on this answer here I could write a custom middleware and I quickly did that but I'm still stuck with the same problem of not being able to check for a 401 or 403 and only being able to get access to custom exceptions that have been throw. If I can access the status code then I think both solutions will work

Categories

Resources