asp.net core - pass exception description to client (e.g. Angular) - asp.net

I've been trying to fetch exception text from backend (ASP.NET Core) in Angular app.
I've seen examples where the controller action's return type is JsonResult or ActionResult.
In this case we can do the following:
[HttpGet]
public ActionResult GetSomething()
{
...
}
catch (Exception ex)
{
return Json(new { error = $"{ex.GetType().FullName}: '{ex.Message}'" }, JsonRequestBehavior.AllowGet);
}
}
All controller actions I have return DTOs, e.g.
public async Task<List<OrderDto>> GetMany(long clientId)
{
....
Since I'm returning DTO - I can't seem to return Json, so the approach above doesn't work.
I wonder if there's a way to pass exception description other than via Json(...).
Does anyone have an idea of how to handle this?

You can create a middleware:
public class ExceptionHandleMiddleware
{
private readonly RequestDelegate next;
public ExceptionHandleMiddleware(RequestDelegate next)
{
this.next = next ?? throw new ArgumentNullException(nameof(next));
}
public async Task Invoke(HttpContext context)
{
try
{
await next(context);
}
catch (Exception ex)
{
context.Response.Clear();
context.Response.ContentType = #"application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(new { error = $"{ex.GetType().FullName}: '{ex.Message}'"}));
}
}
}
And then add it to application Builder in Configure method:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
....
app.UseMiddleware<ExceptionHandleMiddleware>();
}
Pay attention that with this middleware you shouldn't catch your exception on Controller level.

Related

HttpClientFactory HttpClient Cannot access a disposed object

I am implementing a service for posting data to an external RestAPI.
What I did as below:
Service definition:
public class ExternalOutputService : IExternalOutputService
{
private readonly HttpClient _httpClient;
public ExternalOutputService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<object> Send(object data, string baseAddress, string uri)
{
try
{
HttpResponseMessage response = await _httpClient.PostAsJsonAsync(uri, data);
response.EnsureSuccessStatusCode();
}
catch (Exception ex) {
Console.Write(ex.Message);
}
return response.Content;
}
}
Add services.AddHttpClient<IExternalOutputService, ExternalOutputService>(); in Startup
Use the injected the service and call the Send method.
public class ConfigurableOutput
{
private readonly IExternalOutputService _externalOutputService;
public ConfigurableOutput(IExternalOutputService externalOutputService)
{
_externalOutputService = externalOutputService;
}
public override async Task<object> Run(object input)
{
await _externalOutputService.Send(input.data, "URI address");
}
}
But when I run it and hit the httpclient send line, it would throw an exception with 'Cannot access a disposed object'
Anyone has idea or advice?
Hi guys, I finally find the issue.
In another DI extension class, the class has already been registered.
context.Services.AddTransient<IExternalOutputService, ExternalOutputService>();
So removed this line and only keeps
services.AddHttpClient<IExternalOutputService, ExternalOutputService>();
It is all good now.

Deserialize HttpContext context response in asp.net core middleware

