How to retrieve HTTP Code and Content from IActionResult? - c#

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;
}
}

Related

Is it possible to extract code into delegate?

Update:
The issue I had is model validation, but I found FluentValidation already taking care of this when the model passed to Action, so my question is not completely correct and probably misleading. I will close it (if find how to do it).
I have the following code:
[HttpPost]
public async Task<IActionResult> Create(CreateAccountViewModel model)
{
try
{
**await Mediator.Send(_mapper.Map<CreateAccountCommand>(model));
return RedirectToAction("Index");**
}
catch (ValidationException vldEx)
{
foreach (var vldError in vldEx.Errors)
{
ModelState.AddModelError(vldError.PropertyName, vldError.ErrorMessage);
}
return View(model);
}
}
The text inside the try is different for different actions of a controller, but everything else is the same. Is it possible to extract common code into function in the base class?
I tried using Func, but can't' figure out the right way to do it.
I am looking for something like this:
[HttpPost]
public async Task<IActionResult> Create(CreateAccountViewModel model)
{
FunctionWithCommonCode(x =>
{
await Mediator.Send(_mapper.Map<CreateAccountCommand>(model));
return RedirectToAction("Index");
});
}
No need for delegates or anything complicated just creates a base method that you call from each action and pass in the string you need, for example:
private async Task<IActionResult> CreateBase(CreateAccountViewModel model, string action)
{
try
{
await Mediator.Send(_mapper.Map<CreateAccountCommand>(model));
return RedirectToAction(action);
}
catch (ValidationException vldEx)
{
foreach (var vldError in vldEx.Errors)
{
ModelState.AddModelError(vldError.PropertyName, vldError.ErrorMessage);
}
return View(model);
}
}
And an action method would look like this:
[HttpPost]
public async Task<IActionResult> Create(CreateAccountViewModel model)
{
return await CreateBase(model, "Index");
}
I can't get this to compile - there are way too many dependencies on code that you did not provide. If you can provide Minimal, Complete and Verifiable Example (https://stackoverflow.com/help/minimal-reproducible-example) with enough stubbed out classes to get your example code to compile (without dependencies on code you don't provide), then I'll update my answer.
But, if I'm understanding your question, what you need to do is create a method that does everything except your **star-ed** code, and pass the star-ed code in as a parameter with the appropriate delegate type. Sorry, but because I couldn't get things to compile, I'm not sure I have the asyncs and awaits in exactly the right place. But, this should give you a head start.
So, you start with a worker function that provides all the boiler-plate:
private async Task<IActionResult> WebWorker<TModel> (TModel model, Func<TModel, Task<IActionResult>> workToDo) {
try {
return await workToDo(TModel model);
} catch (ValidationException vldEx) {
foreach (var vldError in vldEx.Errors) {
ModelState.AddModelError(vldError.PropertyName, vldError.ErrorMessage);
}
return View(model);
}
}
That function takes in your mode and a delegate of the appropriate Func<> type.
Then you call it something like this:
[HttpPost]
public async Task<IActionResult> Create(CreateAccountViewModel model) {
return await WebWorker<CreateAccountViewModel>(model,
m => {
Mediator.Send(_mapper.Map<CreateAccountCommand>(m));
return RedirectToAction("Index");
});
}
Note that the second parameter to the WebWorker function is a Lambda whose signature matches that of the Func declared as the second parameter to that function.

ASP.NET Core return JSON with status code

