I would like to set ServiceStack's default format to JSON, as opposed to the HTML formatted response it normally returns when a service is accessed from a browser. I know this can be specified on each request by sending a ?format=json parameter or setting the Accept header to application/json. Is there a way to change this without having to rely on these hints from the request?
In addition to specifying it on the QueryString with ?format=json, by appending the format .ext to the end of the route, e.g: /rockstars.json, or by specifying the HTTP Header (in your HttpClient): Accept: application/json.
Otherwise if your HttpClient doesn't send an Accept header you can specify JSON as the default content type in your AppHost with:
SetConfig(new HostConfig {
DefaultContentType = MimeTypes.Json
});
All Configuration options in ServiceStack are set here.
The issue when calling web services from a web browser is that they typically ask for Accept: text/html and not JSON which by contract ServiceStack obliges by returning back HTML if it is enabled.
To ensure JSON is returned you may also want to disable the HTML feature with:
SetConfig(new HostConfig {
EnableFeatures = Feature.All.Remove(Feature.Html),
});
Different ways to specify the Response Content Type
Otherwise if you want to override the Accept header you can force your service to always return json with any of these ways to Customize the HTTP Response, e.g:
Using a filter (AddHeader is built-in):
[AddHeader(ContentType=MimeTypes.Json)]
public object Any(Request request) { ... }
Setting the Response in the service:
public object Any(Request request)
{
base.Response.ContentType = MimeTypes.Json;
return dto;
}
Returning a decorated response:
return new HttpResult(dto, MimeTypes.Json);
I use the PreRequestFilter to force JSON responses to a browser. You still see the ?format=json on the querystring, but it's useful if you've disabled html & xml.
this.PreRequestFilters.Add( (req, res) =>
{
const string queryString = "format=json";
var jsonAccepted = req.AcceptTypes.Any(t => t.Equals(ContentType.Json, StringComparison.InvariantCultureIgnoreCase));
var jsonSpecifiedOnQuerystring = !string.IsNullOrEmpty(req.QueryString["format"]) && req.QueryString["format"].Equals("json", StringComparison.InvariantCultureIgnoreCase);
if (!jsonAccepted && !jsonSpecifiedOnQuerystring)
{
var sb = new StringBuilder(req.AbsoluteUri);
sb.Append(req.AbsoluteUri.Contains("?") ? "&" : "?");
sb.Append(queryString);
res.RedirectToUrl(sb.ToString(), HttpStatusCode.SeeOther);
res.Close();
}
});
Late to the question, but since I couldn't find the answer anywhere, I finally figured it out from ServiceStack's source code :)
The simplest way I found to default to Json instead of Html from the browser was this:
HttpRequestExtensions.PreferredContentTypes = new[] { MimeTypes.Json, MimeTypes.Xml };
Call this at the startup of your app, and it will override default's ServiceStack mime types and start with json (which will work with your browser's requests since / will match it).
Note that you should still disable Html and make Json the default mime type:
SetConfig(new HostConfig {
DefaultContentType = MimeTypes.Json
EnableFeatures = Feature.All.Remove(Feature.Html),
});
For the curious: ServiceStack uses internally HttpRequestExtensions.GetResponseContentType (see HttpRequestExtensions.cs), which loops through preferred content types. Because it contains MimeTypes.Html, it will catch the first accept type from the browser (text/html) and ignore whatever is coming after. By overriding this, text/html is not seen as a preferred content type, and it then skips to */* which defaults to json as expected.
Related
I have a .NET API which is as follows
public List<long> Compare ([fromBody] String NumberOfDatasets)
I am trying to access the API from an angular app. GET requests works fine, but when i try to send a POST request it goes without a body and the API receives a null parameter.
I have checked the API by accessing it from fiddler and it works fine.
Also i have enabled
CORS
HttpPost
HttpOptions
for the api.
Also the headers are getting added properly which i checked through Chrome dev tool. Am i missing anything here ?
My angular app component's TS file is as follows :
myFunction()
{
let httpHeaders = new HttpHeaders({
'Content-Type' : 'application/json'
});
let options = {
headers: httpHeaders
};
return this.httpClient.post(myUrl, "5", options);
}
Posting a string as body when using the content-type of application/json is not acceptable as the
json will try to parse from an object {} or array [].
To send a single string as the request body change the content type to application/x-www-form-urlencoded and adding a equal sign (=) infront of the string
Try it this way
myFunction()
{
let httpHeaders = new HttpHeaders({
'Content-Type' : 'application/x-www-form-urlencoded'
});
let options = {
headers: httpHeaders
};
return this.httpClient.post(myUrl, "=5", options);
}
"5" is not a valid JSON, thus MVC cannot deserialize it and returns null.
Correct way of passing string as JSON is to encapsulate it in quotation marks:
"\"5\"" //that's the way you should write it in JS
or use
JSON.stringify("5")
which will give you correct JSON string of any object you'd like to.
Moreover, passing { NumberOfDatasets: "5" } doesn't work, because proper deserialized object for it would have to look like this:
public class DatasetsNumber
{
public string NumberOfDatasets { get; set; }
}
which clearly isn't string on its own.
When you return a response form an asp .net core controller you can return data in two ways (there may be more but I am just focusing on these two). My question is what is the difference between the two methods (if any); return a value vs writing directly to the body?
[HttpGet("Fetch_Write")]
public void Fetch_Write()
{
HttpContext.Response.ContentType = "application/json";
var s = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new { data = "my_fetched_data" }));
HttpContext.Response.Body.Write(s, 0, s.Length);
}
In the method above the return type of the function is void and I am writing a content directly to the response body, but the version below my function returns a string. When using postman I get the same response from both api calls, is there a difference between the two? Should I use one over the other?
[HttpGet("Fetch_Return")]
public string Fetch_Return()
{
HttpContext.Response.ContentType = "application/json";
return JsonConvert.SerializeObject(new { data = "my_fetched_data" });
}
My guess is is that the function that returns a string does something similar later down the line where it writes the content to the body as I have done in the first code snippet function but I am not sure.
There isn't much difference. But in practice you should avoid both as it's boiler plate and doesn't fully utilize ASP.NET Core MVC tooling.
It's best to use IActionResult instead as return type and use either the helper methods (Ok, BadRequest, NotFound, File etc.) or directly create the OkObjectResult/OkResult classes and return them. This allows you to set status codes and let ASP.NET Core choose the correct formatter (XML or json, later on maybe even OData, protobuf or even custom formatters) which depend on accepted header of the caller.
For example:
[HttpGet("Fetch_Return")]
[Produces("application/json"),Produces("application/xml")]
public string Fetch_Return()
{
return Ok(new { data = "my_fetched_data" });
}
[Produces("application/json"),Produces("application/xml")] will only allow XML and json formatting. So if a user calls this action with Accept: application/xml he will receive an xml file and if he calls with Accept: application/json. If you request application/text, the browser will return Http Code 415 "Unsupported Media Type".
We have found that using an attribute on the controller we can have the controllers default json formatter replaced :-
public class WcsControllerConfigAttribute : Attribute, IControllerConfiguration
{
public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
{
var mediaType = ConfigurationManager.AppSettings["WcsMediaType"];
if (mediaType == "application/json")
{
var formatter = controllerSettings.Formatters.OfType<JsonMediaTypeFormatter>().Single();
controllerSettings.Formatters.Remove(formatter);
formatter = new JsonMediaTypeFormatter
{
SerializerSettings =
{
ContractResolver = new NullableValueContractResolver(),
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Populate
}
};
controllerSettings.Formatters.Add(formatter);
}
}
}
But this seems to have the effect of replacing the formatter not only for the result we wish to return but also for the json body of the incoming request. Is there a way to replace only the formatter for the response and not the request?
Edit: OK In response the doctors remark I think I should possibly state what my ultimate goal is because then perhaps you can offer an even better solution than using the formatter.
We are implementing a RESTful server with a partner. Their system (based on websphere) sends us perfectly standard JSON except that everything is enclosed in quotes. The standard formatter/parser does not miss a beat and happily converts all the incoming quoted values to dates, decimals, booleans, etc. We are not looking to change this behaviour.
The responses are also standard JSON but they also want all the values to be quoted. So in our data transfer object everything that is not either a child object or an array is a string and gets serialised correctly most of the time. But a further limitation is that all the properties that are null have to be treated differently to how most systems would treat them. Namely:
Null strings are passed as empty string. "property":"" and not "property":null
Null arrays are passed as empty arrays. "array":[] and not "array":null
Null child objects are passed as an empty object. "object":{} and not "object":null
And we are not allowed to simply omit them either. Not that that would be easy I suspect.
I hope that makes the requirement a little clearer. Our solution to use the formatter was based on another question Json Convert empty string instead of null and this works well for what it's worth but stumbles only because it tries to apply the rules in both directions.
You can write a custom MediaTypeFormatter to achieve what you need.
An official tutorial on how to write custom media-type formatters is available in this link http://www.asp.net/web-api/overview/formats-and-model-binding/media-formatters
Doing so will allow you to perform an http request using JSON and return a different media-type than JSON.
Keep in mind that when doing so the client needs to send the desired media-type on the request (in the accept header).
ICredentials credentials = CredentialCache.DefaultCredentials;
NetworkCredential credential = credentials.GetCredential(uri, "Basic");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Credentials = credential;
request.Method = "GET";
request.Headers.Add("api-version", "1.0");
request.Headers.Add("customer-code", code);
request.Headers.Add("Authorization", AuthUser);
request.ContentType = "application/json"; //The request is sent in JSON
request.Accept = "text/plain"; //You ask the server to return the response in the format you need it
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
Stream receiveStream = response.GetResponseStream();
YES - You can..
You can define 2 different implementation of MediaTypeFormatter. Set the properties CanReadType() & CanWriteType() as per your choice. Then implement WriteToStream() & ReadFromStream() in both formatters. Finally register both of them for your specific route.
After reading this blog post on how to return HTML from Web API 2 using IHttpActionResult, I wanted to somehow "wire-up" this IHttpActionResult to my ApiController based on the Accept header that is sent with request.
Given controller actions that have signature similar to this:
public MyObject Get(int id)
{
return new MyObject();
}
If the request specifies the Accept: text/html, this IHttpActionResult should be used to return HTML. Is that possible? In addition, some insight on how this content negotiation pipeline works for json or xml (that have built-in support) would be greatly appreciated.
If we keep the discussion of IHttpActionResult aside for a momment, Content-negotiation process in Web API is driven through formatters. So you would need to create a new formatter for handling the media type text/html.
Web API exposes the default algorithm it uses for content-negotiation called DefaultContentNegotiator which is an implementation of the service IContentNegotiator.
Now this negotiation algorithm can be run either by Web API automatically for you like in the following cases:
Usage # 1:
public MyObject Get(int id)
{
return new MyObject();
}
OR
you can manually run the negotiation yourself like in the following:
Usage #2 :
public HttpResponseMessage Get()
{
HttpResponseMessage response = new HttpResponseMessage();
IContentNegotiator defaultNegotiator = this.Configuration.Services.GetContentNegotiator();
ContentNegotiationResult negotationResult = defaultNegotiator.Negotiate(typeof(string), this.Request, this.Configuration.Formatters);
response.Content = new ObjectContent<string>("Hello", negotationResult.Formatter, negotationResult.MediaType);
return response;
}
Regarding IHttpActionResults:
In the following scenario, Ok<> is a shortcut method for generating an instance of type
OkNegotiatedContentResult<>.
public IHttpActionResult Get()
{
return Ok<string>("Hello");
}
The thing is this OkNegotiatedContentResult<> type does similar thing as in Usage # 2 scenario above. i.e they run the negotiator internally.
So to conclude, if you plan to support text/html media type then you need to write a custom formatter and add it to Web API's formatter collection and then when you use Ok<string>("Hello") with an Accept header of text/html, you should see the response in text/html. Hope this helps.
I have a WebService that is being called from an Iphone app (that I am also building)
In my webservice is it being self hosted inside a Service and it is all working well, except I would like to move a security token into the Headers of the Request so that the class objects remain neat. (If I can't get it in the header, i'll resort to putting it in the class but that's a bit ugly imo).
I have looked at the code in this http://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontext.incomingmessageheaders.aspx#Y342 and I can't seem to enumerate the header value.
Looking in Fiddler, I can see the header is being passed through
POST http://192.168.1.221:11001/StockControl/json/SubmitResults HTTP/1.1
Device-Token: bwI2YiAHR4q3Ba5JVj99Cw==
Content-Type: application/json
Content-Length: 1663
User-Agent: StockManage/1.0 CFNetwork/609 Darwin/12.1.0
I'm not sure if I haven't set up my SelfHosted configuration correctly or if I haven't implemented a necessary interface .
WCF IClientMessageInspector and the incoming SOAP headers but this is using SOAP and I'm using JSON.
My Endpoint is setup using the following
WebHttpBinding jsonBind = new WebHttpBinding();
ServiceEndpoint jsonServer = host.AddServiceEndpoint(typeof(POSServer.StockControl.IStockService), jsonBind, "json");
jsonServer.Behaviors.Add(new WebHttpBehavior
{
DefaultBodyStyle = System.ServiceModel.Web.WebMessageBodyStyle.Bare,
HelpEnabled = true,
DefaultOutgoingResponseFormat = WebMessageFormat.Json
});
Finally in my SubmitResults function in my Service implementation
public bool SubmitResults(Business.StockResultData theData)
{
DateTime uploadTime = DateTime.Now;
int index = OperationContext.Current.IncomingMessageHeaders.FindHeader("Device-Token", "");
this.WriteHeaders(OperationContext.Current.IncomingMessageHeaders);
this.WriteHeaders(OperationContext.Current.RequestContext.RequestMessage.Headers);
but index is always -1 (not found) and the WriteHeaders cannot see the header.
After a lot of searching I believe I found the answer here . (http://social.msdn.microsoft.com/Forums/pl-PL/wcf/thread/72ee44cc-58bb-45b2-aff7-49d9bbc8176e)
HttpRequestMessageProperty reqMsg =
OperationContext.Current.IncomingMessageProperties["httpRequest"] as
HttpRequestMessageProperty;
This works for me...where apiKey is Header name
> var headers =OperationContext.Current.IncomingMessageProperties["httpRequest"];
var apiToken = ((HttpRequestMessageProperty)headers).Headers["apiKey"];