I want to Deserialize HttpContext context response in my exception middleware
like
context.Response.Deserialize<myclasss>();
if it deserilizes successfully according to myclass i want to send a specific respponse object back like
StatusCode = (int)HttpStatusCode.InternalServerError,
Message = "something went wrong"
There is a better way of resolving the same. Define a model for your error messages. Let that be ApiError
public class ErrorDetailsVM
{
public string Message { get; set; }
public string Exception { get; set; }
public string StackTrace { get; set; }
public string Source { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
Then you can create your own Middleware that will always send back the ErrorDetailsVM object after serializing it. Following is an example of the middleware.
public class DeveloperExceptionMiddleware
{
private readonly ILoggerFactory _loggerFactory;
private readonly RequestDelegate _next;
public DeveloperExceptionMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext) //If you have additional dependencies, you can inject them here.
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
//Log your errors here. Then send back the client a response.
await HandleExceptionAsync(httpContext, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(new ErrorDetailsVM()
{
Message = exception.Message,
Exception=exception.ToString(),
StackTrace=exception.StackTrace,
Source = exception.Source
}.ToString());
}
}
And finally, inside your Startup.cs you can add the following lines to the Configure method.
if (env.IsDevelopment())
app.UseCustomDeveloperException();
Similarly, you can have a separate UseCustomProductionException middleware for production that sends out less internal information. Let me know if this solves your issue.
Happy Coding <3
in this case i use
context.Response.ReadAsString().Deserilize<MyClass>()
using NewtonSoft.Josn library to deserilize

Calling Web-Api from a SignalR Hub

I am creating a WebApi server with integrated SignalR Hubs. For simplicity's sake I am using a Controller which is operating on a List.
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
public static List<string> Source { get; set; } = new List<string>();
public static int counter = 0;
private IHubContext<ValuesHub, IValuesClient> hubContext;
public ValuesController(IHubContext<ValuesHub, IValuesClient> hub)
{
Source.Add("bla" + counter);
counter++;
Source.Add("bla" + counter);
counter++;
this.hubContext = hub;
}
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return Source;
}
// GET api/values/x
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return Source[id];
}
// POST api/values
[HttpPost]
public void Post([FromBody] string value)
{
Source.Add(value);
}
// PUT api/values/x
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
Source[id] = value;
}
// DELETE api/values/x
[HttpDelete("{id}")]
public void Delete(int id)
{
var item = Source[id];
Source.Remove(item);
Console.WriteLine("Outgoing message!");
hubContext.Clients.All.ReceiveMessage("Message incoming", "Blaaaaa");
}
}
}
My Hub doesn't do anything special yet:
public interface IValuesClient
{
Task ReceiveMessage(string value, string message);
Task ReceiveMessage(string message);
}
public class ValuesHub : Hub<IValuesClient>
{
// private static ValuesController ctrl = Glo
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "Users");
Console.WriteLine("Client connected - Client-Id: {0}", Context.ConnectionId);
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, "Users");
Console.WriteLine("Client disconnected - Client-Id: {0}", Context.ConnectionId);
Console.WriteLine("Disconnection due to: {0}", exception);
await base.OnDisconnectedAsync(exception);
}
public async Task MessageToAll(string user, string message)
{
Console.WriteLine("SendMessage - User: {0} - Message: {1}", user, message);
await Clients.All.ReceiveMessage(user, message);
}
public async Task MessageToCaller(string message)
{
Console.WriteLine("SendMessageToCaller: {0}", message);
await Clients.Caller.ReceiveMessage(message);
}
}
}
Also for simplicity's sake I will not go into detail why I want to achieve this, but I want the server to wait for a certain amount of time and then delete the according value, after a disconnection is detected. Let's say I want to simply delete the first element in my Source list.
How would I access the according Controller-functions from inside my OnDisconnectedAsync function?
One idea I came up with is to create a HttpClient inside my Hub and let the Hub act as a client here by calling e. g. DELETE: http://localhost:5000/api/values/0. I have to admit this sounds like a rather horrible approach, though.
So If I understand your problem is that you are having is that you want to access the methods on the controller from your hubs?
If this is the case - It seems to me that you have a fundamental design flaw. I would create a service that handles all the things your controller is doing, and then inject this service directly into the hub. Then you can use that service directly in the hub on the overrides and operate on your list . If this is unclear I can Provide an example.
public class ValuesHub : Hub<IValuesClient>
{
IListService _listService;
public ValuesHub (IListService listService)
{
_listService = listService;
}
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "Users");
Console.WriteLine("Client connected - Client-Id: {0}", Context.ConnectionId);
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, "Users");
Console.WriteLine("Client disconnected - Client-Id: {0}", Context.ConnectionId);
Console.WriteLine("Disconnection due to: {0}", exception);
//Call your methods here.
_listService.RemoveFirstElement();
await base.OnDisconnectedAsync(exception);
}
public async Task MessageToAll(string user, string message)
{
Console.WriteLine("SendMessage - User: {0} - Message: {1}", user, message);
await Clients.All.ReceiveMessage(user, message);
}
public async Task MessageToCaller(string message)
{
Console.WriteLine("SendMessageToCaller: {0}", message);
await Clients.Caller.ReceiveMessage(message);
}
}
}
Thats your hub - See service example below
public class ListService : IListService
{
public void RemoveFirstElement()
{
//Delete Your Element here
}
}
public interface IListService
{
void RemoveFirstElement();
}
And then your startup.cs
services.AddSingleton<IListService,ListService>();

