Generate response error in Swagger - .netCore Api - c#

For handling all errors I use ErrorHandlingMiddleware. Anywhere where I want I throw exception (my implementation exception) where I define type (enum) and message of errors.
My question is how can I generate this type of error in swagger, becaouse swagger know only about controller return type values. And swagger generate only response (200) bcs in methods is only return Ok();
[HttpPost("login")]
public async Task<IActionResult> Test(LoginRequest req)
{
if (user.Banned)
{
throw new BadRequestHorseedoException(ErrorCode.BANNED, "User is banned");
}
return Ok();
}

You can use ProducesResponseType attribute on methods. Your code can look like:
[HttpPost("login")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> Test(LoginRequest req)

Related

How to return a BadRequest when the return type is not an ActionResult?

It seems that the HttpGet method's return type need not be an ActionResult. For example, the following method works:
[HttpGet]
[Route("list")]
public async Task<IEnumerable<MyItem>> List()
But then, how can I return a BadRequest (BadRequest("...")) in this case?
If you really have to respond with BadRequest from within the controller method, you can use the following approach as recommended by Microsoft here
[HttpGet("list")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<MyItem>))]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async IActionResult List()
{
// Code to validate request
if(requestIsValid)
{
// Code to get IEnumerable of MyItems
return Ok(myItems);
}
else
{
return BadRequest()
}
}

.Net Api - Parent route matched for invalid nested route

I have a .Net 5 API and some nested routes as follows:
[Route("api/v{version:apiVersion}/orders")]
[ApiVersion("1.0")]
[ApiController]
public class OrdersController: ControllerBase
{
[HttpGet("{userId:required}")]
public async Task<ActionResult> Get(string userId,
CancellationToken cancellationToken = default)
{
// return the orders corresponding to the userId
}
}
[Route("api/v{version:apiVersion}/orders/details")]
[ApiVersion("1.0")]
[ApiController]
public class OrdersDetailsController: ControllerBase
{
[HttpGet("{orderId:required}")]
public async Task<ActionResult> Get(string orderId,
CancellationToken cancellationToken = default)
{
// return the order details
}
}
Below is the list of responses I get when making requests to the API:
GET /orders/some_dummy_user_id returns the orders for userId="some_dummy_user_id", which is OK
GET /orders/details/some_dummy_order_id returns the details of orderId="some_dummy_order_id", which is OK
GET /orders/details/ tries to return the orders corresponding to userId="details" which is Not OK
The question is: is it possible to make the GET /orders/details/ request match the OrderDetailsController route and therefore return a 404 because of the missing orderId URL parameter?
try this
[Route("api/v{version:apiVersion}/orders")]
[ApiVersion("1.0")]
[ApiController]
public class OrdersController: ControllerBase
{
[HttpGet("{userId:required}")]
public async Task<ActionResult> Get(string userId,
CancellationToken cancellationToken = default)
{
if(userId.ToLower=="details") throw new HttpException(404, "not found");
// return the orders corresponding to the userId
}
}
It seems that unfortunately it's not possible to achieve the required scenario using only the .Net routing features. The possible solutions I see in this situation are:
As mentioned in Serge's answer, introduce a manual check and throw a 404 Exception if the userId matches the sub-route
Update the routes schema in order to prevent this scenario
As I'd like to achieve this using only the .Net routing features I've proceeded with the 2nd solution (especially as the impact on the overall routes schema wasn't major).

Return a custom error Response from WebApi without throwing Exception

I would like to return a custom Response (ProblemJson) from my asp.net web api controller, but before the method of the controller itself gets executed. Given a controller that looks like this:
[Put]
[Route("api/{userId}")]
public User UpdateUser(Guid userId, [FromBody] user)
{
....
}
and i call the api with this
api/thisIsNoGuid
Then WebApi answers with a default error message that I would like to change so it fits with our other error messages with is a defined ProblemJson. Right now we generate these ProblemDocuments by throwing exceptions and catching them in the WebApi filters that generate the ProblemDocument for us. I could make the Guid nullable and throw an exception if the Guid is null, so our filters do the work, but I do not think that this is a good idea, as it will be forgotten and then the api is inconsistent.
I want to avoid code like this:
[Put]
[Route("api/{userId}")]
public User UpdateUser(Guid userId, [FromBody] user)
{
if (userId == null) return new Error()
....
}
So what I would like is something like the ExceptionFilters where you can override the OnException method to create your own response but for error cases that happen before the request reaches the controller. Is it possible to override the default behaviour of WebApi like this?
You can do this yes, and you're close when you mention ExceptionFilters, because you actually handle this case using a custom ActionFilter. Something like this:
using System.Web.Http.Filters;
public class InterceptBadRequestFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// whatever your condition is
if (...)
{
var response = actionContext
.Request
.CreateErrorResponse(HttpStatusCode.BadRequest, "Custom error message");
actionContext.Response = response;
}
}
}
There is an OnActionExecutingAsync method you can override as well, if you need that.
For custom errors, use IHttpActionResult type:
[Put]
[Route("api/{userId}")]
public IHttpActionResult UpdateUser(Guid userId, [FromBody] user)
{
if (userId == null)
{
var message = "Sorry, this is not a valid GUID";
HttpError err = new HttpError(message);
return ResponseMessage(Request.CreateResponse(HttpStatusCode.InternalServerError, err));
}
. . .
}

