Null response returns a 204 - c#

My controller returns a 204 when I do a GET request and I don't find any data.
[Route("user/v1/[controller]")]
public class UserLoginController : Controller
{
[HttpGet]
public async Task<UserLogin> Get(int userId)
{
var userLoginLogic = new UserLoginLogic();
return await userLoginLogic.GetUserLogin(userId);
}
}
This is only for GET requests, POST, PUT, DELETE return a 200 empty response. This messes with my swagger definition which has a response defined for a 200 response, and I would rather be consistent.
The 204 would be fine if I was serving HTML out of this controller but it is for a REST API.
How do I get it to return a 200?

With the new ActionResult<T> in v2.1+ you can also refactor to specifically tell the controller to return Ok 200 using the Ok() helper methods
[Route("user/v1/[controller]")]
public class UserLoginController : Controller {
[HttpGet]
public async Task<ActionResult<UserLogin>> Get(int userId) {
var userLoginLogic = new UserLoginLogic();
var model = await userLoginLogic.GetUserLogin(userId);
return Ok(model);
}
}
however this can be misleading if there is in fact no content to return. Consider using an appropriate response status
[Route("user/v1/[controller]")]
public class UserLoginController : Controller {
[HttpGet]
public async Task<ActionResult<UserLogin>> Get(int userId) {
var userLoginLogic = new UserLoginLogic();
var model = await userLoginLogic.GetUserLogin(userId);
if(model == null) return NotFound(); //404
return Ok(model); //200
}
}
If intent on returning 200 Ok with no content use ControllerBase.Ok() method
Creates a OkResult object that produces an empty Status200OK response.
[Route("user/v1/[controller]")]
public class UserLoginController : Controller {
[HttpGet]
public async Task<ActionResult<UserLogin>> Get(int userId) {
var userLoginLogic = new UserLoginLogic();
var model = await userLoginLogic.GetUserLogin(userId);
if(model == null) return Ok(); //200 with no content
return Ok(model); //200
}
}
Reference Controller action return types in ASP.NET Core Web API:

See:
https://learn.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-3.1#special-case-formatters
https://www.colabug.com/2020/0224/7036191/
https://weblog.west-wind.com/posts/2020/Feb/24/Null-API-Responses-and-HTTP-204-Results-in-ASPNET-Core
services.AddControllers(opt => // or AddMvc()
{
// remove formatter that turns nulls into 204 - No Content responses
// this formatter breaks Angular's Http response JSON parsing
opt.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
})

Related

How to capture ActionResult Api Response in Angular?

I have the below method in my user controller:
[HttpPost]
public async Task<ActionResult<bool>> Create(User user)
{
var userCreated = userService.register(user); // returns true or false
if (userCreated)
{
return Ok();
}
else
{
return BadRequest("Could not create user.");
}
}
This method should then be captured in my angular calling the service:
onSubmit(user: User): void {
this.userService.registerUser(user).subscribe((response) => {
console.warn(response);
});
}
The register URL method:
registerUser(user: User): Observable <boolean> {
const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) };
return this.http.post<boolean>(environment.userUrl, user, httpOptions);
}
Unfortunately, the console writes null. Am I missing out anything? I want to capture whether the status is OK or BadRequest.
In your controller you are missing to return the value of your response:
[HttpPost]
public async Task<ActionResult<bool>> Create(User user)
{
var userCreated = userService.register(user); // returns true or false
if (userCreated)
{
return Ok(userCreated); // <= HERE
}
else
{
return BadRequest("Could not create user.");
}
}
After changing the above, you should be OK.
A small tip is that you don't need to add HttpOptions on every request in Angular.
The HttpClient is doing that for you:
registerUser(user: User): Observable<boolean> {
return this.http.post<boolean>(environment.userUrl, user);
}
PS: Ok() in C# means that you are returning a response with code 200.
On the other hand, a BadRequest() will result a 400 error code and will be caught as error inside subscription. In your case I thing that the code in the back end should be like this:
[HttpPost]
public async Task<ActionResult<bool>> Create(User user)
{
var userCreated = userService.register(user); // returns true or false
return Ok(userCreated);
}

