Stackoverflow Exception in MediatR Ping Sample? - c#

Followed official doc:
While invoking the handler I get
info: Microsoft.Hosting.Lifetime[0]
Content root path: C:\S\Abhi.MediatR.SampleUsingControllers Stack overflow.
Repeat 260 times:
Could you help me with, what did is missing?
To invoke the handler, I had created a controller as below:
[Route("api/[controller]")]
[ApiController]
public class PingController : ControllerBase
{
private readonly IMediator mediator;
public PingController(IMediator mediator)
{
this.mediator = mediator;
}
[HttpGet]
[Produces(typeof(string))]
public IActionResult Get()
{
try
{
var response = mediator.Send(new Ping());
return Ok(response);
}
catch (Exception ex)
{
return BadRequest($"Oops something went wrong, {ex}");
}
}
}
Created Ping Request
public class Ping : IRequest<string>
{
}
Created PingHandler:
public class PingHandler : IRequestHandler<Ping, string>
{
private readonly IMediator mediator;
public PingHandler(IMediator mediator)
{
this.mediator = mediator;
}
public async Task<string> Handle(Ping request, CancellationToken cancellationToken)
{
//return Task.FromResult("Pong");
var response = await mediator.Send(new Ping());
return response;
}
}
In Program.cs have added the MediatR
builder.Services.AddMediatR(typeof(Program));
As per doc I also tried adding as below:
builder.Services.AddMediatR(Assembly.GetExecutingAssembly());

The exception shows that you have an infinite recursion in your code. Handle method is again calling the Ping request again.
you need to uncomment the line return Task.FromResult("Pong");
and comment the below code.
var response = await mediator.Send(new Ping());
return response;

Related

How to convert MessageHandler in .NetFramework to AspNet.Core

I have below message handler to return some custom messages, and I am registering in WebAPIConfig.Register method in .NetFramework, but I am having troubles with .Net Core as it looks like message handlers are removed in .NET core, I am getting some issues converting it.
public class WebApiCustomMessageHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.NotFound)
{
request.Properties.Remove(HttpPropertyKeys.NoRouteMatched);
var errorResponse = request.CreateResponse(response.StatusCode, "resource not found.");
return errorResponse;
}
return response;
}
}
registering in WebAPIConfig.Register method:
public static void Register(HttpConfiguration config)
{
config.MessageHandlers.Add(new WebApiCustomMessageHandler());
}
.NET Core implementation:
I have below code but it gives errors on creating response and and returning it.
public class WebApiCustomMiddleware
{
private readonly RequestDelegate _next;
public WebApiCustomMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
//process context.Request
await _next(context);
//process context.Response
if (context.Response.StatusCode == HttpStatusCode.NotFound)
{
context.Request.Properties.Remove(HttpPropertyKeys.NoRouteMatched);
var errorResponse = context.Request.CreateResponse(context.Response.StatusCode, "resource not found.");
return errorResponse;
}
return response;
}
}
How do I create response and return in .net core middleware it is giving errors as it doesn't have context.Request.Properties, context.Request.CreateResponse, and also what would be the return type ?
context.Request.Properties.Remove(HttpPropertyKeys.NoRouteMatched);
var errorResponse = context.Request.CreateResponse(context.Response.StatusCode, "resource not found.");
return errorResponse;
Please suggest.
Below is the Implementation (thanks to #King King):
public class WebApiCustomMiddleware
{
private readonly RequestDelegate _next;
public WebApiCustomMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
//process context.Request
//to do like validating api keys, request headers etc..
await _next(context);
//process context.Response
if (context.Response.StatusCode == (int)HttpStatusCode.NotFound)
{
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(new
{
statusCode = 404,
message = "resource not found"
}));
}
else if(...)
{}
}
}
And register in statup.cs
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<WebApiCustomMiddleware>();
}

how to handle Extension methods in controller?

