Trying to show exception message in UI controller in ASP.NET MVC - c#

In the configuration file I have made it so that the property Name of the class Series IsUnique() so whenever I try to add another entity with the same name, I get a DbUpdateException. I can access the message of this exception everywhere except for the UIController.
Here we have the code in my service where I check if the series is valid and if not I throw an exception (I know this is not best practice put at this point I just want it to work first)
public void Add(SeriesDTO series)
{
if (series.Name != null && series.Startdate < series.Enddate)
{
_unitOfWork.Series.AddAsync(_mapper.Map<SeriesDTO, Series>(series));
_unitOfWork.CommitAsync();
}
else
throw new Exception("Series data is not valid");
}
Then I have my controller where I check for the DbUpdateException and if I find it I throw another exception this I prefer not to do because at this point I can access this exception message with the right message.
[HttpPost("add")]
//POST: series/add
public IActionResult Add(SeriesDTO series)
{
try
{
_seriesService.Add(series);
}
catch (DbUpdateException E)
{
throw new Exception("Series with this name already exists.");
}
return Ok(series);
}
Up until this point I can always access the exception error but when I get to my UI controller then this exception turns into a 500 internal server error and thus I can not differentiate between an invalid entity exception and a DbUpdateException and thus cannot access the right message.
public IActionResult Add(SeriesDTO serie)
{
if(serie.Enddate < DateTime.Today || serie.Enddate.Equals(null))
{
serie.Active = false;
}
else
{
serie.Active = true;
}
try
{
string data = JsonConvert.SerializeObject(serie);
var result = _client.UploadString("series/add", data);
}
catch(Exception E)
{
ExceptionModel Exception = new ExceptionModel("Something went wrong with the series data.");
return View("Exception", Exception);
//return View("Create");
}
return Redirect("Index");
}
Does anyone know how to properly send the exception through to the UI controller?

You can do that.
In you Controller method :
public IActionResult Add(SeriesDTO serie)
{
//...
ModelState.AddModelError("CustomError", "You error message as string.");
//...
}
And in you view you need that :
<span asp-validation-for="CustomError" class="text-danger"></span>
If something doesn't work, tell me and edit you post with the code of you View.

Related

Sending Proper Response from web api

I am trying to send the proper response from Web API i.e. If any error send error else send the content. The code flow for the same is as follow.
[HttpPost]
public IActionResult GetInfo([FromBody] InfoModel info)
{
try
{
var result = new Info().ProcessInfoResponse(info);
if (result==null)
return BadRequest();
else
return Ok(result);
}
catch (Exception e)
{
Log.Error("some exception", e);
return StatusCode(500, e.Message);
}
}
and from middle layer i.e. from Info class we are having different method with there own returning type and from here we are calling the third party APIs which are in another class.
public InfoResponse ProcessInfoResponse(InfoModel info)
{
try
{
var result = serviceLayer.Post<InfoModel>(info);
if (result != null)
{
// Do Something
}
else
{
Log.Error("some error");
return null;
}
}
catch (Exception ex)
{
Log.Error("some error");
return null;
}
}
public InfoRequest ProcessInfoRequest()
{
}
And in service layer we are calling the third party api like below
public HttpResponseMessage Post<T>(T parm) where T : class
{
try
{
_client.DefaultRequestHeaders.Clear();
var postTask = _client.PostAsync("some third party url", Serialize<T>(parm));
postTask.Wait();
if (postTask.Result.IsSuccessStatusCode)
{
return postTask.Result;
}
else
{
Log.Error("some error in service layer");
}
}
catch (Exception ex)
{
Log.Error("some error in service layer");
}
return default(HttpResponseMessage);
}
So my question is how can return exceptions/errors if there are any and if there is no exceptions/error then send the response as it is. This is possible by keeping middle layer returning type as is
Right now if there are no errors then I am able to send the response properly, as my middle layer is getting the expected returning type.
The issue is my middle layer methods has own returning type which is causing me to send the exception/error as is. Because I am not able to map it to proper class OR same class.
I was thinking will add new Property under all returning classes/types which will refer to the exception class, then will bind the exception/error details to that class. This will save doing lot of code changes in all places.
Any help on this appreciated !
Why not create a custom response object so that:
public IActionResult<MyCustomResponseObject> GetInfo([FromBody] InfoModel info)
public class MyCustomResponseObject
{
public string Message { get; set; }
public object Content { get; set; }
public enum State { get; set; }
}