Redirecting action from one controller to other with different secure attributes, ASP.NETCore 2.0

I have one question, which connected with redirecting and auth policies.
Let's have one controller, which allow Anonymous method like this:
[Route("Authorization")]
[Authorize]
public class AuthorizationController : Controller
{
...
[HttpPost]
[Route("AddUser")]
[AllowAnonymous]
public async Task<IActionResult> AddUser()
{
return await Task.Run<ActionResult>(() =>
{
return RedirectToAction("Post", "Proxy");
});
}
}
Second controller has Post method, which needs authorization
[Authorize]
public class ProxyController : Controller
{
...
[HttpPost]
public async Task Post()
{
var uri = new Uri(UriHelper.GetEncodedUrl(Request));
var routedUri = NewRouteBuilder(uri);
var client = new HttpClient();
var response = await client.PostAsync(routedUri, new StreamContent(Request.Body));
var content = await response.Content.ReadAsStringAsync();
Response.StatusCode = (int)response.StatusCode;
Response.ContentType = response.Content.Headers.ContentType?.ToString();
Response.ContentLength = response.Content.Headers.ContentLength;
await Response.WriteAsync(content);
}
}
If I use this code, I get 401 error in AuthorizationController, when I call AddUser.
Both these controllers are in one project. How it's possible to redirect on action in this case (which allow pass to action only authorized users or calls from ProxyController)?
Thank you.

CreatedAtRoute with OK(200) code

Whether it's acceptable RESTful design or not, I'd like to give a result as like below code doing but with 200 OK StatusCode.
return CreatedAtRoute("DefaultApi", new { id = model.Id }, model);
Above one provides Location header which utilize given id route variable and json serialized model content.
return Ok(); // how to make it with this?
Note that I'm using ASP.NET WebApi2 (.Net 4.6) template.
What about this, create a custom IHttpActionResult that decorates CreatedAtRouteNegotiatedContentResult and updates the status code:
public class OkWithLocation<T> : IHttpActionResult
{
private readonly CreatedAtRouteNegotiatedContentResult<T> _result;
public OkWithLocation(CreatedAtRouteNegotiatedContentResult<T> result)
{
_result = result;
}
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = await _result.ExecuteAsync(cancellationToken).ConfigureAwait(false);
response.StatusCode = HttpStatusCode.OK;
return response;
}
}
And then use this in your controller action:
return new OkWithLocation<Model>(CreatedAtRoute("DefaultApi", new { id = model.Id }, model));
Maybe not the prettiest, but it gets the job done.

Returning http status code from Web Api controller

