If one omits the Accept header in a request to an Asp.Net web API the server will return (415) Unsupported Media Type
I am looking for a way to force the API to assume a default return type (in my case, application/json) when the request does not contain an Accept value in its headers.
After a substantial amount of reading and searching, I'm not sure this is even possible?
You can force the framework to use XML formatter when HTTP Accept header is missing, by doing the following trick:
var jsonFormatter = config.Formatters.JsonFormatter;
config.Formatters.Remove(config.Formatters.JsonFormatter);
config.Formatters.Add(jsonFormatter);
This way the JSON formatter will be the second registered formatter in the list, and the XML will be the first one.
This is content negotiator resposibility to choose the right formatter to serialize the response object. But by default WebApi framework gets JsonFormatter if could not find appropriate formatter.
If there are still no matches, the content negotiator simply picks the first formatter that can serialize the type.
So it is strange behavior. Anyway you could set up custom content negotiator to choose explicit JsonFormatter if request does not have Accept header.
public class JsonContentNegotiator : DefaultContentNegotiator
{
protected override MediaTypeFormatterMatch MatchAcceptHeader(IEnumerable<MediaTypeWithQualityHeaderValue> sortedAcceptValues, MediaTypeFormatter formatter)
{
var defaultMatch = base.MatchAcceptHeader(sortedAcceptValues, formatter);
if (defaultMatch == null)
{
//Check to find json formatter
var jsonMediaType = formatter.SupportedMediaTypes.FirstOrDefault(h => h.MediaType == "application/json");
if (jsonMediaType != null)
{
return new MediaTypeFormatterMatch(formatter, jsonMediaType, 1.0, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral);
}
}
return defaultMatch;
}
}
And replace in HttpConfiguration object
config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator());
Related
I am currently trying to wrap an internal API and make it external. To do so, I am trying to relay the JSON responses from the internal API and send that exact response when someone makes a get request. Instead, ASP .NET is JSONifying that son and adding extra back slashes as escape characters (when in fact those slashes are really escape slashes put in by the internal api).
How can i get it so asp does not jsonify the string
You can write your json data directly to response and set right content headers.
public HttpResponseMessage GetData()
{
var json = "\"value\": \"da\\ta\"";
var resp = Request.CreateResponse(HttpStatusCode.OK);
resp.Content = new StringContent(json);
resp.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
resp.Content.Headers.ContentEncoding = //your json encoding, you can get it from response inner API
return resp;
}
I'm using the Advanced Rest Client Chrome extension to test a request to a Web API 2 endpoint. I'm trying to include a value in the "From" header but the value is null when it is not a valid email address. By reading the spec, it looks like it only SHOULD be a valid email address, not that it MUST. Is this something that is happening because of Web API, Chrome, the extension, or something else?
After your comment about Fiddler seeing the header, I was curious so I did a little test. Here is my controller code:
public class FromController : ApiController
{
[Route("api/from")]
public dynamic Get()
{
string from1 = null;
string from2 = null;
string from3 = null;
from1 = this.Request.Headers.From;
IEnumerable<string> headers;
if (this.Request.Headers.TryGetValues("From", out headers))
{
from2 = headers.FirstOrDefault();
}
if (HttpContext.Current.Request.Headers.AllKeys.Contains("From"))
{
from3 = HttpContext.Current.Request.Headers["From"];
}
var output = new
{
From1 = from1,
From2 = from2,
From3 = from3
};
return output;
}
}
Test 1: Send e#test.com as the From header outputs:
{
"From1": "e#test.com",
"From2": "e#test.com",
"From3": "e#test.com"
}
Everything is as expected.
Test 2: Send junk as the From header outputs:
{
"From1": null,
"From2": "junk",
"From3": "junk"
}
This shows your findings of the header being null, but you can get it via the other methods.
Internally it's running some parsing on the values. The value is stored in an invalid container so asking for it directly results in null. By asking via TryGetValue, it ignores any "helpful" parsing so you'll get the value.
I added the old HttpContext.Current.Request just to see since this is more raw form, but I'd steer clear from using this in production and try to stick with this.Request for anything while in a Controller. I like to use HttpContext.Current.Request.SaveAs(fileName, true) to see what the actual raw request is. I did this first and saw the header so I knew it had to be accessible somehow.
I created a custom dispatcher to handle versioning that uses a customer media type. It looks something like this:
application/vnd.mycompany.myapi-v1+json
The extraction of the version number in order to select the correct controller is all up and working, but being new to MVC, I am not sure how to set the response format. What we want to do is set the response format to match the request. So in this example, the response will be in json. Now I assume I'm going to have to extract that from this content type as well which is fine, but could someone give me an example of how i set the response format of this request in MVC4 assuming I have already created the method which will extract the format as a string?
private string GetResponseFormat(){
//some shennanigans here
}
P.S. the reason for not having the client use the accept header during the request is that there are already clients out there that are using our old service which would set the accept header to match the request.
You can also use Content method to return custom response type:
string responseType = GetResponseFormat();
...
switch(responseType){
case "json":
string json = "yourJSON";
return Content(json, "application/json");
case "xml":
string xml = "yourXML";
return Content(xml, "text/xml");
default:
string plaintxt = "yourPlaintext";
return Content(plaintxt, "text/plain"):
}
I was able to clear the existing Accept header and add to it:
private void SetResponseFormatToRequestFormat(HttpRequestMessage request)
{
// figure out what the request format was
_contentTypeHeader = request.Content.Headers.ContentType.ToString();
if(_contentTypeHeader.Contains("xml")) _contentType = "application/xml";
if (_contentTypeHeader.Contains("json")) _contentType = "application/json";
// set response format to the same as the request format
request.Headers.Accept.Clear();
request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue(_contentType));
}
I'm using GWT RPC to communicate between client and server.
I want to be able to read the browser's date on the server side, and for that I'm using setRpcRequestBuilder from the class ServiceDefTarget to costumize my request, and add the header I want.
On the client side I'm using:
private static final RpcRequestBuilder rpcReqBuilder = new RpcRequestBuilder() {
#Override
protected RequestBuilder doCreate(String serviceEntryPoint) {
RequestBuilder builder = super.doCreate(serviceEntryPoint);
builder.setHeader("Date1", new Date().toString());
return builder;
}
};
......
((ServiceDefTarget) greetingService).setRpcRequestBuilder(rpcReqBuilder);
//rpc call
greetingService.greetServer(.........)
On the server side I do:
HttpServletRequest request = this.getThreadLocalRequest();
Enumeration<?> enumeration = request.getHeaderNames();
while (enumeration.hasMoreElements()) {
String name = (String) enumeration.nextElement();
String value = request.getHeader(name);
System.out.println(name + ": " + value);
}
which among all the default headers prints
Date1: Tue Apr 10 12:19:28 BST 2012
Ok, this works fine, but when I try to set the "Date" header, then it doesn't show up on the server side. Why is that? Anybody can help. I'll be very helpfull. :)
Date is a predefined header of HTTP, and by definition, XMLHttpRequest (the thing behind GWT's RequestBuilder) cannot let you set it to an arbitrary value.
Anyway, when crafting your own headers, you should add a prefix to avoid conflicts with other things on the network adding headers, something like MyApp-Date or X-MyApp-Date (like GWT does it with X-GWT-Permutation and X-GWT-Module-Base in GWT-RPC and RequestFactory)
i have part of Asp.NET 1.1 project.
I work with remote site, which works incorrect in some cases - sometimes it write incorrect Content-Encoding header.
In my code i get HttpResponse from this remote site. And if Content-Encoding header is equals, for example, "gzip", i need to set Content-Encoding header to "deflate".
But there is no properties or methods in HttpResponse class to get Content-Encoding header.
Content-Encoding property returns, in my case, "UTF-8". In Watch window i see _customProperties field, which contain wrong string value. How can i change header value with Asp.NET 1.1?
There is no way to change custom headers in Asp.NET 1.1.
I solve problem only using reflection.
// first of all we need get type ArrayList with custom headers:
Type responseType = Response.GetType();
ArrayList fieldCustomHeaders = ArrayList)responseType.InvokeMember("_customHeaders",BindingFlags.GetField|BindingFlags.Instance|BindingFlags.NonPublic, null, Response,null);
// next we go thru all elements of list and search our header
for(int i=0; i < fieldCustomHeaders.Count; i++)
{
// see all headers
PropertyInfo propHeaderName = fieldCustomHeaders[i].GetType().GetProperty("Name", BindingFlags.Instance|BindingFlags.NonPublic);
String headerName = (String)propHeaderName.GetValue(fieldCustomHeaders[i], null);
// if we find needed header
if(headerName == "Content-Encoding")
{
// get value of header from its field
FieldInfo fieldHeaderValue = _fieldCustomHeaders[i].GetType().GetField("_value", BindingFlags.Instance|BindingFlags.NonPublic);
String headerValue = (String)fieldHeaderValue.GetValue(fieldCustomHeaders[i]);
// if we find needed value
if (headerValue == "gzip")
{
// just set new value to it
fieldHeaderValue.SetValue(_fieldCustomHeaders[i], "deflate");
break;
}
}
}