How to handle empty variables in a try/catch block? - c#

So I have button that sends a POST request along with two variables, an outbox ID called oid, and an email. The controller takes in the variables and performs a method call, the end goal would be to send a JSON response back depending on whether it fails or succeeds.
I have the following written for the controller:
public ActionResult ResendEmail(int oid, string email = null)
{
try
{
Outbox.ResendEmail(oid, email);
return Json(new { success = true, message = "Email has been queued to be resent", status = (int)HttpStatusCode.OK });
}
catch (Exception)
{
return Json(new { success = false, message = "An error occurred when attempting to resend the email", status = (int)HttpStatusCode.InternalServerError });
}
}
Problem here, is that 1. If the oid and/or email is empty, the method call for Outbox.ResendEmail(oid, email) will still be called and a success json response will be returned. I'm also unsure if this is a proper way to use try/catch.
Feels like I should re-write how i'm handling this. Maybe an if statement to check for an existing oid and email followed by a try/catch?

The try/catch is useful for handling exceptions. If Outbox.ResendEmail() throws an exception, then the try/catch will do its thing.
However, if Outbox.ResendEmail() will not throw an exception when given an incorrect oid or an incorrect, empty, or null email, then you need something else, such as an if-statement.
In addition, it probably is not appropriate to return a 500 error due to invalid parameters. It's not really a server error if the user passes a null email address.
I would recommend something like the following:
public ActionResult ResendEmail(int oid, string email = null)
{
// If you have a way to validate the oid, then use that instead of just checking for 0:
if (oid == 0)
{
return // one of the 400 level responses
}
// Do you have a way to validate the email address?
// If so, probably do that here, depending on how Outbox.ResendEmail() behaves
if (string.IsNullOrWhiteSpace(email))
{
return // one of the 400 level responses
}
try
{
Outbox.ResendEmail(oid, email);
return Json(new { success = true, message = "Email has been queued to be resent", status = (int)HttpStatusCode.OK });
}
catch (Exception)
{
return Json(new { success = false, message = "An error occurred when attempting to resend the email", status = (int)HttpStatusCode.InternalServerError });
}
}

Try Catch always work with exception. As per the below statement in your question, it seems that Outbox.ResendEmail does not throw exception
If the oid and/or email is empty, the method call for
Outbox.ResendEmail(oid, email) will still be called and a success json
response will be returned. I'm also unsure if this is a proper way to
use try/catch
instead of exception, it may be returning response with status false or error or not 200. That why, it is not going to catch and returning json response with success = false.
you can do like this
public ActionResult ResendEmail(int oid, string email = null)
{
if ( oid <=0 || string.IsNullOrWhiteSpace(email) || validation of email if any)
{
return response; // with statuscode among 400 and proper message in response
//that where is the problem in request.
}
try
{
var response= Outbox.ResendEmail(oid, email);
if (response.StatusCode == 200) // condition for check that response is false or success
{
return Json(new { success = true, message = "Email has been queued to be resent", status = (int)HttpStatusCode.OK });
}
return Json(new { success = false, message = "An error occurred when attempting to resend the email", status = (int)HttpStatusCode.InternalServerError });
}
catch (Exception)
{
return Json(new { success = false, message = "An error occurred when attempting to resend the email", status = (int)HttpStatusCode.InternalServerError });
}
}

Related

how to set response when i am using IActionResult in return type with error message?

when try block send error to catch block it throw and response I got at frontend was {status:'error',error:500}.
Please explain me what is going when I Re-throw in catch block.
Is .NET is set response when I re-throw?
I want to set status message. how can I do?
I want to set status message. how can I do?
Well, you can bind custom status inside BadRequest() or Any other response class result. For instance:
public async Task<IActionResult> DownloadAttachedFile()
{
try
{
//Your code
}
catch (Exception ex)
{
return BadRequest(new { StatusMessage = "Your Messaage", StatusCode = 400 });
}
}
Note: Here StatusMessage, you can even concat your ex message as well. Can introduce new custom error class. You can also skip StatusCode = 400 because BadRequest itself will return that code but if you want to fetch easily from your frontend you can set as well.
Output:
In your frontend you would get the response as following:
{
"statusMessage": "Your Messaage",
"statusCode": 400
}
In addition, you can always customize your response class based on your requirement.

