ASP.NET - setting status code in controller response - c#

I am a little bit confused sending specific status codes in responses from Web API controllers. Here is example of a simple controller method:
public IHttpActionResult GetDeliveryMethods()
{
if (AuthenticationControl)
{
return Ok(db.DeliveryMethods);
}
else
return StatusCode(HttpStatusCode.Unauthorized);
}
Well, "AuthenticationControl" represents some procedure checking, if a client can get data from the server. Whole method works well, but when I get back a response from the server status code is always 200 OK.
I don't understand why, this declaration of responses and status codes is also in main controller template in VS.
What I have to do to send my declaration of status code in response?

Return HTTPResoponse instead of your current return type.
This allows you to set HTTP status codes

Related

C#/.NET POST request keeps returning 400 Bad Request in controller

I am trying to write a simple API te receive POST requests with a body. When I try to test my method it keeps resulting in a 400 bad request in Postman. I first thought the issue was with deserializing the JSON body. So to be sure I stripped out that logic from the controller, but the request still returned a 400 status code.
So I removed everything from my method except for the method itself, only returning Ok('Hello World'); and still the response was a 400.
What I have left for now is this:
[Route("api/v1/service/")]
public class ServiceController : Controller
{
public ServiceController()
{
}
[HttpGet("get")]
public IActionResult Get()
{
return Ok("GET works fine");
}
[HttpPost("post")]
public IActionResult Post()
{
return Ok("Hello World"); // <-- Keeps returning 400
}
}
The GET method works fine, but when I sent an empty POST call to /api/v1/service/post in Postman I get a bad request.
I also noticed that when I change the route to something different or random that does not exists it also gets a 400, instead of a 404.
So making a POST call to api/v1/service/this-route-is-not-defined also results in a bad request.
I keep changing small things in my request form adding/removing ContentType or Accept headers and adjusting my StartUp.cs . But every POST call I make to .NET seems to result in a 400 status code.
Edit
This might be related to the routing in Startup.cs:
app.UseHsts();
app.UseMvc(routes =>
{
});
app.UseRouting();
This is the request in POST man:
GET:
POST:
The code in the sample was offcourse altered from my original API method, but the idea is the same. I copied the sample to a new file in my project and clicked in Postman on create new request. So headers are the default ones.
Your missing MapControllers()
In your startup.cs add MapControllers(), this is required for attribute based routing.
app.MapControllers();
If the version of .NET you are using is < 6.0 then add like so:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
MapControllers is called to map attribute routed controllers.
First of all, the answers and comments given to this question were all helpfull.
I found the culprit. Apperently there was an option enabled in the Startup.cs file that puts an anti-forgery token check on all API calls that can modify stuff, like POST, PUT, DELETE. This is not an issue when calling the API from the frontend with a Javascript fetch() for instance. The token is added to a tag in the document and you can add to the request headers like this:
headers.append('X-XSRF-TOKEN', (document.getElementsByName("__RequestVerificationToken")[0] as any).value)
To be able to make a POST call from Postman for instance you can add this line temporarely above your action.
[IgnoreAntiforgeryToken]
So working example would like this:
[Route("api/v1/service/")]
public class ServiceController : Controller
{
public ServiceController()
{
}
[HttpGet("get")]
public IActionResult Get()
{
return Ok("GET works fine");
}
[IgnoreAntiforgeryToken]
[HttpPost("post")]
public IActionResult Post()
{
return Ok("Hello World"); // <-- Keeps returning 400
}
}
It is important to think about when to use [IgnoreAntiforgeryToken] and not to use it. On methods that allready expect an API key for instance you can use it in a production environment. But when method is public the anti-forgery token is a way of protecting your method from attackers or people/robots trying to spam your API.

is return Problem(errorMessage) is the right way to return any kind of error in asp.net core?

