I am creating an ASP.NET MVC app and I would like it so when an error occurs I call another action which loads a different view.
public ActionResult Invoices()
{
invoiceClass invoice = getInvoice();
//do stuff with invoice
}
public invoiceClass getInvoice()
{
invoiceClass invoice = new invoiceClass();
try
{
// Do stuff
}
catch(exception e)
{
return RedirectToAction("Index");
}
return invoice;
}
I have a method that is very similar to this, when I step through the code, the exception is caught and it hits the redirect call then goes to the return and continues without redirecting. Am I missing something obvious?
If this is an HTTP entry point, you should be returning an ActionResult.
public ActionResult stuff()
{
try
{
// Do stuff
}
catch (Exception e)
{
//Return a RedirectResult
return RedirectToAction("Index");
}
//Return a JsonResult
//Example: return the JSON data { "number": 1 }
return Json(new { number = 1 });
}
Edit
Here is how I would address your question given the edits you just made.
public ActionResult Invoices()
{
try
{
invoiceClass invoice = getInvoice();
//do stuff with invoice
return Json(...);
}
catch
{
//Catch the exception at the top level
return RedirectToAction("Index");
}
}
public invoiceClass getInvoice()
{
invoiceClass invoice = new invoiceClass();
// Do stuff; possibly throw exception if something goes wrong
return invoice;
}
you are missing a return
return RedirectToAction("Index");
And don't forget to change your return type to ActionResult
So Ive managed to find a solution to this problem however I'm certain there will be a better more efficient way.
public ActionResult Invoices()
{
try{
invoiceClass invoice = getInvoice();
//do stuff with invoice
}catch (exception e){
return RedirectToAction("Index");
}
}
public invoiceClass getInvoice()
{
invoiceClass invoice = new invoiceClass();
try
{
// Do stuff
}
catch(exception e)
{
index(); //<--the action result
}
return invoice;
}
calling index() runs through the method then throws an exception in the Invoices() method which then redirects.
Related
My methods only return responses with no content.
Controller
[HttpGet("Floors/{floorId}", Name = "FloorById")]
public IActionResult GetFloor(int floorId)
{
try
{
Floor floor = _repository.Floor.GetFloor(floorId);
if (floor == null)
return NotFound();
return Ok(floor);
}
catch (Exception e)
{
return StatusCode(500, "text");
}
}
Repository
public Floor GetFloor(int floorId)
{
return _context.Floors.FirstOrDefault(f => f.Id == floorId);
}
Ideally, this code should return an Ok response with the object as well.
Instead, I only get an Ok response when using swagger. Not even the NotFound.
Swagger is unable to determine what type the action returns based on the IActionResult.
Use the ProducesResponseType attribute:
[ProducesResponseType(typeof(Floor), 200)] // <-- THIS
[HttpGet("Floors/{floorId}", Name = "FloorById")]
public IActionResult GetFloor(int floorId) {
try {
Floor floor = _repository.Floor.GetFloor(floorId);
if (floor == null)
return NotFound();
return Ok(floor);
} catch (Exception e) {
return StatusCode(500, "text");
}
}
I currently have the below code which I thought would work however I am receiving a "HttpControllerContext.Configuration must not be null" error when I create the Ok result. The goal is to be able to call any function in a controller in one line to keep my controllers clean. Such as "return ApiUtilities.TryCatch(() => _someService.Get(id));"
I only have access to 'Ok()', "NotFound()" and "InternalServerError()" because the ApiUtilities Class inherits from ApiController
public IHttpActionResult TryCatch<T>(Func<T> operation)
{
try
{
if (ModelState.IsValid)
{
var result = operation();
return Ok(result);
}
}
else
{
return BadRequest();
}
}
catch (Exception error)
{
return InternalServerError();
}
Edit:
My controller looks like this
public class PageController : ApiController
{
private ISomeService _someService;
private ApiUtilities _apiUtilities;
public PageController(ISomeService someService)
{
_someService= someService;
_apiUtilities = new ApiUtilities();
}
[Route("api/page")]
public IHttpActionResult Get([FromBody]string url)
{
return _apiUtilities.TryCatch(() => _someService.Get(url));
}
}
Below is the update I've made based on a Friend's suggestion. I've removed the inheritance on the ApiController. I've also returned the same models the Ok, BadRequest and NotFound functions generate using the context of the current api.
public static class ApiUtilities
{
public static IHttpActionResult TryCatch(Action action, ApiController apiController)
{
try
{
if (apiController.ModelState.IsValid)
{
action();
return new OkResult(apiController);
}
else
{
return new BadRequestResult(apiController);
}
}
catch (Exception error)
{
return new NotFoundResult(apiController);
}
}
public static IHttpActionResult TryCatch<T>(Func<T> operation, ApiController apiController)
{
try
{
if (apiController.ModelState.IsValid)
{
var result = operation();
return new OkNegotiatedContentResult<T>(result, apiController);
}
else
{
return new BadRequestResult(apiController);
}
}
catch (Exception error)
{
return new NotFoundResult(apiController);
}
}
}
I'm not able to access to my delete method of my api rest.
If i write the method like this it work:
[Route("api/Document/{documentId:int}")]
[HttpDelete]
public IHttpActionResult Delete([FromUri]int documentId,[FromBody] int [] documentsId)
{
try
{
documentCtrl = documentCtrl ?? new DocumentCtrl();
return Ok(documentCtrl.Delete(documentsId));
}
catch (DocumentNotFoundException)
{
return NotFound();
}
catch (Exception)
{
return InternalServerError();
}
}
It works, but if i put:
[Route("api/Document/MassiveDelete")]
[HttpDelete]
public IHttpActionResult MassiveDelete([FromBody] int[] ids)
{
try
{
documentCtrl = documentCtrl ?? new DocumentCtrl();
return Ok(documentCtrl.MassiveDelete(ids));
}
catch (DocumentNotFoundException)
{
return NotFound();
}
catch (Exception)
{
return InternalServerError();
}
}
I don't have acces, any ideas what could it be?
This is my request code:
DeleteDocument(id: number): Observable<boolean> {
return this._httpService.delete(AppModule.service + 'Document/' + id, AppModule.options)
.map((response: Response) => <boolean>response.json())
.catch(this.handleError);
}//This work if i want to delete one
DeleteDocuments2(ids:Array<number>):Observable<boolean>{
AppModule.options.body=ids;
return this._httpService.delete(AppModule.service + 'Document/MassiveDelete', AppModule.options)
.map((response: Response) => <boolean>response.json())
.catch(this.handleError);
}
You cannot send two parameters in your Api, you need to createa custom class like follow and send as follows,
MyCustomRequest {
public int[] documentIds;
public int documentId;
}
and then,
public IHttpActionResult MassiveDelete([FromBody] MyCustomRequest request)
you can access it as,
request.documentIds;
request.documentId;
I have a standard Edit action in Asp.Net MVC 5 and I want to avoid throwing the unhandled exception when a get request is made without the id like ~/food/edit, so I did this.
public ActionResult Edit(int id = 0)
{
if (id == 0)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
string result = _foodAppService.GetById(id);
FoodVm food = string.IsNullOrEmpty(result)
? null
: JsonConvert.DeserializeObject<FoodVm>(result);
if (food == null)
{
return RedirectToAction("Index");
}
return View(food);
}
My question is: Is it a good practice to handled it in this way or there are more suitable strategies ?
I'm new to this asking question thing, if a should I ask in another way, just let me know, thank you for your time.
In case zero could be valid its better to do
public ActionResult Edit(int? id)
{
if (!id.HasValue)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
Or there can be more overall way to handle exceptions in MVC. You can use override of method OnException this gives you abbility to debug all exception in controllers in one method and handle them.
Just add base class to all your controllers like this:
public class BaseController : Controller
{
protected override void OnException(ExceptionContext filterContext)
{
string redirectUrl;
var exception = filterContext.Exception;
if (exception is EntityException)
{
redirectUrl = "/Content/error.html";
}
else
{
redirectUrl = "/Info/Index";
}
//do whatever you wont
Response.Redirect(redirectUrl);
}
Also use input parameters verification as Paul Swetz saied. This method is more genera, allows to intercept all exceptions, and don`t show errors to users.
Following #Fran 's advice. I built a action filter attribute called MissingParam
public class MissingParamAttribute : ActionFilterAttribute
{
public string ParamName { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionParameters.ContainsKey(ParamName))
{
if (filterContext.ActionParameters[ParamName] == null)
{
filterContext.ActionParameters[ParamName] = 0;
}
}
base.OnActionExecuting(filterContext);
}
}
in the action I did this:
[MissingParam(ParamName="id")]
public ActionResult Edit(int id)
That way I don't need to mess with method parameter, any validating happens before. This implementation follows the Open/Close principle. I extended its functionality, but I didn't changed like the code in the question.
First and foremost its a good practice to use Try-Catch.
public ActionResult Edit(int id)
{
try
{
if (id != 0 || id!=null)
{
string result = _foodAppService.GetById(id);
FoodVm food = string.IsNullOrEmpty(result) ? null:JsonConvert.DeserializeObject<FoodVm>(result);
if (food == null)
{
return RedirectToAction("Index");
}
else
{
return View(food);
}
}
else
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
catch (exception ex)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
I am disgusted not have found a solution to this problem.
I started creating a new api using Web API 2 and just cannot get the POST and PUT to work. The Get all and Get single item works perfectly fine.
There are no related articles anywhere, and those that i've found relates only to Gets and Web API, but not Web API 2.
Any assistance would do please.
// POST: api/checkOuts
[HttpPost]
[ResponseType(typeof(checkOut))]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IHttpActionResult> PostcheckOut(checkOut co)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.checkOuts.Add(checkOut);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (checkOutExists(checkOut.id))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtRoute("DefaultApi", new { id = checkOut.id }, checkOut);
}
So basically, I'm just attempting to get a debug into the method.
Was especially disappointed in this link as it covered almost everything, but ai. http://www.asp.net/web-api/overview/web-api-routing-and-actions/create-a-rest-api-with-attribute-routing
Regards
This is a working code
// POST api/values
[HttpPost]
[ResponseType(typeof(CheckOut))]
public async Task<IHttpActionResult> Post([FromBody] CheckOut checkOut)
{
if (checkOut == null)
{
return BadRequest("Invalid passed data");
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.checkOuts.Add(checkOut);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (checkOutExists(checkOut.id))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtRoute("DefaultApi", new { id = checkOut.Id }, checkOut);
}
I've declared CheckOut class to be like this :
public class CheckOut
{
public int Id { get; set; }
public string Property2 { get; set; }
}
The Key things here are :
1- You need to add [FromBody] to your Api method.
2- I've tested it using Fiddler,
i- by choosing POST action.
ii- content-type: application/json.
iii- passing {"Id":1,"Property2":"Anything"} in the message body.
Hope that helps.