API Record not found

I build this API that takes in Id if that Id is found It will return the data. But if that Id is not found I return 409. When I test it in postman I see the status as 409 is that correct? is that all I need or should it also return some text in the body?
[HttpGet]
public IHttpActionResult Read(string type, string id)
{
if (id== null)
{
var msg = new HttpResponseMessage(HttpStatusCode.NotFound) { ReasonPhrase = "Unable to Find Id" };
msg.Content= new StringContent($"No entity with {id} was found");
return ResponseMessage(msg);
}
}
You do see a "not found" text:
You don't see anything in the body, because your API doesn't send a body, just a HTTP header
Re your comment, and linking in GPW's advice, return something custom - let errors be errors, and have this foreseeable condition as an "OK but no" response, perhaps:
[HttpGet]
public ActionResult Read(string type, string id)
{
if (id == null)
return Json(new { status= "fail", message= "id parameter is required" });
else if (type == null)
return Json(new { status= "fail", message= "type parameter is required" });
var ent = dbcontext.Entity.FirstOrDefault(e => e.Type == type && e.Id == id);
if(ent == null)
return Json(new { status="fail", message= "No entity with that type/id was found" });
else
return Json(new { status="ok", entityName= ent.Name });
}
In one of our apps we do use HTTP errors to alter the behavior of the client - there's a promise chain at the client react app and we use returning an error to halt processing of the chain, parse the error out, then go back to parsing knowing the JSON is a slightly different shape. I'm not convinced it's a great way to do it as it's made the client more complex than it could have been but if you want to do that then sure, look at ways of returning meaningful http errors. Personally (and GPW alludes to it) even debugging, many times I've missed the return code in postman, got a blank response, been misled that something else is wrong than what is actually happening/ got a 404 and thought the back end couldn't find the entity when actually I'd got the URL wrong and was getting a 404 for a different reason etc

Handling success and failure API JSON responses when json format is different for each

How do I handle a failure when my method only returns a single return type?
public async Task<User> GetUser(int userId)
{
User user = null;
HttpResponseMessage response = await client.GetAsync("/User/...");
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
user = JsonConvert.DeserializeObject<User>(result);
}
else
{
// ????
}
return user;
}
Success:
{
"a": 1,
"b": 2,
"c": 3
}
Failure
{
"error": "something has gone wrong..."
}
In the failure case currently I am just returning null, but I want to return the error message.
The API I am using doesn't return the same JSON for both failure and success, so I'm not sure how to best handle the failure case.
#Crowcoder's comment is the proper route. However, in case you ever had a successful response with a dynamic object structure this information could be useful. You can test a property on a returned object to see if it is undefined. In your case, and don't do this because you should use the status codes, you could do something like this:
if(result.error){
//this is an error state
} else {
//this was successful
}

Edit BadRequest