What is the purpose of BadRequest(ModelState)?

What is the utility of
protected internal virtual InvalidModelStateResult BadRequest(ModelStateDictionary modelState);
from public abstract class ApiController
I juts tried to us it for my Web API
if (!ModelState.IsValid)
{
var foo = BadRequest(ModelState);
}
but nothing happens
Should I override it? And how because I can override 3 BadRequest method with different return result.
BadRequest method returns InvalidModelStateResult which is IHttpActionResult.
So it should be something you return from action
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
When BadRequest is used then response has 400 status code set.

How to return HTTP 500 from ASP.NET Core RC2 Web Api?

Back in RC1, I would do this:
[HttpPost]
public IActionResult Post([FromBody]string something)
{
try{
// ...
}
catch(Exception e)
{
return new HttpStatusCodeResult((int)HttpStatusCode.InternalServerError);
}
}
In RC2, there no longer is HttpStatusCodeResult, and there is nothing I can find that lets me return a 500 type of IActionResult.
Is the approach now entirely different for what I'm asking? Do we no longer try-catch in Controller code? Do we just let the framework throw a generic 500 exception back to the API caller? For development, how can I see the exact exception stack?
From what I can see there are helper methods inside the ControllerBase class. Just use the StatusCode method:
[HttpPost]
public IActionResult Post([FromBody] string something)
{
//...
try
{
DoSomething();
}
catch(Exception e)
{
LogException(e);
return StatusCode(500);
}
}
You may also use the StatusCode(int statusCode, object value) overload which also negotiates the content.
You could use Microsoft.AspNetCore.Mvc.ControllerBase.StatusCode and Microsoft.AspNetCore.Http.StatusCodes to form your response, if you don't wish to hardcode specific numbers.
return StatusCode(StatusCodes.Status500InternalServerError);
UPDATE: Aug 2019
Perhaps not directly related to the original question but when trying to achieve the same result with Microsoft Azure Functions I found that I had to construct a new StatusCodeResult object found in the Microsoft.AspNetCore.Mvc.Core assembly. My code now looks like this;
return new StatusCodeResult(StatusCodes.Status500InternalServerError);
If you need a body in your response, you can call
return StatusCode(StatusCodes.Status500InternalServerError, responseObject);
This will return a 500 with the response object...
For aspnetcore-3.1, you can also use Problem() like below;
https://learn.microsoft.com/en-us/aspnet/core/web-api/handle-errors?view=aspnetcore-3.1
[Route("/error-local-development")]
public IActionResult ErrorLocalDevelopment(
[FromServices] IWebHostEnvironment webHostEnvironment)
{
if (webHostEnvironment.EnvironmentName != "Development")
{
throw new InvalidOperationException(
"This shouldn't be invoked in non-development environments.");
}
var context = HttpContext.Features.Get<IExceptionHandlerFeature>();
return Problem(
detail: context.Error.StackTrace,
title: context.Error.Message);
}
A better way to handle this as of now (1.1) is to do this in Startup.cs's Configure():
app.UseExceptionHandler("/Error");
This will execute the route for /Error. This will save you from adding try-catch blocks to every action you write.
Of course, you'll need to add an ErrorController similar to this:
[Route("[controller]")]
public class ErrorController : Controller
{
[Route("")]
[AllowAnonymous]
public IActionResult Get()
{
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
More information here.
In case you want to get the actual exception data, you may add this to the above Get() right before the return statement.
// Get the details of the exception that occurred
var exceptionFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionFeature != null)
{
// Get which route the exception occurred at
string routeWhereExceptionOccurred = exceptionFeature.Path;
// Get the exception that occurred
Exception exceptionThatOccurred = exceptionFeature.Error;
// TODO: Do something with the exception
// Log it with Serilog?
// Send an e-mail, text, fax, or carrier pidgeon? Maybe all of the above?
// Whatever you do, be careful to catch any exceptions, otherwise you'll end up with a blank page and throwing a 500
}
Above snippet taken from Scott Sauber's blog.
How about creating a custom ObjectResult class that represents an Internal Server Error like the one for OkObjectResult?
You can put a simple method in your own base class so that you can easily generate the InternalServerError and return it just like you do Ok() or BadRequest().
[Route("api/[controller]")]
[ApiController]
public class MyController : MyControllerBase
{
[HttpGet]
[Route("{key}")]
public IActionResult Get(int key)
{
try
{
//do something that fails
}
catch (Exception e)
{
LogException(e);
return InternalServerError();
}
}
}
public class MyControllerBase : ControllerBase
{
public InternalServerErrorObjectResult InternalServerError()
{
return new InternalServerErrorObjectResult();
}
public InternalServerErrorObjectResult InternalServerError(object value)
{
return new InternalServerErrorObjectResult(value);
}
}
public class InternalServerErrorObjectResult : ObjectResult
{
public InternalServerErrorObjectResult(object value) : base(value)
{
StatusCode = StatusCodes.Status500InternalServerError;
}
public InternalServerErrorObjectResult() : this(null)
{
StatusCode = StatusCodes.Status500InternalServerError;
}
}
return StatusCode((int)HttpStatusCode.InternalServerError, e);
Should be used in non-ASP.NET contexts (see other answers for ASP.NET Core).
HttpStatusCode is an enumeration in System.Net.
When you want to return a JSON response in MVC .Net Core You can also use:
Response.StatusCode = (int)HttpStatusCode.InternalServerError;//Equals to HTTPResponse 500
return Json(new { responseText = "my error" });
This will return both JSON result and HTTPStatus. I use it for returning results to jQuery.ajax().
The built-in Problem()-method of Microsoft.AspNetCore.Mvc will return a "problem detail"-response based on RFC 7807 (in ASP.NET Core 3.0 and later). It will always return status-code 500 as long as no other status is explicitly set.
[HttpPost]
public IActionResult Post([FromBody] string value)
{
try
{
// ...
}
catch (Exception ex)
{
return Problem(
//all parameters are optional:
//detail: "Error while processing posted data."; //an explanation, ex.Stacktrace, ...
//instance: "/city/London" //A reference that identifies the specific occurrence of the problem
//title: "An error occured." //a short title, maybe ex.Message
//statusCode: StatusCodes.Status504GatewayTimeout, //will always return code 500 if not explicitly set
//type: "http://example.com/errors/error-123-details" //a reference to more information
);
}
}
Without setting any parameters it will return this:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.6.1",
"title": "An error occured while processing your request.",
"status": 500,
"traceId": "|fadaed95-4d06eb16160e4996."
}
More infos about "problem details" parameters:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.problemdetails?view=aspnetcore-5.0
For API Responses (using net core), I have tried this and seems that it is working fine:
var err = Content(JsonConvert.SerializeObject(response, SerializerSettings), "application/x-javascript", contentEncoding: System.Text.Encoding.UTF8);
err.StatusCode = StatusCodes.Status500InternalServerError;
return err;
You just need to create a response object first, then respond this. Doing this, we can retain the content type, encoding, and add a status code as well.
Just adding this for future reference to anybody who is stuck as well and wants a quick and easy way to do this.

Categories