When should we use the HttpResponseMessage object and when should we use the Request.CreateResponse(...) method?
Also, what is the difference between the HttpResponseMessage object and the Request.CreateResponse(...) method?
What is difference between HttpResponseMessage object and
Request.CreateResponse(...) method?
It is probably obvious but Request.CreateResponse is a helper method for creating HttpResponseMessage object.
When we must use HttpResponseMessage object and When we must use
Request.CreateResponse(...) method?
If you want to use the built-in content negotiation feature, use Request.CreateResponse. When you return an object, ASP.NET Web API has to serialize the object into response body. This could be generally JSON or XML (other media types are possible but you need to create the formatter). The media type chosen (JSON or XML) is based on the request content type, Accept header in the request and so on and content negotiation is the process that determines the media type to be used. By using Request.CreateResponse, you are automatically using the result of this process.
On the other hand, if you create HttpResponseMessage yourself, you have to specify a media formatter based on which the object will be serialized and by specifying the media formatter yourself, you can override the results of conneg.
EDIT
Here is an example of how to specify JSON formatter.
public HttpResponseMessage Get(int id)
{
var foo = new Foo() { Id = id };
return new HttpResponseMessage()
{
Content = new ObjectContent<Foo>(foo,
Configuration.Formatters.JsonFormatter)
};
}
With this, even if you send a request with Accept:application/xml, you will only get JSON.
Request.CreateResponse(...) is just a builder, it also returns instance of HttpResponseMessage, here is the code:
public static HttpResponseMessage CreateResponse<T>(this HttpRequestMessage request, HttpStatusCode statusCode, T value, HttpConfiguration configuration)
{
if (request == null)
throw Error.ArgumentNull("request");
configuration = configuration ?? HttpRequestMessageExtensions.GetConfiguration(request);
if (configuration == null)
throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoConfiguration, new object[0]);
IContentNegotiator contentNegotiator = ServicesExtensions.GetContentNegotiator(configuration.Services);
if (contentNegotiator == null)
{
throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoContentNegotiator, new object[1]
{
(object) typeof (IContentNegotiator).FullName
});
}
else
{
IEnumerable<MediaTypeFormatter> formatters = (IEnumerable<MediaTypeFormatter>) configuration.Formatters;
ContentNegotiationResult negotiationResult = contentNegotiator.Negotiate(typeof (T), request, formatters);
if (negotiationResult == null)
{
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.NotAcceptable,
RequestMessage = request
};
}
else
{
MediaTypeHeaderValue mediaType = negotiationResult.MediaType;
return new HttpResponseMessage()
{
Content = (HttpContent) new ObjectContent<T>(value, negotiationResult.Formatter, mediaType),
StatusCode = statusCode,
RequestMessage = request
};
}
}
Related
I'm trying to read a JSON string from Web API controller that is send through a HttpClient.PostAsync() method. But for some reason the RequestBody is always null.
My Request looks like this:
public string SendRequest(string requestUrl, StringContent content, HttpMethod httpMethod)
{
var client = new HttpClient { BaseAddress = new Uri(ServerUrl) };
var uri = new Uri(ServerUrl + requestUrl); // http://localhost/api/test
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response;
response = client.PostAsync(uri, content).Result;
if (!response.IsSuccessStatusCode)
{
throw new ApplicationException(response.ToString());
}
string stringResult = response.Content.ReadAsStringAsync().Result;
return stringResult;
}
I call this method like this
var content = new StringContent(JsonConvert.SerializeObject(testObj), Encoding.UTF8, "application/json");
string result = Request.SendRequest("/api/test", content, HttpMethod.Post);
Now currently my Web API controller method reads the send data like this:
[HttpPost]
public string PostContract()
{
string httpContent = Request.Content.ReadAsStringAsync().Result;
return httpContent;
}
This works fine. The stringResult property contains the string returned by the controller method. But I'd like to have my controller method like this:
[HttpPost]
public string PostContract([FromBody] string httpContent)
{
return httpContent;
}
The request seems to be working, getting a 200 - OK, but the stringResult from the SendRequest method is always null.
Why isn't the method where I'm using the RequestBody as parameter not working?
Since you're posting as application/json, the framework is attempting to deserialize it rather than providing the raw string. Whatever the type of testObj is in your sample, use that type for your controller action parameter and return type instead of string:
[HttpPost]
public MyTestType PostContract([FromBody] MyTestType testObj)
{
return testObj;
}
I'm trying to come up with a way to post to a Web API controller with one object but have a different, processed, object return. None of the methods I've been able to find have solved the issue.
Here's my method in my MVC project that posts to my Web API project
public dynamic PostStuff<X>(string action, X request)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var task = client.PostAsJsonAsync(new Uri("host/api/somecontroller/post, request);
task.Wait();
var response = task.Result;
return response;
}
}
This is my Web API controller code
public HttpResponseMessage Post([FromBody] FooObject foo)
{
var res = Request.CreateResponse(HttpStatusCode.OK,
new ValidationResponse<FooObject>
{
ID = new Random().Next(1000, 1000000),
Content =
new List<IContent>()
{
{
new Content()
{
Name = "TheContent",
Type = "SomeType",
Value = "This is some content for the page : " + foo.Bar
}
}
},
Product = new ProductFoo(),
Validated = true
});
return res;
}
}
When I put a break in my WebAPI controller code, the res variable is correctly created. Once the processing goes back to the PostStuff method, all I get is a StreamResponse with no trace of the ValidationResponse object created in the Web API controller. There are no errors but I can't use anything in the response beyond that the post succeeded. How can I extract the ValidationResponse from my posting method?
I have built myself this generic function as a helper for POSTing to Web API
Usage
var result = PostAsync<MyDataType, MyResultType>("http://...", MyData)
Function
public async Task<U> PostAsync<T, U>(string url, T model)
{
using (HttpClient httpClient = new HttpClient(httpHandler))
{
var result = await httpClient.PostAsJsonAsync<T>(url, model);
if (result.IsSuccessStatusCode == false)
{
string message = await result.Content.ReadAsStringAsync();
throw new Exception(message);
}
else
{
return await result.Content.ReadAsAsync<U>();
}
}
}
And HttpClientHandler configured for Windows Auth
protected HttpClientHandler httpHandler = new HttpClientHandler() { PreAuthenticate = true, UseDefaultCredentials = true };
Hello I would like to call Web Api method from C# client by my body variable in web api controller is null all the time.
How to set it correct ?
client side:
IFileService imgService = new ImageServiceBll();
var image = System.Drawing.Image.FromFile(serverFile);
var dataImage = imgService.ImageToBase64(image, System.Drawing.Imaging.ImageFormat.Png);
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://site.local/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP POST
var data = new
{
imageData = dataImage,
version = version
};
HttpResponseMessage response = await client.PostAsJsonAsync("api/contenttool/SaveImage", data);
if (response.IsSuccessStatusCode)
{
Uri gizmoUrl = response.Headers.Location;
}
}
Server Side:
public class ContentToolController : ApiController
{
public IFileService FileService { get; set; }
// POST api/contenttool
public string SaveImage([FromBody]string data)
{
JObject jObject = JObject.Parse(data);
JObject version = (JObject)jObject["version"];
return "-OK-" + version;
}
}
I think it has more to do with the fact that you technically aren't passing in a string. You are passing in a JSON serialized string representation of an anonymous type, so the deserialization process in the Web Api is working against you. By the time your request gets to the controller and that method, it isn't a string anymore. Try changing your type on the SavImage method to be dynamic. Like this:
public string SavImage([FromBody]dynamic data)
{
JObject jObject = JObject.Parse(data);
JObject version = (JObject)jObject["version"];
return "-OK-" + version;
}
Unfortunately at that point you won't be able to use intellisense to get your properties out. You will have to get the data out of the dynamic type via a dictionary.
Dictionary<string, object> obj = JsonConvert.DeserializeObject<Dictionary<string, object>>(Convert.ToString(data));
Of course your other option would be to use an actual type that is shared between the client and the server. That would make this a bit easier.
The string value passed in the body probably needs to be prefixed with the = sign.
I have created an ASP.NET web API controller that is returning a strongly typed object on an action, as follows:
// GET api/iosdevices/5
public iOSDevice Get(string id) {
return new iOSDevice();
}
I have created a BufferedMediaTypeFormatter to handle the type iOSDevice:
public class iOSDeviceXmlFormatter : BufferedMediaTypeFormatter
{
public iOSDeviceXmlFormatter() {
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
}
public override void WriteToStream(Type type, object value, Stream writeStream, HttpContent content) {
content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
iOSDevice device = (iOSDevice)value;
using (XmlWriter writer = XmlWriter.Create(writeStream)) {
writer.WriteStartElement("iOSDevice");
if (device.identifierForVendor != Guid.Empty) {
writer.WriteElementString("identifierForVendor", device.identifierForVendor.ToString());
writer.WriteElementString("userInterfaceIdiom", device.userInterfaceIdiom);
writer.WriteElementString("systemName", device.systemName);
writer.WriteElementString("systemVersion", device.systemVersion);
writer.WriteElementString("model", device.model);
}
writer.WriteEndElement();
}
writeStream.Close();
}
}
My problem is when I catch type "text/html" (e.g. someone attempts to access the API from his or her web browser), the response type is "text/html" instead of "application/xml". I want to override the response type so that the user gets a response that is "application/xml" instead of "text/html".
I cannot in the ApiController type get access to the "Response" property that is on regular MVC controllers and I am at a loss. How do I override the response type for this action that is being handled by a media type formatter?
EDIT: HELPFUL NOTE
I was trying this previously:
var response = Request.CreateResponse<iOSDevice>(HttpStatusCode.Accepted, device);
response.Headers.Remove("Content-Type");
response.Headers.Add("Content-Type", "application/xml; charset=utf-8");
return response;
And it claimed I was "misusing" the headers.
But when I used Filip's example below of setting Content directly, it worked!
var response = Request.CreateResponse();
response.Content = new ObjectContent<iOSDevice>(device, new iOSDeviceXmlFormatter());
return response;
When you write to stream in the formatter, headers have been already sent.
You can do this:
public HttpResponseMessage Get(string id) {
{
var value = new iOSDevice();
var response = Request.CreateResponse();
response.Content = new ObjectContent(typeof(iOSDevice), value, new iOSDeviceXmlFormatter());
//set headers on the "response"
return response;
}
or you can do this (add this method to your formatter):
public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, string mediaType)
{
base.SetDefaultContentHeaders(type, headers, mediaType);
headers.ContentType = new MediaTypeHeaderValue("application/xml");
}
Here is an example on how I used the SetDefaultContentHeaders with a custom formatter:
http://www.strathweb.com/2012/09/generate-kindle-mobi-ebooks-with-your-asp-net-web-api/
public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType)
{
if (CanWriteType(type) && mediaType.MediaType == supportedMediaType)
{
headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
headers.ContentDisposition.FileName = "ebook.mobi";
}
else
{
base.SetDefaultContentHeaders(type, headers, mediaType);
}
}
We have a REST Service created in Mvc4
I am trying to add ETag Header in the Response from my WebApi method. It is added in the Header collection without any error but when I check the response header in the Fiddler it is not there.
Here is the method that I used to write header in the response:
internal static HttpResponseMessage<T> GetResponse<T>(Tuple<T, Dictionary<string, string>> response)
{
HttpResponseMessage<T> httpResponse = new HttpResponseMessage<T>(response.Item1, HttpStatusCode.OK);
if (response.Item2 != null)
{
foreach (var responseHeader in response.Item2)
{
if (string.Compare(responseHeader.Key, "ETAG", StringComparison.OrdinalIgnoreCase) == 0)
{
httpResponse.Headers.ETag = new System.Net.Http.Headers.EntityTagHeaderValue("\"" + responseHeader.Value + "\"");
}
else
{
httpResponse.Headers.Add(responseHeader.Key, responseHeader.Value);
}
}
}
return httpResponse;
}
You can do it 2 ways, you can either set the ETag in an ActionFilter.OnActionExecuted method like this:
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) {
actionExecutedContext.ActionContext.Response.Headers.ETag = new EntityTagHeaderValue(...);
}
But there's no way to easily pass the desired value from your controller to the ActionFilter. The second way is to change your WebAPI Action. Instead of returning a model type, return an HttpResponseMessage:
[HttpGet]
public HttpResponseMessage MyActionMethod() {
var result = // response data
var response = Request.CreateResponse<MyType>(HttpStatusCode.OK, result);
response.Headers.Add("Last Modified", result.Modified.ToString("R"));
response.Headers.ETag = new System.Net.Http.Headers.EntityTagHeaderValue(CreateEtag(result));
return response;
}