What is the purpose of BadRequest(ModelState)? - c#

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.

Related

Generate response error in Swagger - .netCore Api

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)

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()
}
}

How to retrieve HTTP Code and Content from IActionResult?

I've 7 actions in my controllers. I've refactored them an was able to isolate the common portion.
public IActionResult Get()
{
var response = CommonCode();
}
public IActionResult Get(guid id)
{
var response = CommonCode();
}
public IActionResult Post(ViewModel vm)
{
var response = CommonCode();
}
This is where I refactored the common code.
provate IActionResult CommonCode()
{
if(userHasNoPermission())
return Forbid();
if(IdProvidedDoesntExist())
return BadRequest();
//...
}
When I look inside the response, I see only one method: ExecuteResultAsync().
Is there a way to retrieve the HTTP code that I sent inside the helper method?
I'd like for instance to stop the processing if it's 500, retrieve the message to add to the ModelState* if it's 400, but proceed if it's OK.
There are a few "filthy" ways you can do it without a case statement. The status code is actually in the result but IActionResult and ActionResult hate being cast to a common type.
This example takes a IActionResult from any common result and rips the status code out using reflection. If you don't mind doing it that way it saves requiring the case statement to pattern match. The same can be done for the content.
public static HttpStatusCode GetHttpStatusCode(IActionResult functionResult)
{
try
{
return (HttpStatusCode)functionResult
.GetType()
.GetProperty("StatusCode")
.GetValue(functionResult, null);
}
catch
{
return HttpStatusCode.InternalServerError;
}
}
The return of CommonCode is simply some type of IActionResult. The "result" is not actually a "response". That comes later when the action is fully returned back into the request pipeline (which has not occurred yet when you're calling the method directly in code). As result, concepts like HTTP status code aren't even relevant yet. If you return something like StatusCodeResult, that's technically only a suggested status code. If there's an exception later in the request pipeline or some piece of middleware explicitly changes the status code for some reason, it will be different.
Long and short, you're trying to conflate two unrelated things together. I think you simply want to know what happened in CommonCode, and think the HTTP status is the best way to determine that. In actuality, you'd be better served by returning a tuple or doing something like pattern matching:
With a tuple, you can essentially return more than one thing from your CommonCode method. For example, you could do something like:
private (int, IActionResult) CommonCode()
{
if(userHasNoPermission())
return (403, Forbid());
if(IdProvidedDoesntExist())
return (400, BadRequest());
//...
}
Then:
public IActionResult Get()
{
(var status, var response) = CommonCode();
// branch on `status`
}
Or, with pattern matching:
public IActionResult Get()
{
var response = CommonCode();
switch (response)
{
case ForbidResult forbid:
// do something for forbidden
break;
case BadRequestResult badRequest:
// do something for bad request
break;
}
}
I had a similar problem returning Ok() and NotFound(). I
was able to get the status code using the IStatusCodeActionResult interface.
((IStatusCodeActionResult)response).StatusCode;
You could return the statuscode itself from the private method. for example
private IActionResult CommonCode() {
if(userHasNoPermission())
return StatusCode(401);
if(IdProvidedDoesntExist())
return StatusCode(400);
//... }
then in your method just check for the status code
public IActionResult Post(ViewModel vm)
{
var response = CommonCode();
if (this.HttpContext.Response.StatusCode == 418)
Console.WriteLine("I'm a teapot")
else {
return response;
}
}

Catch an invalid HTTP request method globally

I would like to restrict my Web API endpoints to certain HTTP methods, such as GET and POST. I have searched the internet and I found out that you can add either [HttpGet] or [HttpPost] above your method as follows:
[HttpPost]
public ActionResult Login(string userName, string password) {
// do login stuff
return View();
}
Now I want to test if the example above with [HttpPost] really works so I use postman to send a HTTP request to my Web API. I fill in the URI and set the method to GET. The response I get is as follows:
{
"message": "The requested resource does not support http method 'POST'."
}
I'm able to verify that adding [HttpPost] prevents me from using HTTP GET requests.
What I would like to do now is log the event whenever an user tries to sent GET requests when the application is expecting POST, and vice versa. I could implement something for every single method but this would take a lot of time and it wouldn't be easy to make changes once it's been implemented. So I would like to filter it globally or something.
For example something like:
class MethodRequestFilter : SomeGlobalMethodFilter
{
public override void Filter(SomeRequestContext requestContext)
{
if (usedRequestMethod.isNotValid == true)
{
//implementation for logging
}
}
}
But ofcourse I haven't been able to find this yet within the libraries of .Net. How can I log the event globally whenever a user tries to make a request that isn't a supported method?
Greetings,
Damien.
One way is to using common base controller, to implement you need to add one base controller which would inherited from ApiController
public class BaseController : ApiController
{
public override async Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response = await base.ExecuteAsync(controllerContext, cancellationToken);
if (!response.IsSuccessStatusCode) // or if(response.StatusCode == HttpStatusCode.BadRequest)
{
//log here
}
return response;
}
catch(Exception ex)
{
return await InternalServerError(ex).ExecuteAsync(cancellationToken);
}
}
}
Now, let's assume that you're having ValuesController and Login method and it supports only POST, here your all other controllers inherit from BaseController instead ApiController
public class ValuesController : BaseController
{
[HttpPost]
public void Login([FromBody]string value)
{
}
}
So, once you call your login method, it'll call BaseController method first and you will get response there.
Hope this helps!
Thanks to the user div I was able to solve my problem by using a base controller that implements logging. These are the steps that I had to take:
Create a new controller class called BaseController and inherit ApiController:
Override the ExecuteAsync method from ApiController:
Add an implementation for logging in the catch clause
Inherit the new BaseController in every controller class that you would like to have logging functionality.
The code that I used in my implementation:
public class BaseController : ApiController
{
public override async Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response = await base.ExecuteAsync(controllerContext, cancellationToken);
return response;
}
catch (HttpResponseException ex)
{
if (ex.Response.StatusCode == HttpStatusCode.MethodNotAllowed)
{
//Logging implementation
}
return Request.CreateErrorResponse(ex.Response.StatusCode, ex.Message);
}
}
}
If there is any way to make my code better, please let me know :)

How can I return a HttpStatusCode from an IQueryable<T>?

I am coding a C# WebApi 2 webservice, and I have a question in regards to returning a HttpStatusCode from an IQueryable<T> webservice function.
If a web service function returns a single object, the following can easily be used:
public async Task<IHttpActionResult> GetItem(int id)
{
return Content(HttpStatusCode.Unauthorized, "Any object");
}
In the following situation, I am not sure on how to return a specified HttpStatusCode:
public IQueryable<T> GetItems()
{
return Content(HttpStatusCode.Unauthorized, "Any object");
}
Can I please have some help with this?
You can throw a HttpResponseException with the appropriate statuscode
public IQueryable<T> GetItems()
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent("Any object") });
}
In the the first situation, you are still going to be returning the object in the Content method, e.g:
public IHttpActionResult GetItem(int id)
{
return Content(HttpStatusCode.Unauthorized, "Any object");
}
When you execute that method, you would see a "Any object" returned to the client. In Web API 2 IHttpActionResult is essentially a fancy factory around HttpResponseMessage. If you ignore that the return type of the method is IHttpActionResult, you can return an IQueryable by replacing "Any object" with the queryable, e.g:
public IHttpActionResult GetItem(int id)
{
IQueryable<object> results;
return Ok(results);
}
Some more info on action results in Web API can be found here:
http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/action-results

Categories