Given the following:
[HttpGet]
[ActionName("GetContent")]
public HttpResponseMessage GetContent(int id)
{
Content content = _uow.Contents.GetById(id);
if (content == null)
{
var message = string.Format("Content with id = {0} not found", id);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
else
{
return Request.CreateResponse(HttpStatusCode.OK, content);
}
}
and:
[HttpGet]
[ActionName("GetContent")]
public HttpResponseMessage GetContent(int id)
{
try
{
Content content = _uow.Contents.GetById(id);
if (content == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return Request.CreateResponse<Content>(HttpStatusCode.OK, content);
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
I have seen two coding styles. One using exceptions and the other not. One using CreateResponse<> and the other CreateResponse(). Can someone tell what are the advantages / disadvantages of using these? As far as I can see the second method seems to look more complete but is it really needed to use a try / catch for something as simple as this?
The main benefit to throwing HttpResponseException is when your action method returns a model type rather than an HttpResponseMessage. For example:
public Product Get(int id)
{
Product p = _GetProduct(id);
if (p == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return p;
}
This is equivalent to the following:
public HttpResponseMessage Get(int id)
{
Product p = _GetProduct(id);
if (p == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
return Request.CreateResponse(HttpStatusCode.OK, p);
}
It's OK to choose either style.
You shouldn't catch HttpResponseExceptions, because the point is for the Web API pipeline to catch them and translate them into HTTP responses. In your second code example, the Not Found error gets caught and turned into a Bad Request error, when you really wanted the client to receive Not Found (404).
Longer answer:
CreateResponse vs CreateResponse<T> has nothing to do with using HttpResponseException.
CreateResponse returns an HTTP response with no message body:
public HttpResponseMessage Get()
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
CreateResponse<T> takes an object of type T and writes the object into the body of the HTTP response:
public HttpResponseMessage Get()
{
Product product = new Product();
// Serialize product in the response body
return Request.CreateResponse<Product>(HttpStatusCode.OK, product);
}
The next example is exactly the same but uses type inference to leave out the generic type parameter:
public HttpResponseMessage Get()
{
Product product = new Product();
// Serialize product in the response body
return Request.CreateResponse(HttpStatusCode.OK, product);
}
The CreateErrorResponse method creates an HTTP response whose response body is an HttpError object. The idea here is to use a common message format for error responses. Calling CreateErrorResponse is basically the same as this:
HttpError err = new HttpError( ... )
// Serialize err in the response.
return Request.CreateResponse(HttpStatusCode.BadRequest, err);
Related
I am consuming an API that provides this operation:
[HttpGet]
[SwaggerOperation(OperationId = nameof(GetUsers))]
[SwaggerResponse(StatusCodes.Status200OK, "Result", typeof(List<UserModel>))]
[ProducesResponseType(typeof(List<UserModel>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ErrorModel), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetUsers(string tenantId, int departmentId)
{
// Magically queries the users and returns OK or detect something is wrong and returns BadRequest
...
}
This call can return a list of UserModel if everything is fine or an ErrorModel if the request is wrong.
Using swagger and autorest, I get an autogenerated client that has this method returning an object:
public async Task<HttpOperationResponse<object>> GetUsersWithHttpMessagesAsync(string tenantId, int departmentId, string commitReference = default(string), Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
{
...
}
In that method, the algorithm checks for the status code. If the status code is 200, then it builds a list of UserModel:
// Deserialize Response
if ((int)_statusCode == 200)
{
_responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
try
{
_result.Body = SafeJsonConvert.DeserializeObject<List<UserModel>>(_responseContent, DeserializationSettings);
}
catch (JsonException ex)
{
_httpRequest.Dispose();
if (_httpResponse != null)
{
_httpResponse.Dispose();
}
throw new SerializationException("Unable to deserialize the response.", _responseContent, ex);
}
}
However, if the status code is 400, then an instance of ErrorModel is being built:
if ((int)_statusCode == 404)
{
_responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
try
{
_result.Body = SafeJsonConvert.DeserializeObject<ErrorModel>(_responseContent, DeserializationSettings);
}
catch (JsonException ex)
{
_httpRequest.Dispose();
if (_httpResponse != null)
{
_httpResponse.Dispose();
}
throw new SerializationException("Unable to deserialize the response.", _responseContent, ex);
}
}
So anytime I am using this method, I have to check for the type of the object returned to find out if I have a list of UserModel or an ErrorModel.
I have to do something like this when I call the autogenerated client:
object result = await client.GetUserAsync(tenantId, departmentId);
switch (result)
{
case List<UserModel> userModels:
// Handle the users.
case ErrorModel errorModel:
// Handle the error.
}
This pushes any type safety checks at the runtime instead of compile-time which in turn can lead to bugs.
Question
How can I handle this situation in C# without having to rely on runtime type checks?
I have a controller that is made with ASP.NET and I really want to simplify that thing with quick view:
// REST representation of Storage
// There is always at least two options to view them
// Data as is or Quick view at metrics averages
[Route("metrics")]
public class MetricsController : Controller
{
// Get raw Storage object
[HttpGet]
public IActionResult GetStorageView()
{
// TODO: do not use in production
WSManModule.HyperVMetric.test(false);
//
var response = MetricsService.Instance.GetRawMetrics();
if (response == null)
{
return NotFound();
}
if (Request.QueryString.Value == "?q=quick")
{
return Ok(new StorageQuickView(response));
}
return Ok(response);
}
// Get metrics for specific device
[HttpGet("{deviceName}")]
public IActionResult GetDeviceView(string deviceName)
{
var response = MetricsService.Instance.GetDeviceMetrics(deviceName);
if (response == null)
{
return NotFound();
}
if (Request.QueryString.Value == "?q=quick")
{
return Ok(new DeviceQuickView(response));
}
return Ok(response);
}
// Get metrics for specific component within the device
[HttpGet("{deviceName}/{componentName}")]
public IActionResult GetComponentView(string deviceName, string componentName)
{
var response = MetricsService.Instance.GetComponentMetrics(deviceName, componentName);
if (response == null)
{
return NotFound();
}
if (Request.QueryString.Value == "?q=quick")
{
return Ok(new ComponentQuickView(response));
}
return Ok(response);
}
}
now it does have a lot of repetition and I don't like it.
Is there any way to do it right with optional parameters like {quick?} or something similar?
Simply: I want to perform different operations if we have /quick at the end of the route or no.
Just accept the q parameter with your actions:
// Get raw Storage object
[HttpGet]
public IActionResult GetStorageView(string q)
{
// TODO: do not use in production
WSManModule.HyperVMetric.test(false);
//
var response = MetricsService.Instance.GetRawMetrics();
if (response == null)
{
return NotFound();
}
if (q == "quick")
{
return Ok(new StorageQuickView(response));
}
return Ok(response);
}
// Get metrics for specific device
[HttpGet("{deviceName}")]
public IActionResult GetDeviceView(string deviceName, string q)
{
var response = MetricsService.Instance.GetDeviceMetrics(deviceName);
if (response == null)
{
return NotFound();
}
if (q == "quick")
{
return Ok(new DeviceQuickView(response));
}
return Ok(response);
}
The action method parameters are not just derived from routes. The values come from Value Providers, and one of the default providers parses the query string. So, you only need to add the query string value to your action method parameters rather than parsing or comparing the query string manually.
you can create a private method like this:
private IAction ProcessResponse<T>(IMyResponseType response)
{
if(response == null)
{
return NotFound();
}
if (Request.QueryString.Value == "?q=quick")
{
var okInstance = (T) Activator.CreateInstance(typeof (T), response);
return Ok(okInstance);
}
return Ok(response);
}
and use it like this:
// Get metrics for specific component within the device
[HttpGet("{deviceName}/{componentName}")]
public IActionResult GetComponentView(string deviceName, string componentName)
{
var response = MetricsService.Instance.GetComponentMetrics(deviceName, componentName);
return ProcessResponse<ComponentQuickView>(response);
}
// Get raw Storage object
[HttpGet]
public IActionResult GetStorageView()
{
// TODO: do not use in production
WSManModule.HyperVMetric.test(false);
//
var response = MetricsService.Instance.GetRawMetrics();
return ProcessResponse<StorageQuickView>(response);
}
I'm trying to figure out a way to return an object in my response while still maintaining an understandable return type.
So for starters, I know this works as expected.
public async Task<HttpResponseMessage> DoMyThing(MyObject myObject)
{
var result = await _myService.CreateMyThingAsync(myObject);
return Request.CreateResponse(HttpStatusCode.Created, result);
}
But what I really want is for this pseudo code to work... somehow.
public Task<MyObject> DoMyThing(MyObject myObject)
{
var result = _myService.CreateMyThingAsync(myObject);
return Request.CreateResponse<Task<MyObject>>(HttpStatusCode.Created, result);
// or better yet
return Request.CreateResponse<MyObject>(HttpStatusCode.Created, result);
}
Is there any magic in the framework that'll make this happen? Or are there any third party libraries that can do this?
Essentially I need to return the Task<MyObject> instead of the Task<HttpResponseMessage>
I'm also open to other suggestions on how to return a non 200 response while still returning the Task<MyObject>
The issue with specifying the type as the return type is that you restrict yourself to having to return that type. That may sound strange but actually there will be many cases where you need to be able to support multiple response, such as 404, 200 201 and so on.
To handle the documentation of this you can use the ResponseType attribute, like so:
[ResponseType(typeof(BookDto))]
public async Task<IHttpActionResult> GetBook(int id)
{
BookDto book = await db.Books.Include(b => b.Author)
.Where(b => b.BookId == id)
.Select(AsBookDto)
.FirstOrDefaultAsync();
if (book == null)
{
return NotFound();
}
return Ok(book);
}
Take a look here
Edit:
In Asp.Net Core you use the ProducesResponseType attribute which can be used multiple times per method
See here
Example
[ProducesResponseType(typeof(BookDto), 200)]
[ProducesResponseType(typeof(object), 201)]
public async Task<IActionResult> GetBook(int id)
{
BookDto book = await db.Books.Include(b => b.Author)
.Where(b => b.BookId == id)
.Select(AsBookDto)
.FirstOrDefaultAsync();
if (book == null)
{
return NotFound();
}
return Ok(book);
}
EDIT: Multiple response attributes prior to dot net core
You can use Swagger to help document / describe your API, they have a custom attribute called SwaggerResponse
The .Net port of Swagger is Swashbuckle, take a look here
This would be the best pattern in WebApi.
public async Task<IHttpActionResult> DoMyThing(MyObject myObject)
{
try
{
var result = await _myService.CreateMyThingAsync(myObject);
return new JsonStreamHttpActionResult<MyObject>(Request, System.Net.HttpStatusCode.Created, result);
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
return new HttpActionResult(HttpStatusCode.InternalServerError, "An error has occured");
}
}
with a generic serializer. You can then use the "better" IHttpActionResult instead of a real return value.
public class JsonStreamHttpActionResult<T> : IHttpActionResult
{
private T responseData;
private HttpRequestMessage request;
private HttpStatusCode statusCode;
public JsonStreamHttpActionResult(HttpRequestMessage request, System.Net.HttpStatusCode code, T responseData)
{
this.responseData = responseData;
this.request = request;
this.statusCode = code;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = request.CreateResponse(statusCode);
response.Content =
new PushStreamContent((stream, content, context) =>
{
var serializer = new Newtonsoft.Json.JsonSerializer();
using (var writer = new System.IO.StreamWriter(stream))
{
serializer.Serialize(writer, responseData);
stream.Flush();
}
});
return Task.FromResult(response);
}
}
I have an ASP.NET Core 1.0 Web API application and trying to figure out how to pass the exception message to the client if a function that my controller is calling errors out.
I have tried so many things, but nothing implements IActionResult.
I don't understand why this isn't a common thing that people need. If there truthfully is no solution can someone tell me why?
I do see some documentation out there using HttpResponseException(HttpResponseMessage), but in order to use this, I have to install the compat shim. Is there a new way of doing these things in Core 1.0?
Here is something I have been trying with the shim but it isn't working:
// GET: api/customers/{id}
[HttpGet("{id}", Name = "GetCustomer")]
public IActionResult GetById(int id)
{
Customer c = _customersService.GetCustomerById(id);
if (c == null)
{
var response = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent("Customer doesn't exist", System.Text.Encoding.UTF8, "text/plain"),
StatusCode = HttpStatusCode.NotFound
};
throw new HttpResponseException(response);
//return NotFound();
}
return new ObjectResult(c);
}
When the HttpResponseException is thrown, I look on the client and can't find the message I am sending anything in the content.
Here is an simple error DTO class
public class ErrorDto
{
public int Code {get;set;}
public string Message { get; set; }
// other fields
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
And then using the ExceptionHandler middleware:
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = 500; // or another Status accordingly to Exception Type
context.Response.ContentType = "application/json";
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
var ex = error.Error;
await context.Response.WriteAsync(new ErrorDto()
{
Code = <your custom code based on Exception Type>,
Message = ex.Message // or your custom message
// other custom data
}.ToString(), Encoding.UTF8);
}
});
});
Yes it is possible to change the status code to whatever you need:
In your CustomExceptionFilterAttribute.cs file modify the code as follows:
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var exception = context.Exception;
context.Result = new ContentResult
{
Content = $"Error: {exception.Message}",
ContentType = "text/plain",
// change to whatever status code you want to send out
StatusCode = (int?)HttpStatusCode.BadRequest
};
}
}
That's pretty much it.
If you have custom exceptions, then you can also check for them when grabbing the thrown exception from the context. Following on from that you can then send out different HTTP Status Codes depdending on what has happened in your code.
Hope that helps.
You can create a custom Exception Filter like below
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var exception = context.Exception;
context.Result = new JsonResult(exception.Message);
}
}
Then apply the above attribute to your controller.
[Route("api/[controller]")]
[CustomExceptionFilter]
public class ValuesController : Controller
{
// GET: api/values
[HttpGet]
public IEnumerable<string> Get()
{
throw new Exception("Suckers");
return new string[] { "value1", "value2" };
}
}
Rather than raising and catching an exception, how about you simplify your action to:
// GET: api/customers/{id}
[HttpGet("{id}", Name = "GetCustomer")]
public IActionResult GetById(int id)
{
var customer = _customersService.GetCustomerById(id);
if (customer == null)
{
return NotFound("Customer doesn't exist");
}
return Ok(customer);
}
I wrote a blog post with some more options such as returning a JSON object instead of text.
Maybe that is helpful. You can return just object and sent for example a BadRequest (HTTP CODE: 400) with your custom object as actual parameter (I just used an interpolated string here) but you can put in anything.
In your client side you can catch that error situation for example with an AJAX error handler.
// GET: api/TruckFahrerGeoData
[HttpGet]
public object GetTruckFahrerGeoData()
{
var truckFahrerGeoDataItems = new List<TruckFahrerGeoDataViewModel>();
var geodataItems = _context.TruckFahrerGeoData;
foreach (var truckFahrerGeoData in geodataItems)
{
GeoTelemetryData geoTelemetryData = JsonConvert.DeserializeObject<GeoTelemetryData>(truckFahrerGeoData.TelemetryData);
if (geoTelemetryData == null)
{
return BadRequest($"geoTelemetryData null for id: {truckFahrerGeoData.Id}");
}
TruckFahrerGeoDataViewModel truckFahrerGeoDataViewModel = new TruckFahrerGeoDataViewModel
{
Speed = geoTelemetryData.Speed,
Accuracy = geoTelemetryData.Accuracy,
TruckAppId = geoTelemetryData.Activity.TruckAppId,
TruckAuftragStatusId = geoTelemetryData.Activity.TruckAuftragStatusId,
ClId = geoTelemetryData.Activity.ClId,
TruckAuftragLaufStatusId = geoTelemetryData.Activity.TruckAuftragLaufStatusId,
TaskId = geoTelemetryData.Activity.TaskId,
TruckAuftragWorkflowStatusId = geoTelemetryData.Activity.TruckAuftragWorkflowStatusId
};
truckFahrerGeoDataItems.Add(truckFahrerGeoDataViewModel);
}
return truckFahrerGeoDataItems;
}
Or an even more cleaner way with IActionResult like that way:
// GET: api/TruckFahrerGeoData
[HttpGet]
public IActionResult GetTruckFahrerGeoData()
{
var truckFahrerGeoDataItems = new List<TruckFahrerGeoDataViewModel>();
var geodataItems = _context.TruckFahrerGeoData;
foreach (var truckFahrerGeoData in geodataItems)
{
GeoTelemetryData geoTelemetryData = JsonConvert.DeserializeObject<GeoTelemetryData>(truckFahrerGeoData.TelemetryData);
if (geoTelemetryData == null)
{
return BadRequest($"geoTelemetryData null for id: {truckFahrerGeoData.Id}");
}
TruckFahrerGeoDataViewModel truckFahrerGeoDataViewModel = new TruckFahrerGeoDataViewModel
{
Speed = geoTelemetryData.Speed,
Accuracy = geoTelemetryData.Accuracy,
TruckAppId = geoTelemetryData.Activity.TruckAppId,
TruckAuftragStatusId = geoTelemetryData.Activity.TruckAuftragStatusId,
ClId = geoTelemetryData.Activity.ClId,
TruckAuftragLaufStatusId = geoTelemetryData.Activity.TruckAuftragLaufStatusId,
TaskId = geoTelemetryData.Activity.TaskId,
TruckAuftragWorkflowStatusId = geoTelemetryData.Activity.TruckAuftragWorkflowStatusId
};
truckFahrerGeoDataItems.Add(truckFahrerGeoDataViewModel);
}
return Ok(truckFahrerGeoDataItems);
}
Late to the party but refining the answer .
Define your error response class with minimum below attributes
using Microsoft.AspNetCore.Http;
public class ErrorResponse
{
private readonly RequestDelegate next;
public ErrorResponse(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context )
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception ex)
{
var code = HttpStatusCode.InternalServerError;
string result = string.Empty;
object data = new object();
if (ex is ForbiddenException)
{
code = HttpStatusCode.Forbidden;
result = JsonConvert.SerializeObject(new Response<object>(Status.Forbidden(ex.Message), data));
}
else if(ex is BadRequestException){
code = HttpStatusCode.BadRequest;
result = JsonConvert.SerializeObject(new Response<object>(Status.BadRequest(ex.Message), data));
}
else if (ex is NotFoundException)
{
code = HttpStatusCode.NotFound;
result = JsonConvert.SerializeObject(new Response<object>(Status.NotFound(ex.Message), data));
}
else if (ex is UnauthorizedException)
{
code = HttpStatusCode.Unauthorized;
result = JsonConvert.SerializeObject(new Response<object>(Status.Unauthorized(ex.Message), data));
}
else
{
result = JsonConvert.SerializeObject(new Response<object>(Status.InternalServerError(ex.Message), data));
}
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)code;
return context.Response.WriteAsync(result);
}
}
Next use this class as middleware in startup.cs class
app.UseHttpsRedirection();
app.UseMiddleware(typeof(ErrorResponse));
Now each request and response will go through this class,if an error occurs then error code will be set to true with error code. A sample response like below
data: {}
status: {
code: 404
error: true
message: "No employee data found"
type: "Not Found"
}
I had the same problem and after some research, I found out I could use HttpClient to call my API and read the response easily. HttpClient does not throw any error when the HTTP response contains an error code, but it sets the IsSuccessStatusCode property to false.
This is my function using the HttpClient. I call this from my controller.
public static async Task<HttpResponseMessage> HttpClientPost(string header, string postdata, string url)
{
string uri = apiUrl + url;
using (var client = new HttpClient())
{
//client.BaseAddress = new Uri(uri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", header);
HttpResponseMessage response = await client.PostAsync(uri, new StringContent(postdata));
return response;
}
}
This is my controller code, where I call the function and read the response and determine whether I have an error or not and respond accordingly. Note that I am checking the IsSuccessStatusCode.
HttpResponseMessage response;
string url = $"Setup/AddDonor";
var postdata = JsonConvert.SerializeObject(donor);
response = await ApiHandler.HttpClientPost(HttpContext.Session.GetString(tokenName), postdata, url);
//var headers = response.Headers.Concat(response.Content.Headers);
var responseBody = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
tnxresult = JsonConvert.DeserializeObject<TnxResult>(AppFunctions.CleanResponse(responseBody));
return Json(new
{
ok = true,
message = tnxresult.Message,
statusCode = tnxresult.StatusCode
});
}
else
{
ApiError rs = JsonConvert.DeserializeObject<ApiError>(AppFunctions.CleanResponse(responseBody));
return Json(new
{
ok = false,
message = rs.Message,
statusCode = rs.StatusCode
});
}
My API returns error messages in JSON. If the call is successful, I am packing the response in JSON too.
The crucial line of code is this one...
var responseBody = await response.Content.ReadAsStringAsync();
It serializes the HTTP content to a string as an asynchronous operation.
After that I can convert my JSON string to an object and access the error/success message and the Status Code too.
I was wondering if it was possible to return a bad request with content from an MVC Controller? The only way I have been able to do this is to throw HttpException however here I can't set any content. Tried this approach to but for some odd reason I am always getting an OK back. Is it possible to do this?
public class SomeController : Controller
{
[HttpPost]
public async Task<HttpResponseMessage> Foo()
{
var response = new HttpResponseMessage(HttpStatusCode.BadRequest);
response.Content = new StringContent("Naughty");
return response;
}
}
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, "naughty");
Set the Http status code to bad request and use Content method to send your content along with response.
public class SomeController : Controller
{
[HttpPost]
public async Task<ActionResult> Foo()
{
Response.StatusCode = 400;
return Content("Naughty");
}
}
In addition to the #Ekk's answer, make sure to check this:
ASP.NET+Azure 400 Bad Request doesn't return JSON data
Add the following entry to your 'web.config'.
<system.webServer>
<httpErrors existingResponse="PassThrough"/>
</system.webServer>
...
Of course you can.
Take a look at my Action
// GET: Student/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
I think this is best practice
to return HttpStatusCodeResult(HttpStatusCode.BadRequest); in case user does not provided a required value
to return HttpNotFound(); in case the user provided a required value but not veiled
hope this help you
You can pass in error message to the second parameter like so:
return new HttpResponseMessage(HttpStatusCode.BadRequest, "Your message here");
The TrySkipIisCustomErrors flag can be used to turn off IIS custom error handling.
[HttpGet]
public void Foo()
{
HttpContext.Response.TrySkipIisCustomErrors = true;
HttpContext.Response.StatusCode = 400;
HttpContext.Response.Write("Naughty");
}
Answer for .Net Core: Return IActionResult as documented here. For example:
public IActionResult Get(int id = 0)
{
try
{
var obj = _myRepo.Get(id);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
return Ok(obj);
}