Is it possible to return { } instead of null when webApi returns a null object?
This, to prevent my user from getting errors while parsing the response. And to make the response a valid Json Response?
I know that i could be setting it everywhere manually. That when null is the response, an empty Json object should be returned. But, is there a way to do it automaticly for every response?
If you are building a RESTful service, and have nothing to return from the resource, I believe that it would be more correct to return 404 (Not Found) than a 200 (OK) response with an empty body.
You can use a HttpMessageHandler to perform behaviour on all requests. The example below is one way to do it. Be warned though, I whipped this up very quickly and it probably has a bunch of edge case bugs, but it should give you the idea of how it can be done.
public class NullJsonHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
if (response.Content == null)
{
response.Content = new StringContent("{}");
} else if (response.Content is ObjectContent)
{
var objectContent = (ObjectContent) response.Content;
if (objectContent.Value == null)
{
response.Content = new StringContent("{}");
}
}
return response;
}
}
You can enable this handler by doing,
config.MessageHandlers.Add(new NullJsonHandler());
Thanks to Darrel Miller, I for now use this solution.
WebApi messes with StringContent "{}" again in some environment, so serialize through HttpContent.
/// <summary>
/// Sends HTTP content as JSON
/// </summary>
/// <remarks>Thanks to Darrel Miller</remarks>
/// <seealso cref="http://www.bizcoder.com/returning-raw-json-content-from-asp-net-web-api"/>
public class JsonContent : HttpContent
{
private readonly JToken jToken;
public JsonContent(String json) { jToken = JObject.Parse(json); }
public JsonContent(JToken value)
{
jToken = value;
Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
var jw = new JsonTextWriter(new StreamWriter(stream))
{
Formatting = Formatting.Indented
};
jToken.WriteTo(jw);
jw.Flush();
return Task.FromResult<object>(null);
}
protected override bool TryComputeLength(out long length)
{
length = -1;
return false;
}
}
Derived from OkResult to take advantage Ok() in ApiController
public class OkJsonPatchResult : OkResult
{
readonly MediaTypeWithQualityHeaderValue acceptJson = new MediaTypeWithQualityHeaderValue("application/json");
public OkJsonPatchResult(HttpRequestMessage request) : base(request) { }
public OkJsonPatchResult(ApiController controller) : base(controller) { }
public override Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var accept = Request.Headers.Accept;
var jsonFormat = accept.Any(h => h.Equals(acceptJson));
if (jsonFormat)
{
return Task.FromResult(ExecuteResult());
}
else
{
return base.ExecuteAsync(cancellationToken);
}
}
public HttpResponseMessage ExecuteResult()
{
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new JsonContent("{}"),
RequestMessage = Request
};
}
}
Override Ok() in ApiController
public class BaseApiController : ApiController
{
protected override OkResult Ok()
{
return new OkJsonPatchResult(this);
}
}
Maybe better solution is using Custom Message Handler.
A delegating handler can also skip the inner handler and directly
create the response.
Custom Message Handler:
public class NullJsonHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var updatedResponse = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = null
};
var response = await base.SendAsync(request, cancellationToken);
if (response.Content == null)
{
response.Content = new StringContent("{}");
}
else if (response.Content is ObjectContent)
{
var contents = await response.Content.ReadAsStringAsync();
if (contents.Contains("null"))
{
contents = contents.Replace("null", "{}");
}
updatedResponse.Content = new StringContent(contents,Encoding.UTF8,"application/json");
}
var tsc = new TaskCompletionSource<HttpResponseMessage>();
tsc.SetResult(updatedResponse);
return await tsc.Task;
}
}
Register the Handler:
In Global.asax file inside Application_Start() method register your Handler by adding below code.
GlobalConfiguration.Configuration.MessageHandlers.Add(new NullJsonHandler());
Now all the Asp.NET Web API Response which contains null will be replaced with empty Json body {}.
References:
- https://stackoverflow.com/a/22764608/2218697
- https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/http-message-handlers
Related
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 you read the content of an HttpResponse object in C# / ASP.net?
I need to be able to read the body content as a JSON object, modify it, and then write it back to the response output stream. I want to make sure I don't lose what's already in the stream, thus I need to read from it first.
How do I do this?
You can use a Delegating handler
public class ContentHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>((responseToCompleteTask) =>
{
HttpResponseMessage response = responseToCompleteTask.Result;
var YourContent = response.Content.ReadAsStreamAsync().Result;
response.Content = new CompressedContent(response.Content, acceptedEncoding);
return response;
},
TaskContinuationOptions.OnlyOnRanToCompletion);
}
}
Register your handler at WebApiConfig
GlobalConfiguration.Configuration.MessageHandlers.Add(new ContentHandler());
You can edit your reponse content by extending the HttpContent Class.
For example to compress content
public class CompressedContent : HttpContent
{
private HttpContent originalContent;
protected override bool TryComputeLength(out long length)
{
length = -1;
return false;
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
Stream editedStream = null;
if (encodingType == "gzip")
{
editedStream = new GZipStream(stream, CompressionMode.Compress, leaveOpen: true);
}
else if (encodingType == "deflate")
{
editedStream = new DeflateStream(stream, CompressionMode.Compress, leaveOpen: true);
}
return originalContent.CopyToAsync(editedStream).ContinueWith(tsk =>
{
if (editedStream != null)
{
editedStream.Dispose();
}
});
}
}
I want to wrap all my http responses.
For example we have an action which returns some JSON data:
public IActionResult Get()
{
var res = new
{
MessageBody = "Test",
SomeData = 1
};
return Ok(res);
}
I want my response looks like:
{
"StatusCode":200,
"Result":
{
"MessageBody ":"Test",
"SomeData":1
}
}
If there is error then response must contain ErrorMessage field in a response.
In the mvc 5 I used the DelegationHandler, but in the asp.net core this class is not implemented. Now, we have to use middlewares.
This is code for mvc 5:
public class WrappingHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
return BuildApiResponse(request, response);
}
private static HttpResponseMessage BuildApiResponse(HttpRequestMessage request, HttpResponseMessage response)
{
object content;
string errorMessage = null;
if (response.TryGetContentValue(out content) && !response.IsSuccessStatusCode)
{
HttpError error = content as HttpError;
if (error != null)
{
content = null;
errorMessage = error.Message;
#if DEBUG
errorMessage = string.Concat(errorMessage, error.ExceptionMessage, error.StackTrace);
#endif
}
}
var newResponse = request.CreateResponse(response.StatusCode, new ApiResponse(response.StatusCode, content, errorMessage));
foreach (var header in response.Headers)
{
newResponse.Headers.Add(header.Key, header.Value);
}
return newResponse;
}
}
and, a middleware for asp.net core. There are no TryGetContentValue, HttpError and other stuff in asp.net core. So, I am trying to read response body first:
public class FormatApiResponseMiddleware
{
private readonly RequestDelegate _next;
public FormatApiResponseMiddleware(RequestDelegate next)
{
_next = next;
}
private bool IsSuccessStatusCode(int statusCode)
{
return (statusCode >= 200) && (statusCode <= 299);
}
public async Task Invoke(HttpContext context)
{
object content = null;
string errorMessage = null;
if (!IsSuccessStatusCode(context.Response.StatusCode))
{
content = null;
//how to get error
}
var body= context.Response.Body;
}
}
But, Body stream has CanRead equal false and I get error that stream cannot be read. How to properly wrap response?
I suggest using ExceptionHandlerMiddleware as a template/sample on how your middleware should be implemented.
For example, you should be aware about case, when response has already started
// We can't do anything if the response has already started, just abort.
if (context.Response.HasStarted)
{
_logger.LogWarning("The response has already started, the error handler will not be executed.");
throw;
}
or don't forget to clear current response, if you want to replace it:
context.Response.Clear();
Moreover, maybe you will find useful just to reuse it, and implement your own error handler instead of a full middleware. That way you can send a custom JSON error to the client. For that, define a class that will represent your custom error:
public class ErrorDto
{
public int Code { get; set; }
public string Message { get; set; }
// other fields
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
Then register an exception handler middleware in the Configure method. Pay attention to the order in which the middleware is registered, and make sure it’s registered before MVC for example:
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = 500; // or another Status
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 = 1, //<your custom code based on Exception Type>,
Message = ex.Message // or your custom message
// … other custom data
}.ToString(), Encoding.UTF8);
}
});
});
I'm integrating a 3rd part API using TDD, and so I am implementing a HttpClient wrapper interface that exposes the possible api calls and so on.
I want to test that the correct payload was sent in a post method, but when I try to read the string content from my injected fake HttpMessageHandler I get an ObjectDisposedException. Is there a better way to test this?
Test code:
[Fact]
public async void PostSignupRequest_RequestSent_PostedSerializedRequestAsContent()
{
var client = MakeOnboardingClient();
_fakeJsonSerializer.SerializedResult = "some json";
await client.PostSignupRequest(_someSignupRequest);
Assert.Equal("some json", await _fakeMessageHandler.Request.Content.ReadAsStringAsync());
}
My HttpMessageHandler spy/test double:
public class FakeHttpMessageHandler : HttpMessageHandler
{
public HttpRequestMessage Request;
public string ResponseContent = string.Empty;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Request = request;
return await Task.FromResult(new HttpResponseMessage
{
Content = new StringContent(ResponseContent)
});
}
}
Production code:
public async Task<SignupRequestResponse> PostSignupRequest(SignupRequest request)
{
var json = _jsonSerializer.Serialize(request);
await _httpClient.PostAsync(/* url */, new StringContent(json));
return null;
}
I've found a fix now. In my HttpMessageHandler fake I don't just save the Request now, I also explicitly save the content string (which can be extracted at that point since the HttpClient hasn't disposed the request yet). My fake now looks like this:
public class FakeHttpMessageHandler : HttpMessageHandler
{
public HttpRequestMessage Request;
public string LastRequestString = string.Empty;
public string ResponseContent = string.Empty;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request.Content != null) // needed this to prevent some NPEs in other tests, YMMV
{
LastRequestString = await request.Content.ReadAsStringAsync();
}
Request = request;
return await Task.FromResult(new HttpResponseMessage
{
Content = new StringContent(ResponseContent)
});
}
}
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;
}