Creating new IHttpActionResult action result methods - c#

Is there a way I can use the new IHttpActionResult interface to return a HttpStatusCode.NoContent response message?
I am currently using return new HttpResponseMessage( HttpStatusCode.NoContent );
and would like to convert this into return NoContent();.
IHttpActionResult has already got Ok(), Conflict() and NotFound() but I cannot find any for Forbidden() and NoContent() which I need to use in my project.
How easy is it to add other result types?

There's no convenience method for no-content result because, by default, when a action returns void, the response will have the HTTP status 204.
If you wish to explicitly indicate that on the action, you could also return a StatusCode(HttpStatusCode.NoContent) from your action or a
ResponseMessage(new HttpResponseMessage(HttpStatusCode.NoContent)).
The Unauthorized() convenience method gives you a 401 status so, for Forbidden (403), you would also have to use StatusCode(HttpStatusCode.Forbidden) or
ResponseMessage(new HttpResponseMessage(HttpStatusCode.Forbidden))

I found this example site that shows how to add a custom IHttpActionResult method and I've used this to create the Forbidden() and NoContent() methods with great success.
public abstract class CommonApiController : ApiController
{
public class ForbiddenResult : IHttpActionResult
{
private readonly HttpRequestMessage _request;
private readonly string _reason;
public ForbiddenResult(HttpRequestMessage request,string reason)
{
_request = request;
_reason = reason;
}
public ForbiddenResult(HttpRequestMessage request)
{
_request = request;
_reason = "Forbidden";
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = _request.CreateResponse(HttpStatusCode.Forbidden,_reason);
return Task.FromResult(response);
}
}
public class NoContentResult : IHttpActionResult
{
private readonly HttpRequestMessage _request;
private readonly string _reason;
public NoContentResult(HttpRequestMessage request,string reason)
{
_request = request;
_reason = reason;
}
public NoContentResult(HttpRequestMessage request)
{
_request = request;
_reason = "No Content";
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = _request.CreateResponse(HttpStatusCode.NoContent,_reason);
return Task.FromResult(response);
}
}
}
And then I can use it like this:
public class InvoiceController : CommonApiController
{
public async Task<IHttpActionResult> Post([FromBody]Invoice invoice)
{
if(User.IsInRole("Readonly"))
{
return Forbidden();
}
// Rest of code
}
}

I tried the #Intrepid implementation and I ran into some problems. I see two solutions here:
Solution 1:
The part: return Forbidden(); should not work.
The compiler would not recognize this.
Instead it should be: return new ForbiddenResult(Request, "my reason");
UPDATE 1
Solution 2:
I think this is what #Interpid intended in his implementation, but he was missing a few things.
In order to use return Forbidden(); the CommonApiController should be updated with the functions that return the custom IHttpActionResult for Forbidden and NoContent
The class should look like this:
public abstract class CommonApiController: ApiController {
protected ForbiddenResult Forbidden() {
return new ForbiddenResult(this.Request);
}
protected ForbiddenResult Forbidden(string reason) {
return new ForbiddenResult(this.Request, reason);
}
protected NoContentResult NoContent() {
return new NoContentResult(this.Request);
}
public class ForbiddenResult: IHttpActionResult {
private readonly HttpRequestMessage _request;
private readonly string _reason;
public ForbiddenResult(HttpRequestMessage request, string reason) {
_request = request;
_reason = reason;
}
public ForbiddenResult(HttpRequestMessage request) {
_request = request;
_reason = "Forbidden";
}
public Task < HttpResponseMessage > ExecuteAsync(CancellationToken cancellationToken) {
var response = _request.CreateResponse(HttpStatusCode.Forbidden, _reason);
return Task.FromResult(response);
}
}
public class NoContentResult: IHttpActionResult {
private readonly HttpRequestMessage _request;
private readonly string _reason;
public NoContentResult(HttpRequestMessage request, string reason) {
_request = request;
_reason = reason;
}
public NoContentResult(HttpRequestMessage request) {
_request = request;
_reason = "No Content";
}
public Task < HttpResponseMessage > ExecuteAsync(CancellationToken cancellationToken) {
var response = _request.CreateResponse(HttpStatusCode.NoContent, _reason);
return Task.FromResult(response);
}
}
}
Anyway, if I am wrong and #Interpid's answer is correct. What am I missing here to make his implementation work?