I'm trying to return a status code of 304 not modified for a GET method in a web api controller.
The only way I succeeded was something like this:
public class TryController : ApiController
{
public User GetUser(int userId, DateTime lastModifiedAtClient)
{
var user = new DataEntities().Users.First(p => p.Id == userId);
if (user.LastModified <= lastModifiedAtClient)
{
throw new HttpResponseException(HttpStatusCode.NotModified);
}
return user;
}
}
The problem here is that it's not an exception, It's just not modified so the client cache is OK.
I also want the return type to be a User (as all the web api examples shows with GET) not return HttpResponseMessage or something like this.
I did not know the answer so asked the ASP.NET team here.
So the trick is to change the signature to HttpResponseMessage and use Request.CreateResponse.
[ResponseType(typeof(User))]
public HttpResponseMessage GetUser(HttpRequestMessage request, int userId, DateTime lastModifiedAtClient)
{
var user = new DataEntities().Users.First(p => p.Id == userId);
if (user.LastModified <= lastModifiedAtClient)
{
return new HttpResponseMessage(HttpStatusCode.NotModified);
}
return request.CreateResponse(HttpStatusCode.OK, user);
}
You can also do the following if you want to preserve the action signature as returning User:
public User GetUser(int userId, DateTime lastModifiedAtClient)
If you want to return something other than 200 then you throw an HttpResponseException in your action and pass in the HttpResponseMessage you want to send to the client.
Change the GetXxx API method to return HttpResponseMessage and then return a typed version for the full response and the untyped version for the NotModified response.
public HttpResponseMessage GetComputingDevice(string id)
{
ComputingDevice computingDevice =
_db.Devices.OfType<ComputingDevice>()
.SingleOrDefault(c => c.AssetId == id);
if (computingDevice == null)
{
return this.Request.CreateResponse(HttpStatusCode.NotFound);
}
if (this.Request.ClientHasStaleData(computingDevice.ModifiedDate))
{
return this.Request.CreateResponse<ComputingDevice>(
HttpStatusCode.OK, computingDevice);
}
else
{
return this.Request.CreateResponse(HttpStatusCode.NotModified);
}
}
*The ClientHasStale data is my extension for checking ETag and IfModifiedSince headers.
The MVC framework should still serialize and return your object.
NOTE
I think the generic version is being removed in some future version of the Web API.
In MVC 5, things got easier:
return new StatusCodeResult(HttpStatusCode.NotModified, this);
For ASP.NET Web Api 2, this post from MS suggests to change the method's return type to IHttpActionResult. You can then return a built in IHttpActionResult implementation like Ok, BadRequest, etc (see here) or return your own implementation.
For your code, it could be done like:
public IHttpActionResult GetUser(int userId, DateTime lastModifiedAtClient)
{
var user = new DataEntities().Users.First(p => p.Id == userId);
if (user.LastModified <= lastModifiedAtClient)
{
return StatusCode(HttpStatusCode.NotModified);
}
return Ok(user);
}
I hate bumping old articles but this is the first result for this in google search and I had a heck of a time with this problem (even with the support of you guys). So here goes nothing...
Hopefully my solution will help those that also was confused.
namespace MyApplication.WebAPI.Controllers
{
public class BaseController : ApiController
{
public T SendResponse<T>(T response, HttpStatusCode statusCode = HttpStatusCode.OK)
{
if (statusCode != HttpStatusCode.OK)
{
// leave it up to microsoft to make this way more complicated than it needs to be
// seriously i used to be able to just set the status and leave it at that but nooo... now
// i need to throw an exception
var badResponse =
new HttpResponseMessage(statusCode)
{
Content = new StringContent(JsonConvert.SerializeObject(response), Encoding.UTF8, "application/json")
};
throw new HttpResponseException(badResponse);
}
return response;
}
}
}
and then just inherit from the BaseController
[RoutePrefix("api/devicemanagement")]
public class DeviceManagementController : BaseController
{...
and then using it
[HttpGet]
[Route("device/search/{property}/{value}")]
public SearchForDeviceResponse SearchForDevice(string property, string value)
{
//todo: limit search property here?
var response = new SearchForDeviceResponse();
var results = _deviceManagementBusiness.SearchForDevices(property, value);
response.Success = true;
response.Data = results;
var statusCode = results == null || !results.Any() ? HttpStatusCode.NoContent : HttpStatusCode.OK;
return SendResponse(response, statusCode);
}
.net core 2.2 returning 304 status code. This is using an ApiController.
[HttpGet]
public ActionResult<YOUROBJECT> Get()
{
return StatusCode(304);
}
Optionally you can return an object with the response
[HttpGet]
public ActionResult<YOUROBJECT> Get()
{
return StatusCode(304, YOUROBJECT);
}
I don't like having to change my signature to use the HttpCreateResponse type, so I came up with a little bit of an extended solution to hide that.
public class HttpActionResult : IHttpActionResult
{
public HttpActionResult(HttpRequestMessage request) : this(request, HttpStatusCode.OK)
{
}
public HttpActionResult(HttpRequestMessage request, HttpStatusCode code) : this(request, code, null)
{
}
public HttpActionResult(HttpRequestMessage request, HttpStatusCode code, object result)
{
Request = request;
Code = code;
Result = result;
}
public HttpRequestMessage Request { get; }
public HttpStatusCode Code { get; }
public object Result { get; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Request.CreateResponse(Code, Result));
}
}
You can then add a method to your ApiController (or better your base controller) like this:
protected IHttpActionResult CustomResult(HttpStatusCode code, object data)
{
// Request here is the property on the controller.
return new HttpActionResult(Request, code, data);
}
Then you can return it just like any of the built in methods:
[HttpPost]
public IHttpActionResult Post(Model model)
{
return model.Id == 1 ?
Ok() :
CustomResult(HttpStatusCode.NotAcceptable, new {
data = model,
error = "The ID needs to be 1."
});
}
Try this :
return new ContentResult() {
StatusCode = 404,
Content = "Not found"
};
If you need to return an IHttpActionResult and want to return the error code plus a message, use:
return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.NotModified, "Error message here"));
Another option:
return new NotModified();
public class NotModified : IHttpActionResult
{
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.NotModified);
return Task.FromResult(response);
}
}
public HttpResponseMessage Post(Article article)
{
HttpResponseMessage response = Request.CreateResponse<Article>(HttpStatusCode.Created, article);
string uriToTheCreatedItem = Url.Route(null, new { id = article.Id });
response.Headers.Location = new Uri(Request.RequestUri, uriToTheCreatedItem);
return response;
}
An update to #Aliostads answer using the more moden IHttpActionResult introduced in Web API 2.
https://learn.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/action-results#ihttpactionresult
public class TryController : ApiController
{
public IHttpActionResult GetUser(int userId, DateTime lastModifiedAtClient)
{
var user = new DataEntities().Users.First(p => p.Id == userId);
if (user.LastModified <= lastModifiedAtClient)
{
return StatusCode(HttpStatusCode.NotModified);
// If you would like to return a Http Status code with any object instead:
// return Content(HttpStatusCode.InternalServerError, "My Message");
}
return Ok(user);
}
}
I know there are several good answers here but this is what I needed so I figured I'd add this code in case anyone else needs to return whatever status code and response body they wanted in 4.7.x with webAPI.
public class DuplicateResponseResult<TResponse> : IHttpActionResult
{
private TResponse _response;
private HttpStatusCode _statusCode;
private HttpRequestMessage _httpRequestMessage;
public DuplicateResponseResult(HttpRequestMessage httpRequestMessage, TResponse response, HttpStatusCode statusCode)
{
_httpRequestMessage = httpRequestMessage;
_response = response;
_statusCode = statusCode;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(_statusCode);
return Task.FromResult(_httpRequestMessage.CreateResponse(_statusCode, _response));
}
}

