Returning BadRequest from WebApi Custom method - c#

Using .net core web api here.
I have a endpoint in my api as:
[HttpPost("data")]
public async Task<IActionResult> PostData(List<string> udata)
{
JArray sets = new JArray();
try
{
sets = Helper.GetData(udata);
}
catch (Exception e)
{
return StatusCode(500, e.Message);
}
}
In the above I am calling an custom method in my helper "GetData" which does all the processing and calculations. To make the Api controller clean I moved all the processing to this helper method.
Below is my helper class:
public static class Helper
{
public static BadRequestObjectResult GetMessage(string message)
{
return new BadRequestObjectResult(message);
}
public static JArray GetData(List<string> udata)
{
if(udata == null)
return GetMessage("Data is null");
//do some processing and calclulations here
//return BadRequest if some issue
}
}
If there is some processing issue or some of the data is not as intended or some other issue I want to throw BadRequest. For this I made a custom method to do so "BadRequestObjectResult".
Now my issue is, if there is an issue in GetData it doesnt throws back to my api or exits from my loop. It just continues to next statement.
I know there is some issue by the way I am returning this but not able to find the issue.
Can anyone point out the correct way to handle this?

My suggestion is to throw an exception from your Helper class and and handle it from your PostData method. For example...
You could throw an ArgumentException and explicitly catch that from your API method.
public static class Helper
{
public static JArray GetData(List<string> udata)
{
if(udata == null)
throw new ArgumentException("Data is null");
//do some processing and calclulations here
//throw ArgumentException if there is an issue
}
}
[HttpPost("data")]
public async Task<IActionResult> PostData(List<string> udata)
{
JArray sets = new JArray();
try
{
sets = Helper.GetData(udata);
return Ok(sets);
}
catch (ArgumentException e)
{
return BadRequest(e.Message);
}
}
This way you can only worry about return codes from your controller while your Helper methods are only concerned with the input and aren't returning something specialized for a controller. It's a bit more flexible that way if you ever want to use your Helper class elsewhere.
This will also meet your requirement of stopping processing upon hitting a bad result, so as soon as a bad result is encountered the result set is thrown away and a BadRequest response is issued.

Related

what to return when return type is Task<IactionResult> in .netcore

Im really confused in .Netcore what normally should return to a queue(messagebroker),I have a class
public Task<IActionResult> GetMerchantPlatform(int merchantID) {
try
{
var mrchantInfo = dbContext.MerchantPlatforms.Where(s => s.Id == merchantID);
return Task.FromResult(mrchantInfo);
}
catch (Exception ex)
{
throw ex;
}
}
it gives me an error :Severity Code Description Project File Line Suppression State
Error CS0029 Cannot implicitly convert type 'System.Threading.Tasks.Task<System.Linq.IQueryable<Models.MerchantPlatform>>' to 'System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.IActionResult>'
when should i return Iactionresult?and when should i return the class?and why im getting the above error?
By using the ControllerBase class you have to return an IActionResult or a Task<IActionResult>. There are plenty of pre-defined result objects available, which you can choose from, cause the result consist of multiple information that has to be defined by you. First is the HTTP status code that should be returned and depending on that you potentially have to return a body and/or some header entries.
If your method is within a ControllerBase class, there are already some helper methods within this class like Ok(), BadRequest(), Forbid(), NoContent(), NotFound(), etc. All with several overloads, depending on their usual use case.
So in your case, you probably have to write something like this:
public async Task<IActionResult> GetMerchantPlatform(int merchantID)
{
try
{
var mrchantInfo = await dbContext.MerchantPlatforms
.Where(s => s.Id == merchantID)
.ToListAsync();
return Ok(mrchantInfo);
}
catch (Exception ex)
{
return BadRequest(ex);
}
}
Be aware, that the try/catch is probably not needed in every function. For this purpose you can register your self-written middleware, that can put such a block around each action and act accordingly to your needs. But that's some stuff for another question or a search for
asp core middleware
And if you really want to only return the content in your method, you should consider to use the ApiControllerAttribute.

Returning meaningful error messages from a .NET Core API