Return custom HTTP code from ActionFilterAttribute

I use the code below to throttle my ASP.NET Web Api:
public class Throttle : ActionFilterAttribute
{
public override async Task OnActionExecutingAsync(HttpActionContext context, CancellationToken cancellationToken)
{
// ...
if (throttle)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Conflict));
}
}
}
However, I cannot return error code 429, because it's not in HttpStatusCode enum. Is there a way to return a custom error code?
I found this over here.
var response = new HttpResponseMessage
{
StatusCode = (HttpStatusCode)429,
ReasonPhrase = "Too Many Requests",
Content = new StringContent(string.Format(CultureInfo.InvariantCulture, "Rate limit reached. Reset in {0} seconds.", data.ResetSeconds))
};
response.Headers.Add("Retry-After", data.ResetSeconds.ToString(CultureInfo.InvariantCulture));
actionContext.Response = response;
Hope this helps
This is what I did based on another response on StackOverflow.
Create Class (in controller file worked for me)
public class TooManyRequests : IHttpActionResult
{
public TooManyRequests()
{
}
public TooManyRequests(string message)
{
Message = message;
}
public string Message { get; private set; }
public HttpResponseMessage Execute()
{
HttpResponseMessage response = new HttpResponseMessage((HttpStatusCode)429);
if (!string.IsNullOrEmpty(Message))
{
response.Content = new StringContent(Message); // Put the message in the response body (text/plain content).
}
return response;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Execute());
}
}
Use in controller
public IHttpActionResult Get()
{
// with message
return new TooManyRequests("Limited to 5 request per day. Come back tomorrow.");
// without message
// return new TooManyRequests();
}

AuthorizeAttribute at asp.net MVC cannot write log to file

I try to customize AuthorizeAttribute for authentication at restful service. I want to write information into a log file. But it didn't work. I don't see any log file at my log path. (I have enable permission to IIS_USER)
Also, I even found that if AuthorizeCore return false, my test client can still get the result. Is somewhere in my code wrong? Or something I misunderstand how AuthorizeAttribute work?
PS. I also found that if I switch log part to ApiController it will work! It's so weird!
Filter
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
using (StreamWriter sw = new StreamWriter(#"C:\inetpub\wwwroot\mvctest\logs\Trace.log", true, Encoding.UTF8))
{
IEnumerable<String> header = httpContext.Request.Headers.GetValues("User");
foreach (String str in header)
{
sw.WriteLine(str);
}
sw.Flush();
}
return base.AuthorizeCore(httpContext);
}
}
ApiController
public class ValuesController : ApiController
{
// GET api/values
[MyAuthorizeAttribute]
public List<String> Get()
{
return new List<String> { "Success", "Get" };
}
// GET api/values/5
[MyAuthorizeAttribute]
public String Get(int id)
{
return "Success";
}
}
TestClient
static void Main(string[] args)
{
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost/mvctest/api/values/5");
request.Headers.Add("User", "Darkurvivor");
using (WebResponse response = request.GetResponse())
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
String data = sr.ReadToEnd();
Console.WriteLine(data);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.ReadKey();
}
There are two AuthorizeAttributes. One for WebApi and one for MVC. WebApi uses System.Web.http.AuthorizeAttribute, while MVC uses System.Web.Mvc.AuthorizeAttribute. You canoot use one with the other. So, no. It's not weird at all.

Resources