Return http 204 "no content" to client in ASP.NET MVC2

In an ASP.net MVC 2 app that I have I want to return a 204 No Content response to a post operation. Current my controller method has a void return type, but this sends back a response to the client as 200 OK with a Content-Length header set to 0. How can I make the response into a 204?
[HttpPost]
public void DoSomething(string param)
{
// do some operation with param
// now I wish to return a 204 no content response to the user
// instead of the 200 OK response
}
In MVC3 there is an HttpStatusCodeResult class. You could roll your own for an MVC2 application:
public class HttpStatusCodeResult : ActionResult
{
private readonly int code;
public HttpStatusCodeResult(int code)
{
this.code = code;
}
public override void ExecuteResult(System.Web.Mvc.ControllerContext context)
{
context.HttpContext.Response.StatusCode = code;
}
}
You'd have to alter your controller method like so:
[HttpPost]
public ActionResult DoSomething(string param)
{
// do some operation with param
// now I wish to return a 204 no content response to the user
// instead of the 200 OK response
return new HttpStatusCodeResult(HttpStatusCode.NoContent);
}
You can simply return a IHttpActionResult and use StatusCode:
public IHttpActionResult DoSomething()
{
//do something
return StatusCode(System.Net.HttpStatusCode.NoContent);
}
Update as of ASP.NET Core 1.0+ (2016)
You can return a NoContent ActionResult.
[HttpPost("Update")]
public async Task<IActionResult> DoSomething(object parameters)
{
// do stuff
return NoContent();
}
FYI, i am using your approach and it is returning 204 No Content (just return a void), i think you have another problem
[HttpPost]
public void SetInterests(int userid, [FromBody] JObject bodyParams)
{
....
.....
//returning nothing
}

Categories