You can now use the following (.Net Standard):
return StatusCode(HttpStatusCode.NoContent);
or (.Net Core 2.1+)
return NoContent();

If you want to include a reason phrase with your response without adding a sub-class to ApiController, build a ResponseMessage object and return it from the action by the ResponseMessage() method. Try this:
public class InvoiceController : ApiController
{
public async Task<IHttpActionResult> Post([FromBody]Invoice invoice)
{
if(User.IsInRole("Readonly"))
{
var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
response.ReasonPhrase = "User has the Readonly role";
return ResponseMessage(response);
}
// Rest of code
}
}

This worked well for me:
public class CodeAndReason : IHttpActionResult
{
private readonly HttpStatusCode code;
private readonly string reason;
public CodeAndReason(HttpStatusCode code, string reason)
{
this.code = code;
this.reason = reason;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(code)
{
ReasonPhrase = reason,
Content = new StringContent(reason),
};
return Task.FromResult(response);
}
public static IHttpActionResult NotFound(string reason)
{
return new CodeAndReason(HttpStatusCode.NotFound, reason);
}
public static IHttpActionResult Conflict(string reason)
{
return new CodeAndReason(HttpStatusCode.Conflict, reason);
}
public static IHttpActionResult Unauthorized(string reason)
{
return new CodeAndReason(HttpStatusCode.Unauthorized, reason);
}
}
Used as:
return CodeAndReason.NotFound("Record {blah} not found");

Related

How can I return a JSON object and status code (IHttpActionResult) from an ExceptionHandler in Web API?