Scenario
I have a .NET Core 2.2 web API with an exception handling middleware. Whenever an exception occurs in the application (inside the MVC layer) it gets caught by the exception middleware and returned as an internal server error back to the frontend and logged to kibana.
The problem
This is all fine and well when things go wrong, but sometimes I want to notify the calling application of specifically what went wrong. I.e., "Could not find record in database!" or "Failed to convert this to that!"
My Solution
I've used application Exceptions (not great - I know) to piggy back off the error middleware to return this to the frontend. This has been working fine, but has created a lot of noise around the code by having to throw a whole bunch of exceptions. I'm not satisfied with this approach and convinced that there must be a better solution.
My application architecture: I'm following a traditional n-tier application layout being services (business logic) and repositories (DAL) all speaking to each other. I would preferably like to elegantly bubble up any issues back to the user in any of these layers.
I've been thinking about this for a while now and am not sure what the best way to go about it is. Any advice would be appreciated.
I use a kind of the operation result pattern (non-official pattern).
The principle is to return a new Type containing:
Whether the operation was a success.
The result of the operation if was successful.
Details about the Exception that caused the failure.
Consider the following class:
public class OperationResult
{
protected OperationResult()
{
this.Success = true;
}
protected OperationResult(string message)
{
this.Success = false;
this.FailureMessage = message;
}
protected OperationResult(Exception ex)
{
this.Success = false;
this.Exception = ex;
}
public bool Success { get; protected set; }
public string FailureMessage { get; protected set; }
public Exception Exception { get; protected set; }
public static OperationResult SuccessResult()
{
return new OperationResult();
}
public static OperationResult FailureResult(string message)
{
return new OperationResult(message);
}
public static OperationResult ExceptionResult(Exception ex)
{
return new OperationResult(ex);
}
public bool IsException()
{
return this.Exception != null;
}
}
Then you could easily adapt OperationResult or create a class that inherits from OperationResult, but uses a generic type parameter.
Some examples:
The Operation Result Pattern — A Simple Guide
Error Handling in SOLID C# .NET – The Operation Result Approach
As per the Microsoft's standards, it is ideal to use ProblemDetails object in case of 4xx/5xx exceptions -
Following is the customised RequestDelegate method which you can use in ApiExceptionHandler to handle exceptions.
public async Task RequestDelegate(HttpContext context)
{
var exception = context.Features.Get<IExceptionHandlerFeature>().Error;
var problemDetails = new ProblemDetails
{
Title = "An unexpected error occurred!",
Status = GetStatusCode(exception),
Detail = _env.IsDevelopment() ? exception.Message : "An unexpected error occurred!",
Instance = $"{Environment.MachineName}:{context.TraceIdentifier}:{Guid.NewGuid()}"
};
_logger.LogError($"Exception thrown. StatusCode: {problemDetails.Status}. Instance: {problemDetails.Instance}", exception);
context.Response.StatusCode = problemDetails.Status.Value;
context.Response.WriteJson(problemDetails, "application/problem + json");
await Task.CompletedTask;
}

Web API 2 services - how to return an Exception message in the Status

Is it possible, in Web API 2 to directly return the Exception message in the response's Status ?
For example, if I was writing a WCF Service (rather than Webi API), I could follow this tutorial to directly return an Exception message as part of the response status:
Here, the web service doesn't return any data in the Response, and the error message gets returned directly in the Status Description.
This is exactly what I'd like my Web API services to do when an exception occurs, but I can't work out how to do it.
Most suggestions suggest using code like below, but then the error message will then always get returned in a separate response string, rather than being part of the Status.
For example, if I were to use this code:
public IHttpActionResult GetAllProducts()
{
try
{
// Let's get our service to throw an Exception
throw new Exception("Something went wrong");
return Ok(products);
}
catch (Exception ex)
{
return new System.Web.Http.Results.ResponseMessageResult(
Request.CreateErrorResponse((HttpStatusCode)500,
new HttpError("Something went wrong")));
}
}
... then it returns a generic 500 message, and the exception is returned in a JSON string.
Does anyone know how to modify a Web API function (which returns an IHttpActionResult object) to do this ?
You could register a custom global filter that will handle all Exceptions. Something like:
public class CatchAllExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
context.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(context.Exception.Message)
};
}
}
You will need to register it in WebApiConfig.cs, with:
config.Filters.Add(new CatchAllExceptionFilterAttribute());
This filter will be hit everytime there is an unhandled exception in the system and set the http response to the exception message. You could also check the different types of exception and alter your response accordingly, for example:
public override void OnException(HttpActionExecutedContext context)
{
if(context.Exception is NotImplementedException)
{
context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented)
{
Content = new StringContent("Method not implemented.")
};
}
else
{
context.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(context.Exception.Message)
};
}
}
https://www.asp.net/web-api/overview/error-handling/web-api-global-error-handling
Please refer above link, it will help you!