Is it possible to show Error Message in VOID type for webapi?

I am new to Web Api 2 , I want to show some message if i hit Exception , I know that VOID is a non return type, I tried with HttpResponseMessage but it is not working or showing the "Error Message" , is that any way to show message ?
My code is as below
public void Post(int id)
{
string result = string.Empty;
try
{
//some code
}
catch (Exception ex)
{
HttpResponseMessage response2 = Request.CreateResponse(HttpStatusCode.BadRequest, "Error message");
ErrId = 999;
}
}
If you are using web api 2, then I would use IHttpActionResult as the return type, which allows you to do something like:
public IHttpActionResult Post(int id)
{
string result = string.Empty;
try
{
//some code
return Ok();
}
catch (Exception ex)
{
return BadRequest("Error message");
}
}
However, it must be noted that by default Web API has a built-in exception handler and logger that will automatically return a 500 response code for any exception, so the code above is potentially redundant unless you are catching a custom exception. Lastly the code above does not log the exception.

Better way than try-catch to pass failure messages back to Web API caller?

I've got many Web API calls that delegate to methods in data-layer classes that call my ORM (Entity Framework) and look like this:
public OperationResult DeleteThing(Guid id)
{
var result = new OperationResult() { Success = true };
using (var context = this.GetContext())
{
try
{
context.Things.Where(x => x.Id == id).Delete();
context.SaveChanges();
}
catch (Exception ex)
{
Logger.Instance.LogException(ex);
result.AddError("There was a database error deleting the thing. Check log for details.");
}
return result;
}
(You may recognize the return value as similar to the Notification pattern.)
So I have many of the same try-catch blocks and it smells bad to me. I'd like to get rid of them all and use a global exception handler to log errors instead, but in addition to logging, I also need to pass a message back to the consumer, specific to each different service method, so that the consumer can perhaps pass the message as the results of the service call appropriately. Web service consumers, e.g. our web site, ultimately can display the message generated here to clue the user in to the nature of the error.
Can anyone suggest a better way? My instinct is to go through and replace with catches of specific exception types, but that seems like a lot of work for zero practical benefit and a harm to my code maintainability.
Similar to Stuart's answer, you can also use a Filter attribute inherited from ExceptionFilterAttribute to modify the response based on any input you require.
Here's a full working example that accomplishes:
Custom message for exception type
Modifying the operation result
Fall through generic message for all exception types
ValuesController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Filters;
using Demo.Models;
namespace Demo.Controllers
{
public class ValuesController : ApiController
{
// DELETE api/values/5
[OperationError("The operation failed to delete the entity")]
public OperationResult Delete(int id)
{
throw new ArgumentException("ID is bad", nameof(id));
}
// DELETE api/values/5?specific=[true|false]
[OperationError("The operation tried to divide by zero", typeof(DivideByZeroException))]
[OperationError("The operation failed for no specific reason")]
public OperationResult DeleteSpecific(int id, bool specific)
{
if (specific)
{
throw new DivideByZeroException("DBZ");
}
else
{
throw new ArgumentException("ID is bad", nameof(id));
}
}
}
public class OperationErrorAttribute : ExceptionFilterAttribute
{
public Type ExceptionType { get; }
public string ErrorMessage { get; }
public OperationErrorAttribute(string errorMessage)
{
ErrorMessage = errorMessage;
}
public OperationErrorAttribute(string errorMessage, Type exceptionType)
{
ErrorMessage = errorMessage;
ExceptionType = exceptionType;
}
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
// Exit early for non OperationResult action results
if (actionExecutedContext.ActionContext.ActionDescriptor.ReturnType != typeof(OperationResult))
{
base.OnException(actionExecutedContext);
return;
}
OperationResult result = new OperationResult() {Success = false};
// Add error for specific exception types
Type exceptionType = actionExecutedContext.Exception.GetType();
if (ExceptionType != null)
{
if (exceptionType == ExceptionType)
{
result.AddError(ErrorMessage);
}
else
{
// Fall through
base.OnException(actionExecutedContext);
return;
}
}
else if (ErrorMessage != null)
{
result.AddError(ErrorMessage);
}
// TODO: Log exception, generate correlation ID, etc.
// Set new result
actionExecutedContext.Response =
actionExecutedContext.Request.CreateResponse(HttpStatusCode.InternalServerError, result);
base.OnException(actionExecutedContext);
}
}
}
Specific exception:
Generic exception:
You could move your logic up the stack into a custom ExceptionHandler. This is a simple example, but the basic idea is to handle specific exceptions and control the status code and (not pictured below) normalize error messages for the caller.
public class ApiExceptionHandler: ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
if (context == null) throw new ArgumentNullException("context");
LogManager.GetLoggerForCurrentClass().Error(context.Exception, "Captured in ExceptionHandler");
if (context.Exception.GetType() == typeof(NotFoundException))
{
context.Result = new NotFoundResult(context.Request);
}
else if (context.Exception.GetType() == typeof(ArgumentException))
{
// no-op - probably a routing error, which will return a bad request with info
}
else if (context.Exception.GetType() == typeof(ArgumentNullException))
{
context.Result = new BadRequestResult(context.Request);
}
else
{
context.Result = new InternalServerErrorResult(context.Request);
}
}
}
Hook this up in the WebApiConfig:
config.Services.Replace(typeof(IExceptionHandler), new ApiExceptionHandler());