I really Didn't get proper Title for this question. Please correct it if its misleading.
I have a WebApi controller, where there are multiple validation check are there. Controller sample code
public async Task<IActionResult> UploadFile(IFormFile file)
{
try
{
return file.IsValid();
//some more Functionality
}
}
Here Isvalid is a Extension method where the code is as follows
public static IActionResult PrepareResult(this ControllerBase controller, IFormFile file)
{
if (file== null)
{
return controller.Badrequest("No data sent");
}
return controller.Ok();
}
Issue:- In current scenario , if the file is Null then Extension method will be returning Badrequest() & the same will be returned to the client. But if file is not null then It's going to return Ok() & same will be returned to the Clint, where as i have more code to execute(i.e.//some more Functionality).
I don't want to return controller.Ok(), so that for positive scenario i can continue with my remaining code.
NB:- i don't want to assign to any variable & check with If condition. In order to avoid if condition only i am using extension methods.
Not sure why you don't want to assign varaibles and avoid if condition as this is the most efficient way. You can use exception handling, though that comes with a performance cost.
public static void EnsureFileIsValid(this IFormFile file)
{
if(file == null) { throw new InvalidOperationException("No data sent"); }
}
public async Task<IActionResult> UploadFile(IFormFile file)
{
try
{
file.EnsureFileIsValid();
return Ok();
}
catch(InvalidOperationException ex)
{
return BadRequest(ex.Message);
}
}
You can pass a action to your method like :
public static IActionResult PrepareResult(this ControllerBase controller, IFormFile file, Action<IFormFile> work)
{
if (file == null)
{
return controller.Badrequest("No data sent");
}
work(file);
return controller.Ok();
}
In you action, the use is :
public class FilesController : ControllerBase
{
[HttpPost]
public IActionResult UploadFile([FromServices]IFileService fileService, IFormFile file)
{
return Ok(PrepareResult(file, fileService.Upload));
}
}
But maybe you can consider to use validation.
In validation step, you enforce a parameter isn't null with RequiredAttribute.
ApiControllerAttribute enforce the validation before the action is called. If the validation fail, then ASP.NET Core return directly BadRequest and the action isn't called.
In this example, if the parameter file is null then the action isn't called and it return BadRequest :
[ApiController]
public class FilesController : ControllerBase
{
[HttpPost]
public IActionResult UploadFile([Required]IFormFile file)
{
//Do something
return Ok();
}
[HttpPut]
public IActionResult UploadFileBis([Required] IFormFile file)
{
//Do something
return Ok();
}
}
PS : You can use [ApiControllerAttribute] at assembly level, it will be enable to all controllers in assembly :
[assembly: ApiController]
If you want to verify conditions and define the error response in the same lower layer class, here is a solution.
First, let's create a custom exception that will hold the http status code and message to return:
// serialization implementation redacted
public class InfrastructureException : Exception
{
public HttpStatusCode HttpStatusCode { get; }
public InfrastructureException(HttpStatusCode code, string message) : base(message)
{
HttpStatusCode = code;
}
}
We need a class to handle the response serialization:
public class ExceptionResponse
{
public int StatusCode { get; set; }
public string Message { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
Then create a middleware that handle exceptions:
public class InfrastructureExceptionMiddleware
{
private readonly RequestDelegate next;
public InfrastructureExceptionMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task InvokeAsync(HttpContext httpContext, IHostEnvironment hostEnvironment)
{
try
{
await this.next(httpContext);
}
catch (Exception ex)
{
await HandleExceptionAsync(httpContext, ex);
}
}
private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
ExceptionResponse response = exception is InfrastructureException infrastructureException
? new ExceptionResponse()
{
StatusCode = (int)infrastructureException.HttpStatusCode,
Message = infrastructureException.Message
}
: new ExceptionResponse()
{
StatusCode = (int)HttpStatusCode.InternalServerError,
Message = ReasonPhrases.GetReasonPhrase(context.Response.StatusCode)
};
return context.Response.WriteAsync(response.ToString());
}
}
Now, we need to register our middleware:
public class Startup
{
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// redacted
app.UseMiddleware<InfrastructureExceptionMiddleware>();
// redacted
}
}
In the controller, we delegate the validation to the extension method:
public async Task<IActionResult> UploadFile(IFormFile file)
{
file.IsValid();
// now you can consider file is valid
}
Finally, in the extension method, we throw the exception:
public static void IsValid(this IFormFile file)
{
if(file == null)
{
throw new InfrastructureException(HttpStatusCode.BadRequest, "No data sent");
}
if(...) // condition for Http NotFound
{
throw new InfrastructureException(HttpStatusCode.NotFound, "Data not found");
}
// other validation conditions
}

Get data from REST API using .NET core

