I was trying to return an error to the call to the controller as advised in
This link so that client can take appropriate action.
The controller is called by javascript via jquery AJAX. I am getting the Json object back only if I don't set the status to error.
Here is the sample code
if (response.errors.Length > 0)
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(response);
I get the Json if I don't set the statuscode.
If I set the status code I get the status code back but not the Json error object.
Update
I want to send an Error object as JSON so that it can be handled error callback of ajax.
The neatest solution I've found is to create your own JsonResult that extends the original implementation and allows you to specify a HttpStatusCode:
public class JsonHttpStatusResult : JsonResult
{
private readonly HttpStatusCode _httpStatus;
public JsonHttpStatusResult(object data, HttpStatusCode httpStatus)
{
Data = data;
_httpStatus = httpStatus;
}
public override void ExecuteResult(ControllerContext context)
{
context.RequestContext.HttpContext.Response.StatusCode = (int)_httpStatus;
base.ExecuteResult(context);
}
}
You can then use this in your controller action like so:
if(thereWereErrors)
{
var errorModel = new { error = "There was an error" };
return new JsonHttpStatusResult(errorModel, HttpStatusCode.InternalServerError);
}
I found the solution here
I had to create a action filter to override the default behaviour of MVC
Here is my exception class
class ValidationException : ApplicationException
{
public JsonResult exceptionDetails;
public ValidationException(JsonResult exceptionDetails)
{
this.exceptionDetails = exceptionDetails;
}
public ValidationException(string message) : base(message) { }
public ValidationException(string message, Exception inner) : base(message, inner) { }
protected ValidationException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
: base(info, context) { }
}
Note that I have constructor which initializes my JSON. Here is the action filter
public class HandleUIExceptionAttribute : FilterAttribute, IExceptionFilter
{
public virtual void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.Exception != null)
{
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
filterContext.Result = ((ValidationException)filterContext.Exception).myJsonError;
}
}
Now that I have the action filter, I will decorate my controller with the filter attribute
[HandleUIException]
public JsonResult UpdateName(string objectToUpdate)
{
var response = myClient.ValidateObject(objectToUpdate);
if (response.errors.Length > 0)
throw new ValidationException(Json(response));
}
When the error is thrown the action filter which implements IExceptionFilter get called and I get back the Json on the client on error callback.
There is a very elegant solution to this problem, just configure your site via web.config:
<system.webServer>
<httpErrors errorMode="DetailedLocalOnly" existingResponse="PassThrough"/>
</system.webServer>
Source: https://serverfault.com/questions/123729/iis-is-overriding-my-response-content-if-i-manually-set-the-response-statuscode
A simple way to send a error to Json is control Http Status Code of response object and set a custom error message.
Controller
public JsonResult Create(MyObject myObject)
{
//AllFine
return Json(new { IsCreated = True, Content = ViewGenerator(myObject));
//Use input may be wrong but nothing crashed
return Json(new { IsCreated = False, Content = ViewGenerator(myObject));
//Error
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return Json(new { IsCreated = false, ErrorMessage = 'My error message');
}
JS
$.ajax({
type: "POST",
dataType: "json",
url: "MyController/Create",
data: JSON.stringify(myObject),
success: function (result) {
if(result.IsCreated)
{
//... ALL FINE
}
else
{
//... Use input may be wrong but nothing crashed
}
},
error: function (error) {
alert("Error:" + erro.responseJSON.ErrorMessage ); //Error
}
});
Building on the answer from Richard Garside, here's the ASP.Net Core version
public class JsonErrorResult : JsonResult
{
private readonly HttpStatusCode _statusCode;
public JsonErrorResult(object json) : this(json, HttpStatusCode.InternalServerError)
{
}
public JsonErrorResult(object json, HttpStatusCode statusCode) : base(json)
{
_statusCode = statusCode;
}
public override void ExecuteResult(ActionContext context)
{
context.HttpContext.Response.StatusCode = (int)_statusCode;
base.ExecuteResult(context);
}
public override Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.StatusCode = (int)_statusCode;
return base.ExecuteResultAsync(context);
}
}
Then in your controller, return as follows:
// Set a json object to return. The status code defaults to 500
return new JsonErrorResult(new { message = "Sorry, an internal error occurred."});
// Or you can override the status code
return new JsonErrorResult(new { foo = "bar"}, HttpStatusCode.NotFound);
The thing that worked for me (and that I took from another stackoverflow response), is to set the flag:
Response.TrySkipIisCustomErrors = true;
You have to return JSON error object yourself after setting the StatusCode, like so ...
if (BadRequest)
{
Dictionary<string, object> error = new Dictionary<string, object>();
error.Add("ErrorCode", -1);
error.Add("ErrorMessage", "Something really bad happened");
return Json(error);
}
Another way is to have a JsonErrorModel and populate it
public class JsonErrorModel
{
public int ErrorCode { get; set;}
public string ErrorMessage { get; set; }
}
public ActionResult SomeMethod()
{
if (BadRequest)
{
var error = new JsonErrorModel
{
ErrorCode = -1,
ErrorMessage = "Something really bad happened"
};
return Json(error);
}
//Return valid response
}
Take a look at the answer here as well
You need to decide if you want "HTTP level error" (that what error codes are for) or "application level error" (that what your custom JSON response is for).
Most high level objects using HTTP will never look into response stream if error code set to something that is not 2xx (success range). In your case you are explicitly setting error code to failure (I think 403 or 500) and force XMLHttp object to ignore body of the response.
To fix - either handle error conditions on client side or not set error code and return JSON with error information (see Sbossb reply for details).
Several of the responses rely on an exception being thrown and having it handled in the OnException override. In my case, I wanted to return statuses such as bad request if the user, say, had passed in a bad ID. What works for me is to use the ControllerContext:
var jsonResult = new JsonResult { JsonRequestBehavior = JsonRequestBehavior.AllowGet, Data = "whoops" };
ControllerContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return jsonResult;
And if your needs aren't as complex as Sarath's you can get away with something even simpler:
[MyError]
public JsonResult Error(string objectToUpdate)
{
throw new Exception("ERROR!");
}
public class MyErrorAttribute : FilterAttribute, IExceptionFilter
{
public virtual void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.Exception != null)
{
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
filterContext.Result = new JsonResult() { Data = filterContext.Exception.Message };
}
}
}
If you are just using MVC the simplest way is to use HttpStatusCodeResult.
public ActionResult MyAjaxRequest(string args)
{
string error_message = string.Empty;
try
{
// successful
return Json(args);
}
catch (Exception e)
{
error_message = e.Message;
}
return new HttpStatusCodeResult(500, error_message);
}
When the error is returned to the client you can display it or action it how you like.
request.fail(function (jqXHR) {
if (jqXHR.status == 500) {
alert(jqXHR.statusText);
}
})
I was running Asp.Net Web Api 5.2.7 and it looks like the JsonResult class has changed to use generics and an asynchronous execute method. I ended up altering Richard Garside's solution:
public class JsonHttpStatusResult<T> : JsonResult<T>
{
private readonly HttpStatusCode _httpStatus;
public JsonHttpStatusResult(T content, JsonSerializerSettings serializer, Encoding encoding, ApiController controller, HttpStatusCode httpStatus)
: base(content, serializer, encoding, controller)
{
_httpStatus = httpStatus;
}
public override Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var returnTask = base.ExecuteAsync(cancellationToken);
returnTask.Result.StatusCode = HttpStatusCode.BadRequest;
return returnTask;
}
}
Following Richard's example, you could then use this class like this:
if(thereWereErrors)
{
var errorModel = new CustomErrorModel("There was an error");
return new JsonHttpStatusResult<CustomErrorModel>(errorModel, new JsonSerializerSettings(), new UTF8Encoding(), this, HttpStatusCode.InternalServerError);
}
Unfortunately, you can't use an anonymous type for the content, as you need to pass a concrete type (ex: CustomErrorType) to the JsonHttpStatusResult initializer. If you want to use anonymous types, or you just want to be really slick, you can build on this solution by subclassing ApiController to add an HttpStatusCode param to the Json methods :)
public abstract class MyApiController : ApiController
{
protected internal virtual JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus, JsonSerializerSettings serializerSettings, Encoding encoding)
{
return new JsonHttpStatusResult<T>(content, httpStatus, serializerSettings, encoding, this);
}
protected internal JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus, JsonSerializerSettings serializerSettings)
{
return Json(content, httpStatus, serializerSettings, new UTF8Encoding());
}
protected internal JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus)
{
return Json(content, httpStatus, new JsonSerializerSettings());
}
}
Then you can use it with an anonymous type like this:
if(thereWereErrors)
{
var errorModel = new { error = "There was an error" };
return Json(errorModel, HttpStatusCode.InternalServerError);
}
Here is the JsonResult override answer for ASP.NET v5+ . I have tested and it works just as well as in earlier versions.
public class JsonHttpStatusResult : JsonResult
{
private readonly HttpStatusCode _httpStatus;
public JsonHttpStatusResult(object data, HttpStatusCode httpStatus) : base(data)
{
_httpStatus = httpStatus;
}
public override Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.StatusCode = (int)_httpStatus;
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var services = context.HttpContext.RequestServices;
var executor = services.GetRequiredService<IActionResultExecutor<JsonResult>>();
return executor.ExecuteAsync(context, this);
}
}
I'd want to ask about way to return errors when it comes to WebAPI
I'm not sure whether I should use this approach, because this is not giving really good details except 500 internal server error code without UseDeveloperExceptionPage();
public async Task<Token> login(User creds)
{
var user = await Task.FromResult(_context.GetUser.....);
if (user == null)
{
throw new Exception("Invalid credentials");
}
return BuildToken(user); // Task<Token>
}
public class Token
{
public string token { get; set; }
public long Expires { get; set; }
}
So, should I create dynamic json and cast Error model / my JWT model to that dynamic json or something like that?
Basically I'd want to return either:
{
"Token": "aaaaa.aaaaaaaaaa.aaaaaaaaaaa",
"Expires": "111111111"
}
or
{
"message": "Invalid credentials"
}
More details:
Use method type:
IActionResult
and return errors as e.g
return new BadRequestObjectResult
(
new { message = "error message"}
);
I need to catch the errors from the "ModelState" to send a personalized message. The problem is that the filter is never executed if a property of the UserDTO has the attribute "Required". If you remove it, enter the filter, but the modelState is valid
[HttpPost]
[ModelState]
public IActionResult Post([FromBody] UserDTO currentUser)
{
/*if (!ModelState.IsValid)
{
return BadRequest();
}*/
return Ok();
}
public class ModelStateAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext currentContext)
{
if (!currentContext.ModelState.IsValid)
{
currentContext.Result = new ContentResult
{
Content = "Modelstate not valid",
StatusCode = 400
};
}
else
{
base.OnActionExecuting(currentContext);
}
}
}
public class UserDTO
{
[Required]
public string ID { get; set; }
public string Name { get; set; }
}
Your issue is caused by a new feature Automatic HTTP 400 responses:
Validation errors automatically trigger an HTTP 400 response.
So, if you want to custom the Validation errors, you need to disable this feature.
The default behavior is disabled when the SuppressModelStateInvalidFilter property is set to true. Add the following code in Startup.ConfigureServices after services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.Configure<ApiBehaviorOptions>(options => {
options.SuppressModelStateInvalidFilter = true; });
In ASP.NET Core 2.1 you also can change validation error response with InvalidModelStateResponseFactory parameter in ConfigureServices at Startup.cs:
services.Configure<ApiBehaviorOptions>(options =>
options.InvalidModelStateResponseFactory = actionContext =>
new BadRequestObjectResult(
new
{
error = string.Join(
Environment.NewLine,
actionContext.ModelState.Values.SelectMany(v => v.Errors.Select(x => x.ErrorMessage)).ToArray()
)
}
)
);
For example this configuration returns object with error field where combined all validation errors.
In that case ValidationAttribute is not needed, but you should decorate your controller with [ApiController] attribute.
I have used fiddler and swagger to attempt testing and get the same results.
Here is the model:
using System.ComponentModel.DataAnnotations;
namespace AdGeo.Configuration.WebApi.Model
{
public class ValueAdd
{
public string test { get; set; }
[Required(ErrorMessage = "Range is required")]
[Range(0,9,ErrorMessage ="Range 0 to 9")]
[Display(Name ="Test Required")]
public int? testrequired { get; set; }
}
}
Here Is the Controller:
using Microsoft.AspNet.Mvc;
using AdGeo.Configuration.WebApi.Model;
// For more information on enabling Web API for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
namespace AdGeo.Configuration.WebApi.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/values
[HttpPost]
public IActionResult Post([FromBody]ValueAdd valueAdd)
{
if(ModelState.IsValid)
{
return new CreatedAtActionResult("Get", "Values", new { id = 5 }, valueAdd);
}
return new BadRequestObjectResult(ModelState);
}
}
}
Sample Json:
{
"test": "string",
"testrequired": 100
}
It gets to the ModelState.IsValid which equals true, I am expecting it to equal false since 100 is outside the range.
Also, if I try to post this Json:
Sample Json:
{
"test": "string"
}
I would expect the ModelState.IsValid to be false, however it equals true.
I have been searching and searching to no avail, the closest I have seen is people trying to create an action filter, but those are mostly solution for prev MVC6 and not the same.
What am I missing, please note this is my first post on StackExchange, so apologies on any formatting issues. Please let me know what needs clarification and I will edit accordingly.
Thank You!
Same here, I think it's because I'm using AddMvcCore rather than AddMvc. To work it out, I've created the following action filter attribute. It will check if the model is null and required and it will run validation when not null.
/// <summary>
/// Validate inputs and update ModelState with errors
/// </summary>
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var parameters = context.ActionDescriptor.Parameters.Cast<ControllerParameterDescriptor>().ToDictionary(p => p.Name);
foreach (var kvp in context.ActionArguments)
{
if (kvp.Value == null)
{
if (!parameters[kvp.Key].ParameterInfo.IsOptional)
{
context.ModelState.AddModelError($"{kvp.Key}", "Parameter is required");
}
continue;
}
var results = new List<ValidationResult>();
var vc = new ValidationContext(kvp.Value);
if (!Validator.TryValidateObject(kvp.Value, vc, results))
{
var errs = from vr in results
from member in vr.MemberNames
select new { Member = member, vr.ErrorMessage };
foreach (var err in errs)
{
context.ModelState.AddModelError($"{kvp.Key}.{err.Member}", err.ErrorMessage);
}
}
}
}
}
I have the following custom authorization class inside my asp.net mvc web application, which i call before my action methods:-
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CheckUserPermissionsAttribute : AuthorizeAttribute
{
public string Model { get; set; }
public string Action { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!httpContext.Request.IsAuthenticated)
return false;
//code goes here................
if (!repository.can(ADusername, Model, value)) // implement this method based on your tables and logic
{ return false; }
return true;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
var viewResult = new JsonResult();
viewResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
viewResult.Data = (new { IsSuccess = "Unauthorized", description = "Sorry, you do not have the required permission to perform this action." });
filterContext.Result = viewResult;
}
else
{
var viewResult = new ViewResult();
viewResult.ViewName = "~/Views/Errors/_Unauthorized.cshtml";
filterContext.Result = viewResult;
}
// base.HandleUnauthorizedRequest(filterContext);
}
}
and i call this custom authorization before my action method as follow:-
[CheckUserPermissions(Action = "Read", Model = "Accounts")]
public ActionResult Index(){
Currently as seen in the above code when the request is not authorized , I will return a JSON or a partial view depending on request type (if it is Ajax request or not).
And inside my code I always take care of handling the json returned from the custom authorization class inside the onsuccess script as follow:-
function addrecords(data) {
if (data.IsSuccess == "Unauthorized") {
jAlert(data.description, 'Unauthorized Access');
}
else if (data.IsSuccess) {
jAlert(data.description, 'Creation Confirmation');
}
Currently my approach is working well, but I start thinking if I should continue with the fact that I am NOT retuning 401 http response for unauthorized requests ? and instead of that I am returning an http 200 , either as json object with status = “unauthrized” or redirect to a partial view ?
Can anyone advice ?
Thanks.
i used to do like this:
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 403;
filterContext.Result = new JsonResult { Data = "LogOut" };
}
else
{
filterContext.Result = new RedirectResult("~/Home/Index");
}
and in jquery i check in generic ajaxError:
$(document).ajaxError(function(xhr, statusText, err){
if(xhr.status == 403) {
alert("Unathorized Request");
}
});
or:
$.ajaxSetup({
error: function (x, e) {
if (x.status == 403) {
alert("Unauthorized Access");
}
});
});
In your approach you have to check in every Ajax call success the response what is coming, but in this approach in unauthorized case returning 403 code will make Ajax call fail and error callback executes and we i use to write a generic error handler for Ajax and check if status code is that what u i return then show message that it is unauthorized request.
you can see details : Asp.net mvc Check User is Logged In and authorized Before Access to Page