I am new to Asp.net core web API.
is return Problem(errorMessage) is the right way to return any kind of error in asp.net core if we are not sure about a particular status code.
below is my code,
[HttpPost]
public async Task<ActionResult> Post([FromForm] MyModel request)
{
var (success,statuscode, errorMessage) = await _service.MyMethod(Id, request);
return success ? Ok() : Problem(errorMessage, statusCode: statuscode);
}
MyMethod method will either return BadRequest or UnprocessableEntity status code if any error will come.
So,is it a right way to return the error in Problem()?
Thanks in advance!
For any validation issues, you should return UnprocessableEntity which is 422 if you want to go by the book and add the details on ProblemDetails(check below).
Now, this is not mandatory. At the end of the days your api is a contract between you and your client, and you should define this hand check for your integration. What are you expecting on the server side and what is the client going to receive.
Now for global exceptions you can inject a middleware and capture them and provide a specified details of what happened. In this case you should return a ProblemDetails instance.
ProblemDetails class
The ietf tried to create a standard by specifying the details of this class.
Now by default when you return this type you should include as well a MediaType, if the request was a get, you should return as part of the headers Accept: application/problem + json
Using a post you will be adding Content-Type:application/problem + json instead.
My recommendation:
Now for Validation issues you can check for ModelState.IsValid and if there is any error of your model it should be on the Errors property.
For exceptions go with the middleware.
Now this is not a blue print. You can extend the class or the customer may use another type of format to process your responses in case of any errors, at the end of the day as I said, this is a contract between you and your client.

WebApi Internal Server Error