I am trying to get data from REST API using HttpClient, but I have an issue.
Using the same service but from Console Application, everything works fine.
From Controller everything works fine, but when GetAsync(url) method from HttpHandler is calling, it looks like something works in the background but nothing happen..
This is my service:
public class UserService : IUsersService
{
private const string url = "https://jsonplaceholder.typicode.com/users";
private IHttpHandler httpHandler;
public UserService(IHttpHandler httpHandler)
{
this.httpHandler = httpHandler;
}
public List<User> GetAllUsers()
{
HttpResponseMessage response = httpHandler.Get(url);
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<List<User>>().Result;
}
//Nice to add Logging system that we cannot connect into following URL
return new List<User>();
}
public User GetUserById(int userId)
{
HttpResponseMessage response = httpHandler.Get(
string.Concat(url,"?id=",userId));
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<List<User>>().Result.FirstOrDefault();
}
//Nice to add Logging system that we cannot connect into following URL
return null;
}
}
This is my Controller (using WEB API controller, httpClient is not getting data from REST API)
public class UsersController : ApiController
{
IUsersService userService;
public UsersController(IUsersService userService)
{
this.userService = userService;
}
public List<User> GetUsers()
{
return userService.GetAllUsers();
}
public User GetUser(int userId)
{
return userService.GetUserById(userId);
}
}
And this is my HttpHandler which is currently using HttpClient:
public class HttpHandler : IHttpHandler
{
private HttpClient client = new HttpClient();
public HttpResponseMessage Get(string url)
{
return GetAsync(url).Result;
}
public HttpResponseMessage Post(string url, HttpContent content)
{
return PostAsync(url, content).Result;
}
public async Task<HttpResponseMessage> GetAsync(string url)
{
return await client.GetAsync(url);
}
public async Task<HttpResponseMessage> PostAsync(string url, HttpContent content)
{
return await client.PostAsync(url, content);
}
}
This is my console Application which is working well and shows correct result:
class Program
{
static void Main(string[] args)
{
HttpHandler handler = new HttpHandler();
UserService service = new UserService(handler);
var users = service.GetAllUsers();
Console.WriteLine(users[0].Email);
Console.ReadKey();
}
}
I don't really know, what could be a problem.
During digging network, I found the solution to my problem https://stackoverflow.com/a/10369275/5002910
In the HttpHandler class in the GetAsync method I have to return
return await client.GetAsync(url).ConfigureAwait(continueOnCapturedContext:false);

MongoDB / Asp.Net Core remove method error with "filter"