I'm looking for the correct way to return JSON with a HTTP status code in my .NET Core Web API controller. I use to use it like this:
public IHttpActionResult GetResourceData()
{
return this.Content(HttpStatusCode.OK, new { response = "Hello"});
}
This was in a 4.6 MVC application but now with .NET Core I don't seem to have this IHttpActionResult I have ActionResult and using like this:
public ActionResult IsAuthenticated()
{
return Ok(Json("123"));
}
But the response from the server is weird, as in the image below:
I just want the Web API controller to return JSON with a HTTP status code like I did in Web API 2.
The most basic version responding with a JsonResult is:
// GET: api/authors
[HttpGet]
public JsonResult Get()
{
return Json(_authorRepository.List());
}
However, this isn't going to help with your issue because you can't explicitly deal with your own response code.
The way to get control over the status results, is you need to return a ActionResult which is where you can then take advantage of the StatusCodeResult type.
for example:
// GET: api/authors/search?namelike=foo
[HttpGet("Search")]
public IActionResult Search(string namelike)
{
var result = _authorRepository.GetByNameSubstring(namelike);
if (!result.Any())
{
return NotFound(namelike);
}
return Ok(result);
}
Note both of these above examples came from a great guide available from Microsoft Documentation: Formatting Response Data
Extra Stuff
The issue I come across quite often is that I wanted more granular control over my WebAPI rather than just go with the defaults configuration from the "New Project" template in VS.
Let's make sure you have some of the basics down...
Step 1: Configure your Service
In order to get your ASP.NET Core WebAPI to respond with a JSON Serialized Object along full control of the status code, you should start off by making sure that you have included the AddMvc() service in your ConfigureServices method usually found in Startup.cs.
It's important to note thatAddMvc() will automatically include the Input/Output Formatter for JSON along with responding to other request types.
If your project requires full control and you want to strictly define your services, such as how your WebAPI will behave to various request types including application/json and not respond to other request types (such as a standard browser request), you can define it manually with the following code:
public void ConfigureServices(IServiceCollection services)
{
// Build a customized MVC implementation, without using the default AddMvc(), instead use AddMvcCore().
// https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc/MvcServiceCollectionExtensions.cs
services
.AddMvcCore(options =>
{
options.RequireHttpsPermanent = true; // does not affect api requests
options.RespectBrowserAcceptHeader = true; // false by default
//options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
//remove these two below, but added so you know where to place them...
options.OutputFormatters.Add(new YourCustomOutputFormatter());
options.InputFormatters.Add(new YourCustomInputFormatter());
})
//.AddApiExplorer()
//.AddAuthorization()
.AddFormatterMappings()
//.AddCacheTagHelper()
//.AddDataAnnotations()
//.AddCors()
.AddJsonFormatters(); // JSON, or you can build your own custom one (above)
}
You will notice that I have also included a way for you to add your own custom Input/Output formatters, in the event you may want to respond to another serialization format (protobuf, thrift, etc).
The chunk of code above is mostly a duplicate of the AddMvc() method. However, we are implementing each "default" service on our own by defining each and every service instead of going with the pre-shipped one with the template. I have added the repository link in the code block, or you can check out AddMvc() from the GitHub repository..
Note that there are some guides that will try to solve this by "undoing" the defaults, rather than just not implementing it in the first place... If you factor in that we're now working with Open Source, this is redundant work, bad code and frankly an old habit that will disappear soon.
Step 2: Create a Controller
I'm going to show you a really straight-forward one just to get your question sorted.
public class FooController
{
[HttpPost]
public async Task<IActionResult> Create([FromBody] Object item)
{
if (item == null) return BadRequest();
var newItem = new Object(); // create the object to return
if (newItem != null) return Ok(newItem);
else return NotFound();
}
}
Step 3: Check your Content-Type and Accept
You need to make sure that your Content-Type and Accept headers in your request are set properly. In your case (JSON), you will want to set it up to be application/json.
If you want your WebAPI to respond as JSON as default, regardless of what the request header is specifying you can do that in a couple ways.
Way 1
As shown in the article I recommended earlier (Formatting Response Data) you could force a particular format at the Controller/Action level. I personally don't like this approach... but here it is for completeness:
Forcing a Particular Format If you would like to restrict the response formats for a specific action you can, you can apply the
[Produces] filter. The [Produces] filter specifies the response
formats for a specific action (or controller). Like most Filters, this
can be applied at the action, controller, or global scope.
[Produces("application/json")]
public class AuthorsController
The [Produces] filter will force all actions within the
AuthorsController to return JSON-formatted responses, even if other
formatters were configured for the application and the client provided
an Accept header requesting a different, available format.
Way 2
My preferred method is for the WebAPI to respond to all requests with the format requested. However, in the event that it doesn't accept the requested format, then fall-back to a default (ie. JSON)
First, you'll need to register that in your options (we need to rework the default behavior, as noted earlier)
options.RespectBrowserAcceptHeader = true; // false by default
Finally, by simply re-ordering the list of the formatters that were defined in the services builder, the web host will default to the formatter you position at the top of the list (ie position 0).
More information can be found in this .NET Web Development and Tools Blog entry
You have predefined methods for most common status codes.
Ok(result) returns 200 with response
CreatedAtRoute returns 201 + new resource URL
NotFound returns 404
BadRequest returns 400 etc.
See BaseController.cs and Controller.cs for a list of all methods.
But if you really insist you can use StatusCode to set a custom code, but you really shouldn't as it makes code less readable and you'll have to repeat code to set headers (like for CreatedAtRoute).
public ActionResult IsAuthenticated()
{
return StatusCode(200, "123");
}
With ASP.NET Core 2.0, the ideal way to return object from Web API (which is unified with MVC and uses same base class Controller) is
public IActionResult Get()
{
return new OkObjectResult(new Item { Id = 123, Name = "Hero" });
}
Notice that
It returns with 200 OK status code (it's an Ok type of ObjectResult)
It does content negotiation, i.e. it'll return based on Accept header in request. If Accept: application/xml is sent in request, it'll return as XML. If nothing is sent, JSON is default.
If it needs to send with specific status code, use ObjectResult or StatusCode instead. Both does the same thing, and supports content negotiation.
return new ObjectResult(new Item { Id = 123, Name = "Hero" }) { StatusCode = 200 };
return StatusCode( 200, new Item { Id = 123, Name = "Hero" });
or even more fine grained with ObjectResult:
Microsoft.AspNetCore.Mvc.Formatters.MediaTypeCollection myContentTypes = new Microsoft.AspNetCore.Mvc.Formatters.MediaTypeCollection { System.Net.Mime.MediaTypeNames.Application.Json };
String hardCodedJson = "{\"Id\":\"123\",\"DateOfRegistration\":\"2012-10-21T00:00:00+05:30\",\"Status\":0}";
return new ObjectResult(hardCodedJson) { StatusCode = 200, ContentTypes = myContentTypes };
If you specifically want to return as JSON, there are couple of ways
//GET http://example.com/api/test/asjson
[HttpGet("AsJson")]
public JsonResult GetAsJson()
{
return Json(new Item { Id = 123, Name = "Hero" });
}
//GET http://example.com/api/test/withproduces
[HttpGet("WithProduces")]
[Produces("application/json")]
public Item GetWithProduces()
{
return new Item { Id = 123, Name = "Hero" };
}
Notice that
Both enforces JSON in two different ways.
Both ignores content negotiation.
First method enforces JSON with specific serializer Json(object).
Second method does the same by using Produces() attribute (which is a ResultFilter) with contentType = application/json
Read more about them in the official docs. Learn about filters here.
The simple model class that is used in the samples
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
The easiest way I came up with is :
var result = new Item { Id = 123, Name = "Hero" };
return new JsonResult(result)
{
StatusCode = StatusCodes.Status201Created // Status code here
};
This is my easiest solution:
public IActionResult InfoTag()
{
return Ok(new {name = "Fabio", age = 42, gender = "M"});
}
or
public IActionResult InfoTag()
{
return Json(new {name = "Fabio", age = 42, gender = "M"});
}
Awesome answers I found here and I also tried this return statement see StatusCode(whatever code you wish) and it worked!!!
return Ok(new {
Token = new JwtSecurityTokenHandler().WriteToken(token),
Expiration = token.ValidTo,
username = user.FullName,
StatusCode = StatusCode(200)
});
Instead of using 404/201 status codes using enum
public async Task<IActionResult> Login(string email, string password)
{
if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(password))
{
return StatusCode((int)HttpStatusCode.BadRequest, Json("email or password is null"));
}
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
return StatusCode((int)HttpStatusCode.BadRequest, Json("Invalid Login and/or password"));
}
var passwordSignInResult = await _signInManager.PasswordSignInAsync(user, password, isPersistent: true, lockoutOnFailure: false);
if (!passwordSignInResult.Succeeded)
{
return StatusCode((int)HttpStatusCode.BadRequest, Json("Invalid Login and/or password"));
}
return StatusCode((int)HttpStatusCode.OK, Json("Sucess !!!"));
}
Controller action return types in ASP.NET Core web API
02/03/2020
6 minutes to read
+2
By Scott Addie Link
Synchronous action
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById(int id)
{
if (!_repository.TryGetProduct(id, out var product))
{
return NotFound();
}
return product;
}
Asynchronous action
[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<Product>> CreateAsync(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return BadRequest();
}
await _repository.AddProductAsync(product);
return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}
Please refer below code, You can manage multiple status code with different type JSON
public async Task<HttpResponseMessage> GetAsync()
{
try
{
using (var entities = new DbEntities())
{
var resourceModelList = entities.Resources.Select(r=> new ResourceModel{Build Your Resource Model}).ToList();
if (resourceModelList.Count == 0)
{
return this.Request.CreateResponse<string>(HttpStatusCode.NotFound, "No resources found.");
}
return this.Request.CreateResponse<List<ResourceModel>>(HttpStatusCode.OK, resourceModelList, "application/json");
}
}
catch (Exception ex)
{
return this.Request.CreateResponse<string>(HttpStatusCode.InternalServerError, "Something went wrong.");
}
}
What I do in my Asp Net Core Api applications it is to create a class that extends from ObjectResult and provide many constructors to customize the content and the status code.
Then all my Controller actions use one of the costructors as appropiate.
You can take a look at my implementation at:
https://github.com/melardev/AspNetCoreApiPaginatedCrud
and
https://github.com/melardev/ApiAspCoreEcommerce
here is how the class looks like(go to my repo for full code):
public class StatusCodeAndDtoWrapper : ObjectResult
{
public StatusCodeAndDtoWrapper(AppResponse dto, int statusCode = 200) : base(dto)
{
StatusCode = statusCode;
}
private StatusCodeAndDtoWrapper(AppResponse dto, int statusCode, string message) : base(dto)
{
StatusCode = statusCode;
if (dto.FullMessages == null)
dto.FullMessages = new List<string>(1);
dto.FullMessages.Add(message);
}
private StatusCodeAndDtoWrapper(AppResponse dto, int statusCode, ICollection<string> messages) : base(dto)
{
StatusCode = statusCode;
dto.FullMessages = messages;
}
}
Notice the base(dto) you replace dto by your object and you should be good to go.
I got this to work. My big issue was my json was a string (in my database...and not a specific/known Type).
Ok, I finally got this to work.
////[Route("api/[controller]")]
////[ApiController]
////public class MyController: Microsoft.AspNetCore.Mvc.ControllerBase
////{
//// public IActionResult MyMethod(string myParam) {
string hardCodedJson = "{}";
int hardCodedStatusCode = 200;
Newtonsoft.Json.Linq.JObject job = Newtonsoft.Json.Linq.JObject.Parse(hardCodedJson);
/* "this" comes from your class being a subclass of Microsoft.AspNetCore.Mvc.ControllerBase */
Microsoft.AspNetCore.Mvc.ContentResult contRes = this.Content(job.ToString());
contRes.StatusCode = hardCodedStatusCode;
return contRes;
//// } ////end MyMethod
//// } ////end class
I happen to be on asp.net core 3.1
#region Assembly Microsoft.AspNetCore.Mvc.Core, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
//C:\Program Files\dotnet\packs\Microsoft.AspNetCore.App.Ref\3.1.0\ref\netcoreapp3.1\Microsoft.AspNetCore.Mvc.Core.dll
I got the hint from here :: https://www.jianshu.com/p/7b3e92c42b61
The cleanest solution I have found is to set the following in my ConfigureServices method in Startup.cs (In my case I want the TZ info stripped. I always want to see the date time as the user saw it).
services.AddControllers()
.AddNewtonsoftJson(o =>
{
o.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Unspecified;
});
The DateTimeZoneHandling options are Utc, Unspecified, Local or RoundtripKind
I would still like to find a way to be able to request this on a per-call bases.
something like
static readonly JsonMediaTypeFormatter _jsonFormatter = new JsonMediaTypeFormatter();
_jsonFormatter.SerializerSettings = new JsonSerializerSettings()
{DateTimeZoneHandling = DateTimeZoneHandling.Unspecified};
return Ok("Hello World", _jsonFormatter );
I am converting from ASP.NET and there I used the following helper method
public static ActionResult<T> Ok<T>(T result, HttpContext context)
{
var responseMessage = context.GetHttpRequestMessage().CreateResponse(HttpStatusCode.OK, result, _jsonFormatter);
return new ResponseMessageResult(responseMessage);
}

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.