500 Internal Error in ASP.NET MVC Ajax

I'm using Visual Studio 2010 and MVC 4 for my web application. This is my controller code:
public ActionResult MyController()
{
if (Request.IsAjaxRequest())
{
using (MyContainer context = new MyContainer())
{
try
{
var result = Some Query;
return PartialView("_MyView", result);
}
catch (Exception ex)
{
}
}
}
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Index", "Home", new { area = "User" });
}
else
{
return Redirect("/");
}
}
This method will be done successfully, But my ajax container not showing any things. In firebug this error raised:
NetworkError: 500 Internal Server Error + http://localhost....?X-Requested-With=XMLHttpRequest
Why does this error occur?
What do I do to solve this problem?
Thanks in advance!
The 500 Internal Server Error message might be seen in any number of ways because something was not processed fine on the server. In your case, as the commends, your MyContainer type does not implement IDisposable interface, so, you cannot use this type on the using(){ } block. When you use a type on a using block, this type have to implement IDIsposable because when it get over, the .Net Framework will remove the instance from the heap and the reference. I did some changes on your code without using block. Take a look:
public ActionResult ActionName()
{
if (Request.IsAjaxRequest())
{
try
{
MyContainer context = new MyContainer();
var result = Some Query;
return PartialView("_MyView", result);
}
catch (Exception ex)
{
// return some partial error that shows some message error
return PartialView("_Error");
}
}
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Index", "Home", new { area = "User" });
}
return Redirect("/");
}

How to report error to $.ajax without throwing exception in MVC controller?