I am working on a Url Shortener in Asp.Net Core and using MongoDB.
I currently have a working Get method and a working Create method.
I ran into an issue with my Delete method and this is the message I get:
Argument 1: cannot convert from
'MongoDB.Driver.FilterDefinition)', candidates
are: System.Threading.Tasks.Task
DeleteOneAsync(MongoDB.Driver.FilterDefinition,
System.Threading.CancellationToken)(in interface
IMongoCollection)
System.Threading.Tasks.Task
DeleteOneAsync(this
MongoDB.Driver.IMongoCollection,
System.Linq.Expressions.Expression>,
System.Threading.CancellationToken) (in class
IMongoCollectionExtensions)
The error has something to do with this ".DeleteOneAsync(filter);" in my 'ShortUrlRepository' class:
public async Task<ShortUrl> Delete(ShortUrl su)
{
var filter = Builders<Url>.Filter.Eq("Su", su);
return await _db.Urls.DeleteOneAsync(filter);
}
My ShortUrlsController class seems to be working just fine but I will post it in case:
namespace ShortenUrls.Controllers
{
[Route("api/codes")]
public class ShortUrlsController : Controller
{
private readonly ShortUrlRepository _repo;
//private readonly IShortUrlService _service;
public ShortUrlsController(ShortUrlRepository repo /*IShortUrlService service*/)
{
_repo = repo;
//_service = service;
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(string id)
{
var su = await _repo.GetAsync(id);
if (su == null)
return NotFound();
return Ok(su);
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] ShortUrl su)
{
await _repo.CreateAsync(su);
return Ok(su);
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete (ShortUrl su)
{
try
{
if (su == null)
return new NotFoundResult();
await _repo.Delete(su);
return Ok("Deleted Successfully");
}
catch (Exception ex)
{
return BadRequest(ex.ToString());
}
}
}
}
I have tried other remove methods but have gotten similar errors so maybe I am missing something?
If anyone can offer some suggestions I would greatly appreciate it as I am new to Asp.Net Core and I am having very little success finding a solution to this error. If I can provide anymore information please let me know.
Creating the variable 'r' and returning it solved the 'Argument 1 error':
public async Task<bool> Delete(ObjectId id)
{
var filter = Builders<ShortUrl>.Filter.Eq(x => x.Id, id);
var r = await _db.Urls.DeleteOneAsync(filter);
return r.DeletedCount > 0;
}
I made other changes that were unrelated to this error but were necessary to make the Delete method work properly. Here are the changes I had to make to my 'ShortUrlsController' class:
[HttpDelete("{id}")]
public async Task<IActionResult> Delete (string id)
{
return (await _repo.Delete(ObjectId.Parse(id)))
? (IActionResult) Ok("Deleted Successfully")
: NotFound();
}

ASP.NET Core equivalent of ASP.NET MVC 5's HttpException

In ASP.NET MVC 5 you could throw a HttpException with a HTTP code and this would set the response like so:
throw new HttpException((int)HttpStatusCode.BadRequest, "Bad Request.");
HttpException does not exist in ASP.NET Core. What is the equivalent code?
I implemented my own HttpException and supporting middleware which catches all HttpException's and turns them into the corresponding error response. A short extract can be seen below. You can also use the Boxed.AspNetCore Nuget package.
Usage Example in Startup.cs
public void Configure(IApplicationBuilder application)
{
application.UseIISPlatformHandler();
application.UseStatusCodePagesWithReExecute("/error/{0}");
application.UseHttpException();
application.UseMvc();
}
Extension Method
public static class ApplicationBuilderExtensions
{
public static IApplicationBuilder UseHttpException(this IApplicationBuilder application)
{
return application.UseMiddleware<HttpExceptionMiddleware>();
}
}
Middleware
internal class HttpExceptionMiddleware
{
private readonly RequestDelegate next;
public HttpExceptionMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await this.next.Invoke(context);
}
catch (HttpException httpException)
{
context.Response.StatusCode = httpException.StatusCode;
var responseFeature = context.Features.Get<IHttpResponseFeature>();
responseFeature.ReasonPhrase = httpException.Message;
}
}
}
HttpException
public class HttpException : Exception
{
private readonly int httpStatusCode;
public HttpException(int httpStatusCode)
{
this.httpStatusCode = httpStatusCode;
}
public HttpException(HttpStatusCode httpStatusCode)
{
this.httpStatusCode = (int)httpStatusCode;
}
public HttpException(int httpStatusCode, string message) : base(message)
{
this.httpStatusCode = httpStatusCode;
}
public HttpException(HttpStatusCode httpStatusCode, string message) : base(message)
{
this.httpStatusCode = (int)httpStatusCode;
}
public HttpException(int httpStatusCode, string message, Exception inner) : base(message, inner)
{
this.httpStatusCode = httpStatusCode;
}
public HttpException(HttpStatusCode httpStatusCode, string message, Exception inner) : base(message, inner)
{
this.httpStatusCode = (int)httpStatusCode;
}
public int StatusCode { get { return this.httpStatusCode; } }
}
In the long term, I would advise against using exceptions for returning errors. Exceptions are slower than just returning an error from a method.
After a brief chat with #davidfowl, it seems that ASP.NET 5 has no such notion of HttpException or HttpResponseException that "magically" turn to response messages.
What you can do, is hook into the ASP.NET 5 pipeline via MiddleWare, and create one that handles the exceptions for you.
Here is an example from the source code of their error handler middleware which will set the response status code to 500 in case of an exception further up the pipeline:
public class ErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly ErrorHandlerOptions _options;
private readonly ILogger _logger;
public ErrorHandlerMiddleware(RequestDelegate next,
ILoggerFactory loggerFactory,
ErrorHandlerOptions options)
{
_next = next;
_options = options;
_logger = loggerFactory.CreateLogger<ErrorHandlerMiddleware>();
if (_options.ErrorHandler == null)
{
_options.ErrorHandler = _next;
}
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError("An unhandled exception has occurred: " + ex.Message, ex);
if (context.Response.HasStarted)
{
_logger.LogWarning("The response has already started,
the error handler will not be executed.");
throw;
}
PathString originalPath = context.Request.Path;
if (_options.ErrorHandlingPath.HasValue)
{
context.Request.Path = _options.ErrorHandlingPath;
}
try
{
var errorHandlerFeature = new ErrorHandlerFeature()
{
Error = ex,
};
context.SetFeature<IErrorHandlerFeature>(errorHandlerFeature);
context.Response.StatusCode = 500;
context.Response.Headers.Clear();
await _options.ErrorHandler(context);
return;
}
catch (Exception ex2)
{
_logger.LogError("An exception was thrown attempting
to execute the error handler.", ex2);
}
finally
{
context.Request.Path = originalPath;
}
throw; // Re-throw the original if we couldn't handle it
}
}
}
And you need to register it with StartUp.cs:
public class Startup
{
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerfactory)
{
app.UseMiddleWare<ExceptionHandlerMiddleware>();
}
}
Alternatively, if you just want to return an arbitrary status code and aren't concerned with the Exception-based approach, you can use
return new HttpStatusCodeResult(400);
Update: as of .NET Core RC 2, the Http prefix is dropped. It is now:
return new StatusCodeResult(400);
The Microsoft.AspNet.Mvc.Controller base class exposes a HttpBadRequest(string) overload which takes an error message to return to the client. So from within a controller action, you could call:
return HttpBadRequest("Bad Request.");
Ultimately my nose says any private methods called from within a controller action should either be fully http-context-aware and return an IActionResult, or perform some other small task completely isolated from the fact that it's inside of an http pipeline. Granted this is my personal opinion, but a class that performs some piece of business logic should not be returning HTTP status codes, and instead should be throwing its own exceptions which can be caught and translated at the controller/action level.
There is no equivalent in ASP.NET Core itself. As others have said, the way to implement this is with a middleware and your own exceptions.
The Opw.HttpExceptions.AspNetCore NuGet package does exactly this.
Middleware and extensions for returning exceptions over HTTP, e.g. as ASP.NET Core Problem Details. Problem Details are a machine-readable format for specifying errors in HTTP API responses based on https://www.rfc-editor.org/rfc/rfc7807. But you are not limited to returning exception results as Problem Details, but you can create your own mappers for your own custom formats.
It is configurable and well documented.
Here is the list of provided exceptions out of the box:
4xx
400 BadRequestException
400 InvalidModelException
400 ValidationErrorException<T>
400 InvalidFileException
401 UnauthorizedException
403 ForbiddenException
404 NotFoundException
404 NotFoundException<T>
409 ConflictException
409 ProtectedException
415 UnsupportedMediaTypeException
5xx
500 InternalServerErrorException
500 DbErrorException
500 SerializationErrorException
503 ServiceUnavailableException
Here is an extended version of #muhammad-rehan-saeed answer.
It logs exceptions conditionaly and disables http cache.
If you use this and UseDeveloperExceptionPage, you should call UseDeveloperExceptionPage before this.
Startup.cs:
app.UseMiddleware<HttpExceptionMiddleware>();
HttpExceptionMiddleware.cs
/**
* Error handling: throw HTTPException(s) in business logic, generate correct response with correct httpStatusCode + short error messages.
* If the exception is a server error (status 5XX), this exception is logged.
*/
internal class HttpExceptionMiddleware
{
private readonly RequestDelegate next;
public HttpExceptionMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await this.next.Invoke(context);
}
catch (HttpException e)
{
var response = context.Response;
if (response.HasStarted)
{
throw;
}
int statusCode = (int) e.StatusCode;
if (statusCode >= 500 && statusCode <= 599)
{
logger.LogError(e, "Server exception");
}
response.Clear();
response.StatusCode = statusCode;
response.ContentType = "application/json; charset=utf-8";
response.Headers[HeaderNames.CacheControl] = "no-cache";
response.Headers[HeaderNames.Pragma] = "no-cache";
response.Headers[HeaderNames.Expires] = "-1";
response.Headers.Remove(HeaderNames.ETag);
var bodyObj = new {
Message = e.BaseMessage,
Status = e.StatusCode.ToString()
};
var body = JsonSerializer.Serialize(bodyObj);
await context.Response.WriteAsync(body);
}
}
}
HTTPException.cs
public class HttpException : Exception
{
public HttpStatusCode StatusCode { get; }
public HttpException(HttpStatusCode statusCode)
{
this.StatusCode = statusCode;
}
public HttpException(int httpStatusCode)
: this((HttpStatusCode) httpStatusCode)
{
}
public HttpException(HttpStatusCode statusCode, string message)
: base(message)
{
this.StatusCode = statusCode;
}
public HttpException(int httpStatusCode, string message)
: this((HttpStatusCode) httpStatusCode, message)
{
}
public HttpException(HttpStatusCode statusCode, string message, Exception inner)
: base(message, inner)
{
}
public HttpException(int httpStatusCode, string message, Exception inner)
: this((HttpStatusCode) httpStatusCode, message, inner)
{
}
}
I had better results with this code than with :
UseExceptionHandler:
automatically logs every "normal" exceptions (ex 404).
disabled in dev mode (when app.UseDeveloperExceptionPage is called)
cannot catch only specific exceptions
Opw.HttpExceptions.AspNetCore: logs exception when everything works fine
See also ASP.NET Core Web API exception handling
Starting from ASP.NET Core 3 you can use ActionResult to return HTTP status code:
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<ITEMS_TYPE> GetByItemId(int id)
{
...
if (result == null)
{
return NotFound();
}
return Ok(result);
}
More details are here: https://learn.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-3.1

Categories