I have 2 projects, a Front-End (AngularJS) and a Back-End (C# Web-Api). The thing is, when the front-end query the api (e.g GET localhost/api/Especialistas?rol=XXXX) I get a 500 error. Here is the code of the API:
public IHttpActionResult GetEspecialista(string rol)
{
Especialista especialista = db.Especialistas.First( e=> e.Rol == rol);
if (especialista == null)
{
return NotFound();
}
return Ok(especialista);
}
The API is working, since I reach the return Ok(especialista).
The Front-end is using this Restangular to query the API
Restangular.one("Especialistas?rol=XXXX").get()
The Network console shows a request 200 OK OPTION, but a 500 Internal Server Error GET.
I tried a message handler in the WebApiConfig.cs file to check if the GET request was reaching the Api, and is indeed reaching it, so I don't know what happened, since I didn't change any configuration file.
Any clue on how to fix this problem will be appreciated, thanks.
If your action is called successfully, but still receive a 500 error, I think the error is created by the serializing of especialista object when converted to a HTTP response.
Most probably, serialization fails because of some navigation properties which creat cycles in your object graph. It is recommended to return simple objects, not entity framework models.
Try the following:
var retObj = new { Prop1 = especialista.Prop1, Prop2 = especialista.Prop2 };
return Ok(retObj);
If above code works, I suggest creating service models "mirror" objects that should be populated based on your data models. These objects should be returned instead of data models.
A good helper to avoid the boilerplate code of property value assignments is Automapper.

Why does ASP.NET Web Api model binding uses the parameter type to determine the source of the value?

Since a few days I'm trying to create my own web api controller. Duo to the rest conventions I need to use a post request to create an object. To get concrete, Im having this controller with this action:
public class ReservationController : ApiController
{
[HttpPost]
public void Create(int roomId, DateTime arrivalDate)
{
//do something with both parameters
}
}
This code is not working when I fire a post request at it, I'm receiving a 404 exception something like this:
No action was found on the controller 'Some' that matches the request.
The reason for it is that simple types are read from the query string, complex types from the body, according to this aricle. The web api uses the parameters to match the action to a request and can't therefore map my action to the request.
I do know that I can use the [frombody] tag, but you can only apply that to one parameter and I have 2. I also know that I can create a wrapper object which have both the parameters, but I'm not willing to use wrappers for all my calls.
So I do know that I can work around this by these methods. I also think that this is caused by the fact that the body of the post request can only be read once. But my actual question is:
Why is the source of a parameter determined by it's type and not by it's availability, especially when the conventions state that you should make for example a post request for creation? In MVC this is the case, why isn't it in the web api?
Best regards,
BHD
FINAL UPDATE
Since I'm getting some upvotes, problably more people are facing the same question. In the end it comes to this: Web-Api != MVC. It's simply not the same thing and the web api team made different design decisions than the mvc team I guess.
It seems that you have a fundamental misunderstanding of how Web API actually works.
Web API routing is driven off of verbiage, not the method names. "SomeMethod" actually translates to zero useful information for Web API. As a result, if I post
api/some/some?id=1
OR
api/some/somemethod?id=1
OR EVEN
api/some/?id=1
and the SomeMethod endpoint is the ONLY available POST, it will hit that endpoint.
As such, first of all, make sure you have only one POST on that api controller. If you do, POSTing to it from any test client using either of the query strings above will work just fine.
You can use the [FromBody] attribute on the parameter to force it to read from the body of the HTTP POST instead of the Uri. This is opposed to the [FromUri] attribute which does the opposite.
[HttpPost]
public void SomeAction([FromBody] int id)
{
//do something with id
}
Are you sure you're actually putting the id in the body? It could also be a routing issue. If this still doesn't work then maybe you should use Fiddler and copy the RAW output of your HTTP message here.
If you're packing multiple values into the body such as with JSON then you should use a model which should automatically be deserialized to:
public class PostModel
{
public int ID { get; set; }
public int SomeOtherID { get; set; }
}
[HttpPost]
public void SomeAction(PostModel postModel)
{
//do something with postModel.ID and postModel.SomeOtherID
}
You can actually do this straight out of the box in WebAPI, at least in 2.2 (.Net version 4.5.2). Your controller is correct. Using your controller, if you call it with a HTTP POST like this (tested through Fiddler):
http://localhost:58397/api/Reservation?roomId=123&arrivalDate=2015-12-17
You'll get the correct values of roomId = 123 and arrivalDate = 17.12.2015.
I suspect there's something wrong in your call to the WebAPI. Maybe post that call if you're still not getting it to work.

In ASP.NET MVC Should I Redirect to an Error Page or Simply Return an Error View?

As I ask this question, I'm mainly thinking about bad parameters. Parameters where:
int <= 0
string is empty or whitespace
model bound object is missing key properties
"id not found errors" (a "valid" integer id is passed to the action, but there is no corresponding database record)
Here are the two error handling scenarios I'm talking about:
public ActionResult GoToError(int value, string name)
{
if (value <= 0 || string.IsNullOrWhiteSpace(name))
{
// Parameter(s) not meeting basic conditions
TempData["ErrorMessage"] = "Invalid parameters";
return RedirectToAction("Index", "Error");
}
return View();
}
public ActionResult ReturnView(int value, string name)
{
if (value <= 0 || string.IsNullOrWhiteSpace(name))
{
// Parameter(s) not meeting basic conditions
ViewData["ErrorMessage"] = "Invalid parameters";
return View("Error");
}
return View();
}
There are different scenarios, that need to be handled differently:
For totally unexpected errors you don't anticipate, I suggest letting the exception bubble up and handling it in the Controller.OnException(...) method and/or the asp.net custom error pages
For common errors that are anticipated, such as a user providing bad input, its proper to return a view and display errors passed through ModelState.Errors
If the action is intended to be called via AJAX / Javascript / As a service, you need to coordinate what you send back. It may be a custom JSON object or a specific view.
Based on what you've posted, I can't classify weather they're 'anticipated' or 'unanticipated.' The main question is, how does the client of these calls expect them to be handled?
In a RESTFul application you should return the view and set the corresponding HTTP status code (401, 403, 404, 500, ...). When you redirect this means status code 200 and returning status code 200 for an error page doesn't make sense. Here's a technique that I use to handle errors. This works also very nicely with AJAX when you can subscribe for different status codes. For example let's suppose that you have an authenticated site where logged in users can perform AJAX requests. After certain inactivity their session could expire and when they need to perform some AJAX request if your server doesn't return proper status code (401 in this case) the client script will have hard time understanding and handling the scenario.
Doing errors properly in ASP.NET is just short of nightmare-ishly hard.
If you follow what the web is meant to do:
You should return an error result for any impossible to recover from error, like 400 bad request, 404 resource not found, etc. This includes most of the errors in the 4xx range.
For application errors that are unhandled exception that will result in a 500 error, the correct solution is to issue a 302 redirect to an error page that properly returns the 500 status code.
As #Darin Dimitrov said, in RESTful applications, you should never issue a redirect for anything it should always return the result definitively.

Categories

Resources