Web Api 2 bad request

Im a beginner with Web api and Im trying to setup a simple owin selfhosted service that Im trying out.
I've been searching stackoverflow and other places for a while now, but I cant seem to find anything obviously wrong.
The problem I have is that I get a bad request response everytime I try to call my service.
The controller looks like this:
[RoutePrefix("api/ndtdocument")]
public class NDTDocumentsController : ApiController, INDTDocumentsController
{
[HttpGet]
public IHttpActionResult Get()
{
var document = Program.NDTServerSession.GetNextNDTDocument(DateTime.Today);
if (document == null)
return null;
return Ok(document);
}
[Route("")]
public IHttpActionResult Post([FromBody] NDTDocument ndtDocument)
{
try
{
Program.NDTServerSession.AddNDTDocument(ndtDocument);
return Ok();
}
catch(Exception ex)
{
return BadRequest(ex.Message);
}
}
}
And the client looks like this:
static void Main(string[] args)
{
AddNDTDocument(#"C:\Testing.txt");
}
private static void AddNDTDocument(string centralserverPath)
{
var client = GetServerClient();
NDTDocument document = new NDTDocument();
var response = client.PostAsJsonAsync("ndtdocument", document).Result;
}
static HttpClient GetServerClient()
{
var client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:9000/api/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
I can see when I debug it that the request uri is infact http://localhost:9000/api/ndtdocument
But the response is allways bad request and I have a breakpoint in the controller and it is never invoked.
Everytime I try to do something with web apis I Always run into some weird (but simple problem).
Any thoughts?
Thanks!
Web API will decide your route based on your method names. Since you have added [RoutePrefix("api/ndtdocument")] on class level this will be the route to your controller. When web api looks for an action it will match on method names, so in your case your actual route would be http://localhost:9000/api/ndtdocument/post.
When trying to decide what http method that a specific action requires web api will check your method names and methods starting with post will be http post, get will be http get etc.
So lets say we would instead call our method PostData, for starters we could remove the [HttpPost] attribute. Our route would now be http://localhost:9000/api/ndtdocument/postdata. Let's now say that we want our path to be just /data. We would then first rename our method to Data, but now web api does not know what http method we want to invoke this method with, thats why we add the [HttpPost] attribute.
Edit after reading your comment
[Route("{id:int}")]
public IHttpActionResult Get(int id)
[Route("")]
public IHttpActionResult Post([FromBody] NDTDocument ndtDocument)
Okey, after nearly going seriously insane. I found the problem.
I forgot to reference webapi.webhost and then system.web.
After this Everything worked like a charm.
You must use route tags and call this way http://localhost:9000/api/get or http://localhost:9000/api/post
[RoutePrefix("api/ndtdocument")]
public class NDTDocumentsController : ApiController, INDTDocumentsController
{
[HttpGet]
[Route("get")]
public IHttpActionResult Get()
{
var document = Program.NDTServerSession.GetNextNDTDocument(DateTime.Today);
if (document == null)
return null;
return Ok(document);
}
[HttpPost]
[Route("post")]
public IHttpActionResult Post([FromBody] NDTDocument ndtDocument)
{
try
{
Program.NDTServerSession.AddNDTDocument(ndtDocument);
return Ok();
}
catch(Exception ex)
{
return BadRequest(ex.Message);
}
}
}
for more infromation pls check this link

Correct return type for validation errors

I need to implement validation in my web api controller.
In my class I have a method like this:
public MyEntity Post(MyEntity entity)
{
// ...
}
In POST and PUT methods I usually return the created/updated object.
In this tutoral they are returning a HttpResponseMessage so that they can do something like this:
if (ModelState.IsValid)
{
// Do something with the product (not shown).
return new HttpResponseMessage(HttpStatusCode.OK);
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
Is there a way to use a similar approach returning the saved entity?
You should use HttpRequestMessageExtensions.CreateResponse method. For example:
if (ModelState.IsValid)
{
// Do something with the product (not shown).
return Request.CreateResponse<MyEntity>(HttpStatusCode.OK, entity);
}

Categories