I have a controller, and a method as defined...
[HttpPost]
public ActionResult UpdateUser(UserInformation model){
// Instead of throwing exception
throw new InvalidOperationException("Something went wrong");
// I need something like
return ExecutionError("Error Message");
// which should be received as an error to my
// $.ajax at client side...
}
Problems with Exceptions
We have to log exceptions in case of device or network errors like SQL Connectivity errors.
These messages are like validation messages for users, we dont want to log.
Throwing exceptions also floods Event Viewer.
I need some easy way to report some custom http status to my $.ajax call so that it should result an error at client side, but I do not want to throw an error.
UPDATE
I cannot change client script because it becomes inconsistent with other data source.
So far, HttpStatusCodeResult should work but it's IIS that is causing the problem here. No matter what error message I set, tried all answers, still I receive default message only.
This is where HTTP status codes come into play. With Ajax you will be able to handle them accordingly.
[HttpPost]
public ActionResult UpdateUser(UserInformation model){
if (!UserIsAuthorized())
return new HttpStatusCodeResult(401, "Custom Error Message 1"); // Unauthorized
if (!model.IsValid)
return new HttpStatusCodeResult(400, "Custom Error Message 2"); // Bad Request
// etc.
}
Here's a list of the defined status codes.
Description
What about returning an object back to your page and analyse that in your ajax callback.
Sample
[HttpPost]
public ActionResult UpdateUser(UserInformation model)
{
if (SomethingWentWrong)
return this.Json(new { success = false, message = "Uuups, something went wrong!" });
return this.Json(new { success=true, message=string.Empty});
}
jQuery
$.ajax({
url: "...",
success: function(data){
if (!data.success)
{
// do something to show the user something went wrong using data.message
} else {
// YES!
}
}
});
You can create a helper method in a base controller that will return an server error but with your custom status code. Example:
public abstract class MyBaseController : Controller
{
public EmptyResult ExecutionError(string message)
{
Response.StatusCode = 550;
Response.Write(message);
return new EmptyResult();
}
}
You will call this method in your actions when needed.
In your example:
[HttpPost]
public ActionResult UpdateUser(UserInformation model){
// Instead of throwing exception
// throw new InvalidOperationException("Something went wrong");
// The thing you need is
return ExecutionError("Error Message");
// which should be received as an error to my
// $.ajax at client side...
}
The errors (including the custom code '550') can be handled globally on client side like this:
$(document).ready(function () {
$.ajaxSetup({
error: function (x, e) {
if (x.status == 0) {
alert('You are offline!!\n Please Check Your Network.');
} else if (x.status == 404) {
alert('Requested URL not found.');
/*------>*/ } else if (x.status == 550) { // <----- THIS IS MY CUSTOM ERROR CODE
alert(x.responseText);
} else if (x.status == 500) {
alert('Internel Server Error.');
} else if (e == 'parsererror') {
alert('Error.\nParsing JSON Request failed.');
} else if (e == 'timeout') {
alert('Request Time out.');
} else {
alert('Unknow Error.\n' + x.responseText);
}
}
});
});
This is a class I wrote the sends exceptions back to ajax requests as JSON
public class FormatExceptionAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new JsonResult()
{
ContentType = "application/json",
Data = new
{
name = filterContext.Exception.GetType().Name,
message = filterContext.Exception.Message,
callstack = filterContext.Exception.StackTrace
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.StatusCode = 500;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
else
{
base.OnException(filterContext);
}
}
}
It gets registered with MVC in your application's Global.asax.cs file like so:
GlobalFilters.Filters.Add(new FormatExceptionAttribute());
I use this Particular class for Ajax errors
public class HttpStatusCodeResultWithJson : JsonResult
{
private int _statusCode;
private string _description;
public HttpStatusCodeResultWithJson(int statusCode, string description = null)
{
_statusCode = statusCode;
_description = description;
}
public override void ExecuteResult(ControllerContext context)
{
var httpContext = context.HttpContext;
var response = httpContext.Response;
response.StatusCode = _statusCode;
response.StatusDescription = _description;
base.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
base.ExecuteResult(context);
}
}
Status code is a custom HTTP Status Code and in a global Ajax Error Function I test it like:
MyNsp.ErrorAjax = function (xhr, st, str) {
if (xhr.status == '418') {
MyNsp.UI.Message("Warning: session expired!");
return;
}
if (xhr.status == "419") {
MyNsp.UI.Message("Security Token Missing");
return;
}
var msg = 'Error: ' + (str ? str : xhr.statusText);
MyNsp.UI.Message('Error. - status:' + st + "(" + msg + ")");
return;
};
Best way I have found is as follows:
// Return error status and custom message as 'errorThrown' parameter of ajax request
return new HttpStatusCodeResult(400, "Ajax error test");
Based on what BigMike posted, this was what I did in my NON MVC/WEBAPI webproject.
Response.ContentType = "application/json";
Response.StatusCode = 400;
Response.Write (ex.Message);
For what it is worth (and thanks BigMike!) It worked perfectly.

Categories