Normally, I'd just do in my controller action:
return Content(System.Net.HttpStatusCode.InternalServerError,
new MyCustomObject("An error has occurred processing your request.", // Custom object, serialised to JSON automatically by the web api service
ex.ToString()));`
However the Content method exists on the controller. The ExceptionHandler I made has this:
public override void Handle(ExceptionHandlerContext context)
{
context.Result = ???;
The type of context.Result is IHttpActionResult, so what I need to do is create one and stick it in there. I can't find any constructors or similar that will allow me to create an IHttpActionResult outside of a controller. Is there an easy way?
I thing for custom responses you should probably implement your own http action result:
public override void Handle(ExceptionHandlerContext context)
{
context.Result = new HttpContentResult(new { }, context.Request);
}
public class HttpContentResult : IHttpActionResult
{
private readonly object content;
private readonly HttpRequestMessage requestMessage;
public HttpContentResult(object content, HttpRequestMessage requestMessage)
{
this.content = content;
this.requestMessage = requestMessage;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var httpContentResponse = new HttpResponseMessage(HttpStatusCode.BadRequest);
var httpContent = new StringContent(content);
//... [customize http contetnt properties]
httpContentResponse.Content = httpContent;
httpContentResponse.RequestMessage = this.requestMessage;
//... [customize another http response properties]
return Task.FromResult(httpContentResponse);
}
}

How do I return common/generic response object for all controller methods?

Is there a way to extend IActionResult so that all controller methods return same response object (ex: CommonActionResult) with
HttpResponseCode, Message and Response object (ex: ObjectOne, ObjectTwo based on the methods initiated)?
[HttpGet]
public ActionResult<ObjectOne> Get(string id)
{
}
[HttpGet]
public ActionResult<ObjectTwo> Get(string name)
{
}
[HttpPost]
public IActionResult Post(AppPost app)
{
}
How do I extend this?
public class CommonActionResult : IActionResult
{
}
If you just want a generic return type, use IActionResult. That will accept any action result type, such as OkResult, OkObjectResult, BadRequestResult, NotFoundResult, etc.
ActionResult<T> is just a helper that wraps objects in a result, allowing you to return an object directly, instead of having to wrap it in a result first (i.e. return obj vs return Ok(obj). However, when you use that, then you must return that specific object type: the type passed into the T generic type param. If you need to return different types, then use IActionResult as the return in your method signature, and then just wrap the object in something like Ok.
public IActionResult FooOrBar()
{
...
if (something)
return Ok(foo);
else
return Ok(bar);
}
you can implement IHttpAction based on you requirements
it's a simple sample :
public class CustomResult<T> : IHttpActionResult
{
private readonly HttpRequestMessage _request;
private readonly T _content;
public CustomResult(HttpRequestMessage request, T content)
{
_request = request;
_content = content;
}
public CustomResult(HttpRequestMessage request)
{
_request = request;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
if (_content==null)
{
var response =_request.CreateResponse(HttpStatusCode.NoContent, JsonMediaTypeFormatter.DefaultMediaType);
}else
{
var jsonData = JsonConvert.SerializeObject(_content, _content.GetType(), new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Formatting = Formatting.Indented
});
var response = _request.CreateResponse(HttpStatusCode.OK, jsonData, JsonMediaTypeFormatter.DefaultMediaType);
response.Content = new StringContent(jsonData, Encoding.UTF8, "application/json");
}
return Task.FromResult(response);
}
}
also you can add extra information to response header look at this sample for more information
https://www.strathweb.com/2013/06/ihttpactionresult-new-way-of-creating-responses-in-asp-net-web-api-2/

Web Api 2 : How to Set Resource Manager Culture in Every Request?

I have a resource manager class for web api project. Like this :
public static class MyResource
{
private static global::System.Globalization.CultureInfo resourceCulture;
public static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
public static string RecordAdded { get { return Content.ResourceManager.GetString("RecordAdded", resourceCulture); } }
}
I want to set resourceCulture in every request. How can i do this with most generic way ?
public class BaseApiController : ApiController
{
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
IEnumerable<string> lang;
controllerContext.Request.Headers.TryGetValues("lang", out lang);
MyResource.Culture = new System.Globalization.CultureInfo(lang.FirstOrDefault());
base.Initialize(controllerContext);
}
}
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
}
This did not work.
By the way, I dont want to use action filters if i dont have to.
Thanks.
I think you can use delegating handler before processing of request. You can find docs here.
public class MessageHandler1 : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
IEnumerable<string> lang;
request.Headers.TryGetValues("lang", out lang);
MyResource.Culture = new System.Globalization.CultureInfo(lang.FirstOrDefault());
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}

Passing generic functions with paramaters

I think I might be retarded or I'm asking too much out of C# but I can't get this to work.
What essentially I'm trying to do is to wrap an API-client with some logging functions and a method to request a new token from the API-server.
controller:
public class TestController : ApiController
{
[HttpGet]
[Route("api/model/get/{id}"]
public IHttpActionResult GetModel<Model>(int id)
{
var result = Service.DoHttp<Model>(ServiceClass.GetModel, id);
}
}
service:
public static class ServiceClass
{
private static readonly HttpClient client = new HttpClient() { BaseAddress = new Uri(Globals.ExternalApiPath) };
private static string TokenHeader = "";
public async static Task<HttpResponseMessage> GetModel(int id)
{
var response = client.GetAsync($"/api/get/{id}");
return await response;
}
public static T DoHttp<T>(Func<int, HttpResponseMessage> funk, int id)
{
try
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", TokenHeader);
var result = funk(id);
if (result.IsSuccessStatusCode)
{
return result.Content.ReadAsAsync<T>().Result;
}
else
{
throw new Exception(String.Format($"Unknown error! Unable to contact remote API! AccessToken: {TokenHeader} Status code: {result.StatusCode}"));
}
}
catch (Exception e)
{
// log ex
throw e;
}
}
}
But my Service.DoHttp(Service.GetModel, id); complains about it being the wrong return type.
What am I doing wrong or have I misunderstood the whole concept?
EDIT: Compiler complains about 'Task ServiceClass.GetModel(int)' has the wrong return type
Change the DoHttp method to the following.
public static T DoHttp<T>(Func<int, Task<HttpResponseMessage>> funk, int id)
As the GetModel method returns a Task you need to use a task as the return type of the Func too.

WebApi 2 return types

I'm looking at the documentation of WebAPI 2, and i'm severely disappointed with the way the action results are architected. I really hope there is a better way.
So documentation says I can return these:
**void** Return empty 204 (No Content)
**HttpResponseMessage** Convert directly to an HTTP response message.
**IHttpActionResult** Call ExecuteAsync to create an HttpResponseMessage, then convert to an HTTP response message.
**Other type** Write the serialized return value into the response body; return 200 (OK).
I don't see a clean way to return an array of items with custom HTTP status code, custom headers and with auto negotiated content though.
What I would like to see is something like
public HttpResult<Item> Post()
{
var item = new Item();
var result = new HttpResult<Item>(item, HttpStatusCode.Created);
result.Headers.Add("header", "header value");
return result;
}
This way I can glance over a method and immediately see whats being returned, and modify status code and headers.
The closest thing I found is NegotiatedContentResult<T>, with weird signature (why does it need an instance of controller?), but there's no way to set custom headers?
Is there a better way ?
The following code should give you everything you want:
[ResponseType(typeof(Item))]
public IHttpActionResult Post()
{
var item = new Item();
HttpContext.Current.Response.AddHeader("Header-Name", "Header Value");
return Content(HttpStatusCode.Created, item);
}
... if you really need to return an array of items ...
[ResponseType(typeof(List<Item>))]
public IHttpActionResult Post()
{
var items = new List<Item>();
// Do something to fill items here...
HttpContext.Current.Response.AddHeader("Item-Count", items.Count.ToString());
return Content(HttpStatusCode.Created, items);
}
I don't think the designers of the web-api intended for controller methods to be fiddling with the headers.
The design pattern seems to be to use DelegatingHandler, ActionFilterAttribute and the ExecuteAsync overridable method of ApiController to handle authentication and response formatting.
So perhaps your logic for message content negotiation should be handled there ?
However if you definitely need to control headers from within your controller method you can do a little set-up to make it work.
To do so you can create your own DelegationHandler that forwards selected headers from your "Inner" response headers:
public class MessageHandlerBranding : DelegatingHandler {
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
//If we want to forward headers from inner content we can do this:
if (response.Content != null && response.Content.Headers.Any())
{
foreach (var hdr in response.Content.Headers)
{
var keyUpr = hdr.Key.ToUpper(); //Response will not tolerate setting of some header values
if ( keyUpr != "CONTENT-TYPE" && keyUpr != "CONTENT-LENGTH")
{
string val = hdr.Value.Any() ? hdr.Value.FirstOrDefault() : "";
response.Headers.Add(hdr.Key, val);
}
}
}
//Add our branding header to each response
response.Headers.Add("X-Powered-By", "My product");
return response;
}
}
Then you register this handler in your web-api configuration, this is usually in the GlobalConfig.cs file.
config.MessageHandlers.Add(new MessageHandlerBranding());
You could also write your own custom class for the response object like this:
public class ApiQueryResult<T> : IHttpActionResult where T : class
{
public ApiQueryResult(HttpRequestMessage request)
{
this.StatusCode = HttpStatusCode.OK; ;
this.HeadersToAdd = new List<MyStringPair>();
this.Request = request;
}
public HttpStatusCode StatusCode { get; set; }
private List<MyStringPair> HeadersToAdd { get; set; }
public T Content { get; set; }
private HttpRequestMessage Request { get; set; }
public void AddHeaders(string headerKey, string headerValue)
{
this.HeadersToAdd.Add(new MyStringPair(headerKey, headerValue));
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = this.Request.CreateResponse<T>(this.StatusCode, this.Content);
foreach (var hdr in this.HeadersToAdd)
{
response.Content.Headers.Add(hdr.key, hdr.value);
}
return Task.FromResult(response);
}
private class MyStringPair
{
public MyStringPair(string key, string value)
{
this.key = key;
this.value = value;
}
public string key;
public string value;
}
}
And use it like this in your controller:
[HttpGet]
public ApiQueryResult<CustomersView> CustomersViewsRow(int id)
{
var ret = new ApiQueryResult<CustomersView>(this.Request);
ret.Content = this.BLL.GetOneCustomer(id);
ret.AddHeaders("myCustomHkey","myCustomValue");
return ret;
}

Categories

Resources