Exception handling strategy - Catching exceptions from repository and passing into WebApi Controller

Forgive me for my ignorance but at the moment I'm struggling to figure out the best approach to catch an exception and display a message to the client based on an exception type.
My architecture:
Repository
Page IPageRepository.FindDefault()
{
try
{
return MapPageFromCategory.MapFromEntity(_context.tbl_Category.
FirstOrDefault(p =>
p.IsLandingPage == true
)
);
}
catch (Exception ex)
{
throw new ApplicationException("Error getting values from database :", ex);
}
}
With the above as you can see it just fetches data from the DB, but in some scenarios I could get a object not set to an instance of an object exception due to physically no data in the table or a Null exception depending on the data passed in.
Now in both scenario I would like to generate a 404 exception which passes to my controller.
Controller
public class PageController : ApiController
{
private IPageRepository _pageRepository;
public PageController(IPageRepository pageRepository)
{
this._pageRepository = pageRepository;
}
// GET
[HttpGet]
[Route("")]
public Page Get()
{
return this._pageRepository.FindDefault();
}
}
My Controller hit's the method and if any of those exceptions are hit, what would be the best approach to intercept those exceptions and pass to the end client (Calling app) ?
Again apologies for the question, really trying to create some sort of systematic approach to exception handling.
Thanks for your time!
You can either have a separate try/catch in the Controller class and handle the exception thrown from repository there or do something different.
You can create a class similiar to this:
public class Result
{
public bool Succeded { get; private set; }
protected Result(bool succeeded)
{
Succeded = succeeded;
}
}
public class Result<TData> : Result
{
public TData Data { get; private set; }
protected Result(bool succeeded, TData data) : base(succeeded)
{
Data = data;
}
}
Then, you can return this from your repository and check in the controller if the call has succeeded. If not, throw a desired exception, which will set StatusCode accordingly.

Capture exception during request deserialization in WebAPI C#

I'm using WebAPI v2.2 and I am getting WebAPI to deserialise JSON onto an object using [FromBody] attribute. The target class of the deserialisation has a [OnDeserialized] attribute on an internal method, like this:
[OnDeserialized]
internal void OnDeserialisedMethod(StreamingContext context) {
// my method code
}
I know for a fact there is a problem with the code inside this method, I've stepped through it and found it. The problem for me is that I get no exception at all. What happens is this method gets jumped out of and the exception seems to be ignored. My controller action gets called and my target object is not properly populated because this serialisation method has not been correctly executed.
My question is; how can I capture an exception that occurs during deserialisation in WebAPI?
I've written up a filter (as suggested in various comments) that checks the ModelState and throws an exception if serialization errors did occur. Beware though, that this may not contain only serialization exceptions - that could be adjusted by specifing the concrete exception type in the Select statement.
public class ValidModelsOnlyFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ModelState.IsValid)
{
base.OnActionExecuting(actionContext);
}
else
{
var exceptions = new List<Exception>();
foreach (var state in actionContext.ModelState)
{
if (state.Value.Errors.Count != 0)
{
exceptions.AddRange(state.Value.Errors.Select(error => error.Exception));
}
}
if (exceptions.Count > 0)
throw new AggregateException(exceptions);
}
}
}
I suggest binding this filter on a global scope. I really can't fathom why it should be ok to ignore deserialization exceptions.
I had exactly the same problem and bookmarked your question in hope that someone would provide a solution. I thought using ModelState implied rewriting some validations in the JSON model, but it just works, in fact it's simple and very well done. I didn't have to modify the model, just the controllers.
My code from one of my controllers, StdResponse being the class used to provide the response with details if needed (in this case, for instance) :
[HttpPost]
public StdResponse Test([FromBody]StdRequest request)
{
if (ModelState.IsValid)
{
//Work on the data from the request...
}
else
{
//Retrieve the exceptions raised during deserialization
var errors = ModelState.SelectMany(v => v.Value.Errors.Select(e => e.Exception));
List<String> messages = new List<string>();
foreach (Exception e in errors)
{
messages.Add(e.GetType().ToString() + ": " + e.Message);
}
return new StdResponse(exchangeVersion, "null", ExecutionResponse.WithError("StdRequest invalid", messages));
}
}
You can check ModelState.IsValid inside your controller.
If "OnDeserialisedMethod" throws an exception (or any other model validation fails) it will be false, if everything succeed it will be true.

Categories