I have an Response object that I want to return to the user if there is an exception in the controller. However when i try to send back BadRequest i cant seem to send back my Response object. So my question is how do i edit BadRequst to contain my Response object and/or how do i send back my Response object with an error status code ?
Controller
public async Task<IActionResult> Login([FromBody] LoginViewModel model) {
Response response = new Response();
try {
if (ModelState.IsValid) {
var result = await signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded) {
logger.LogInformation(1, "User logged in.");
return Json("User logged in.");
}
if (result.RequiresTwoFactor) {
response.id = 1 ;
throw new LoginException("Login requiest two factor", new InvalidOperationException());
}
if (result.IsLockedOut) {
response.id = 2 ;
logger.LogWarning(2, "User account locked out.");
throw new LoginException("User account locked out", new InvalidOperationException());
}
else {
response.id = 3 ;
throw new LoginException("Invalid login attempt", new InvalidOperationException());
}
}
response.id = 4 ;
var modelErrors = ModelState.Values.ToList();
throw new LoginException("Model State Error", ModelState, new InvalidOperationException());
} catch (LoginException ex){
return BadRequest(response); // response with status: 400 Bad Request for URL, no response object is in here.
}
}
Front-End
login(email:any, password:any, remember:any){
//let body:User = {email:email, password:password};
let headers = new Headers({ 'Content-Type': 'application/json' });
let body = {Email:email, Password:password,RememberMe:false };
console.log(body);
this.http.post('/api/Account/Login', body ,{headers:headers})
.map(response => response.json())
.subscribe(
response => {
console.log("Success !!!:\n"+response);
this.router.navigate(['/home']);
},
error => {
console.log("Error !!!:\n"+error);
}
);
}
It's not really clear what you're asking?
You only want to return status code 400? Use the parameterless version of return BadRequest()
Want a different status code? Use different Method. BadRequest is named by the HTTP Statuscode 400 (BadRequest). If you want to set the status code yourself, use return Status(HttpStatusCode.Unauthorized); or whatever.
Want to return a error message as json? Use error classes or annonymous class to do so:
return BadRequest(new { ErrorMessage = "Account is locked." });
But whatever you want to do, throwing exceptions in Controller is wrong. An controller action should NEVER throw an exception. Also as pointed in the comments, exceptions are to be used for exceptional cases. A wrong password, locked user or wrong authentication method (i.e. no two factor auth when its required) are expected errors and shouldn't be handled via excpetions.
Exceptions (when thrown only) are inherently expensive operations in a computer program. For that reason Microsoft implemented an IdentityResult class to return a list of expected errors, rather than throwing exceptions during the call of signInManager.PasswordSignInAsync
Instead of returning IActionResult why don't you return a HttpResponseMessage and then you can do something like:
return Request.CreateResponse(System.Net.HttpStatusCode.BadRequest)

How to return error message from Catch block. Right now empty is returned

My sample code of ApiKey validation is given below (I am using MVC4 web api RC):
public class ApiKeyFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext context)
{
//read api key from query string
string querystring = context.Request.RequestUri.Query;
string apikey = HttpUtility.ParseQueryString(querystring).Get("apikey");
//if no api key supplied, send out validation message
if (string.IsNullOrWhiteSpace(apikey))
{
var response = context.Request.CreateResponse(HttpStatusCode.Unauthorized, new Error { Message = "You can't use the API without the key." });
throw new HttpResponseException(response);
}
else
{
try
{
GetUser(decodedString); //error occurred here
}
catch (Exception)
{
var response = context.Request.CreateResponse(HttpStatusCode.Unauthorized, new Error { Message = "User with api key is not valid" });
throw new HttpResponseException(response);
}
}
}
}
Here problem is with Catch block statement. I Just wanted to send custom error message to user. But nothing is sent. It displays a blank screen
However, the statement below is working well and sends out the validation error message correctly:
if (string.IsNullOrWhiteSpace(apikey))
{
var response = context.Request.CreateResponse(HttpStatusCode.Unauthorized, new Error { Message = "You can't use the API without the key." });
throw new HttpResponseException(response);
}
Is there anything that i am doing wrong.
I was having the same issue in the exact same scenario. However, in this scenario, you need to return some content in your response to be shown and not really throw the exception. So based on this, I would change your code to the following:
catch (Exception)
{
var response = context.Request.CreateResponse(httpStatusCode.Unauthorized);
response.Content = new StringContent("User with api key is not valid");
context.Response = response;
}
So with this change you are now returning your response, with content that will displayed in place of the blank screen.

Categories