Returned content not encoded automatically after changing Content-Type - cherrypy

Given a resource with multiple representations (media types) that doesn't use custom CherryPy "tools" to handle the interpretation of the "Accept" HTTP header and the serialization of the response entity body, CherryPy raises the following ValueError exception upon returning the content from the page handler after setting the "Content-Type" HTTP header appropriately, but for certain media types only:
ValueError: Page handlers MUST return bytes. Use tools.encode if you wish to return unicode.
Example:
contentType = tools.accept.callable(media = ['application/json', 'text/html'])
if contentType == 'application/json':
return json.dumps(studies)
elif contentType == 'text/html':
...
This works for both media types, although the JSON representation will be wrongly declared as HTML (the default).
contentType = tools.accept.callable(media = ['application/json', 'text/html'])
response.headers['Content-Type'] = "{mediaType}; charset=utf-8".format(mediaType = contentType)
if contentType == 'application/json':
return json.dumps(studies)
elif contentType == 'text/html':
...
Here the exception above is raised when returning the JSON content as a string.
Attempts to ensure tools.encode is indeed enabled and setting tools.encode.encoding to utf-8 explicitly (even though it is the default) fail. Things work out for the HTML representation, so it seems like it should work for the JSON representation as well.
Currently the documentation for tools.encode seems rather sparse so which is the best approach to take is not immediately obvious.

The CherryPy encode tool automatically encodes content only when the top-level media type is text (text/*).
There is a way to control this with the encode.text_only setting, but it is global and so might introduce issues when returning content that really shouldn't be encoded. As of this writing, an open issue tracks a feature request for more granular control over this behavior: #1123.
For this reason, the most appropriate way to resolve this in this particular situation is to encode the content manually:
contentType = tools.accept.callable(media = ['application/json', 'text/html'])
response.headers['Content-Type'] = "{mediaType}; charset=utf-8".format(mediaType = contentType)
if contentType == 'application/json':
return json.dumps(studies).encode('utf-8')
elif contentType == 'text/html':
...

Related

Debugging a Base64 string

How would I go about debugging what's wrong with a string that I believe is in Base64 format, but which in VB using the below line of code
Dim theImage As Drawing.Image = imageUtils.Base64ToImage(teststring)
throws the following exception?
{"Base64ToImage(): The input is not a valid Base-64 string as it contains a
non-base 64 character, more than two padding characters, or an illegal
character among the padding characters. "}
The test string itself is far too long to paste here; I tried but reached (a lot) over the character limit. I've tried a few online conversion tools and it doesn't seem to work there either. At first I thought I was passing the string wrong from my ajax call to the web method VB code behind...but I've tried hard-coding my string into the function as well with the same failure. So, I'm convinced the string itself is bad data.
It looks like:
Dim teststring = "dataImage/ png;base64,
iVBORw0KGgoAAAANSUhEUgAAB4AAAAQ4CAYAAADo08FDAAAgAElEQVR4Xuy9268sWbbe9UVE3i /
rvte + V....K/1Tx5/8A736wVclDQN4AAAAASUVORK5CYII="
But I also tried removing the "dataImage" part and using
Dim teststring =
"iVBORw0KGgoAAAANSUhEUgAAB4AAAAQ4CAYAAADo08FDAAAgAElEQVR4Xuy9268sWbbe9UVE3i /
rvte + V....K/1Tx5/8A736wVclDQN4AAAAASUVORK5CYII="
And it doesn't make a difference.
I am getting this string in javascript using this function:
btnFreeze.onclick = video.onclick = function (e) {
e.preventDefault();
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0);
alert("got here");
$hfLicenseScreenshot.val(canvas.toDataURL());
$img.css("background-image", "url(" + $hfLicenseScreenshot.val() + ")");
$("#hiddenTextbox").val($hfLicenseScreenshot.val());
//$("#save").show();
return false;
};
...where ultimately the string is from
canvas.toDataURL()
and about halfway through that function there is a hidden field called $hfLicenseScreenshot, from which I am saving the value into a "hidden" textbox (I dont know why my variable was getting lost, I know it's redundant but that's why I saved the value to a textbox called hiddentextbox. I get the sstring from hiddentextbox later, like:
$("#hiddenTextbox").val().toString();
So, I have no idea how to go about debugging this image base 64 string. I've tried different images taken from my webcam and it's just not working with any of them. Any ideas?
...I don't know if it's been serialized or not, since I think the JSON stringify method is supposed to do that. I might be confused there.
...Here is my ajax call:
$.ajax({
type: "POST",
url: "/BackCode/FirstPage.aspx/SaveData",
data: JSON.stringify({ currentData: currentData, str: makeblob(str) }),
contentType: "application/json; charset=utf-8",
dataType: "json",
async: true,
currentData: currentData,
success: function (resp) {
resp = resp.d;
if (resp.success) {
if (typeof callback === "function")
callback.apply(this, [resp]);
load();
} else {
$.statusMessage(resp.statusMessage, "red");
}
},
error: function (jsonObject, textStatus, errorThrown) {
$.statusMessage(errorThrown, "red");
}
});
I have also been having issues with this, and it goes into the last error function a lot:
$.statusMessage(errorThrown, "red");
So I don't know whether I'm passing it correctly either.
The following works for me:
Dim base64String = "Qk2uAAAAAAAAADYAAAAoAAAABgAAAAYAAAABABgAAAAAAAAAAADEDgAAxA4AAAAAAAAAAAAA////AAAAAAAAAAAAAAAA////AAAAAAD///////////////8AAAAAAP///////////////////////wAA////////////////////////AAD///8AAAD///////8AAAD///8AAP///////////////////////wAA"
Dim base64Bytes = Convert.FromBase64String(base64String)
Using memoryStream = New MemoryStream(base64Bytes)
Dim image As Image = Image.FromStream(memoryStream)
image.Save($"C:\Users\{Environment.UserName}\Desktop\Smile.bmp")
End Using
I've removed the initial metadata which indicates what type of image it is, the original string was:
data:image/bmp;base64,Qk2uAAAAAAAAADYAAAAoAAAABgAAAAYAAAABABgAAAAAAAAAAADEDgAAxA4AAAAAAAAAAAAA////AAAAAAAAAAAAAAAA////AAAAAAD///////////////8AAAAAAP///////////////////////wAA////////////////////////AAD///8AAAD///////8AAAD///8AAP///////////////////////wAA
Try removing the spaces from your base64 encoding - as they're not a valid base64 character - and the metadata from the start.
Here are the valid base64 characters taken from Wikipedia:
I tried removing whitespace like some other suggestions here, and that did not fix my problem (although I'm sure that was likely a secondary problem).
In my case, my string wasn't actually a valid image at all because I had tried to pass it from Ajax to the VB WebMethod as an object (this was because passing as a string was not working). And at one point I had tried converting it to a Blob object....so my test string was just something totally invalid. I should have just left it as a string in both front end and backend. So, I don't even know what my string one that I posted earlier, but it wasn't an image.
Eventually I realized that my string on the front-end was too long, which was my actual/original issue. Now I realize the irony of having mentioned earlier that the string was too long to post in my question. There is something in the WebConfig you can set for maxlength of an image string:
<webServices>
<jsonSerialization maxJsonLength="86753010">
</jsonSerialization>
</webServices>
So, the json serialization was failing because I had this code commented-out. I commented it back in and made the number bigger, and that fixed my issue. So then I was able to pass the string properly and everything worked!

Can Cloudant list/show functions return objects and arrays(any JSON)?

As per the API documentation for Cloudant: Show function can be used to render a document in a different format or extract only some information from a larger document. Same is the case for a list function, the only difference is that it applies on a set of documents. I created a design document with a show function as follows:
{ "shows": { "showDemo":"function(doc,req){return {'body': doc, 'headers':{'Content-Type':'application/json'}}}" } }
When I use this function, _design/showFunc/_show/showDemo/doc1, I get the following error:
{ "error": "unknown_error", "reason": "badarg", "ref": 1793182837 }
I have observed the same error when the show function returns an array. However, no error is given when HTML,Text, XML is returned. Can we say that list/show functions can only return data in a format other than JSON? This example shows the "Accept" header for req object request Object.
What's happening here is that the show function needs to return a response object. From the docs (see http://docs.couchdb.org/en/2.1.0/json-structure.html#response-object) the body field needs to be a string, so you can return whatever you like but it needs to be stringified or otherwise turned into a format that can be sent as HTTP.
If you want to send JSON then doing JSON.Stringify(doc) as the value for body should do what you expect.

Defalut XmlSiteMapProvider implementation cannot use SiteMap.FindSiteMapNode?

I just upgrade MvcSiteMapProvider from v3 to v4.6.3.
I see the upgrade note indicate:
In general, any reference to System.Web.SiteMap.Provider will need to be updated to MvcSiteMapProvider.SiteMaps.Current
I am trying to get the sitemap node by using:
SiteMaps.Current.FindSiteMapNode(rawUrl)
But it always return null
I looked into the code. In the sitemap it's actually calling the function:
protected virtual ISiteMapNode FindSiteMapNodeFromUrlMatch(IUrlKey urlToMatch)
{
if (this.urlTable.ContainsKey(urlToMatch))
{
return this.urlTable[urlToMatch];
}
return null;
}
It's trying to find a match in the urlTable.
I am using Default implementation of XmlSiteMapProvider .
It define var url = node.GetAttributeValue("url");
siteMapNode.Url = url;
siteMapNode.UrlResolver = node.GetAttributeValue("urlResolver");
So if I did not define url or urlResolver attribute in the .sitemap file. These variables a set to empty string, when generate the node.
And when this nodes are passed to AddNode function in SiteMap.
When adding the node
bool isMvcUrl = string.IsNullOrEmpty(node.UnresolvedUrl) && this.UsesDefaultUrlResolver(node);
this code will check if there is url or urlResolver
// Only store URLs if they are clickable and are configured using the Url
// property or provided by a custom URL resolver.
if (!isMvcUrl && node.Clickable)
{
url = this.siteMapChildStateFactory.CreateUrlKey(node);
// Check for duplicates (including matching or empty host names).
if (this.urlTable
.Where(k => string.Equals(k.Key.RootRelativeUrl, url.RootRelativeUrl, StringComparison.OrdinalIgnoreCase))
.Where(k => string.IsNullOrEmpty(k.Key.HostName) || string.IsNullOrEmpty(url.HostName) || string.Equals(k.Key.HostName, url.HostName, StringComparison.OrdinalIgnoreCase))
.Count() > 0)
{
var absoluteUrl = this.urlPath.ResolveUrl(node.UnresolvedUrl, string.IsNullOrEmpty(node.Protocol) ? Uri.UriSchemeHttp : node.Protocol, node.HostName);
throw new InvalidOperationException(string.Format(Resources.Messages.MultipleNodesWithIdenticalUrl, absoluteUrl));
}
}
// Add the URL
if (url != null)
{
this.urlTable[url] = node;
}
Finally no url is add to the urlTable, which result in FindSiteMapNode cannot find anything.
I am not sure if there needs to be specific configuration. Or should I implement custom XmlSiteMapProvider just add the url.
ISiteMapNodeProvider instances cannot use the FindSiteMapNode function for 2 reasons. The first you have already discovered is that finding by URL can only be done if you set the url attribute explicitly in the node configuration. The second reason is that the SiteMapBuilder doesn't add any of the nodes to the SiteMap until all of the ISiteMapNodeProvider instances have completed running, so it would be moot to add the URL to the URL table anyway.
It might help if you explain what you are trying to accomplish.
The ISiteMapNodeProvider classes have complete control over the data that is added to the SiteMapNode instances and they also have access to their parent SiteMapNode instance. This is generally all that is needed in order to populate the data. Looking up another SiteMapNode from the SiteMap object while populating the data is not supported. But as long as the node you are interested in is populated in the same ISiteMapNodeProvider instance, you can just get a reference to it later by storing it in a variable.
Update
Okay, I reread your question and your comment and it now just seems like you are looking in the wrong place. MvcSiteMapProvider v4 is no longer based on Microsoft's SiteMap provider model, so using XmlSiteMapProvider doesn't make sense, as it would sidestep the entire implementation. The only case where this might make sense is if you have a hybrid ASP.NET and ASP.NET MVC application that you want to keep a consitant menu structure between. See Upgrading from v3 to v4.
There are 2 stages to working with the data. The first stage (the ISiteMapBuilder and ISiteMapNodeProvider) loads the data from various sources (XML, .NET attributes, DynamicNodeProviders, and custom implementations of ISiteMapNodeProvider) and adds it to an object graph that starts at the SiteMap object. Much like Microsoft's model, this data is stored in a shared cache and only loaded when the cache expires. This is the stage you have been focusing on and it definitely doesn't make sense to lookup nodes here.
The second stage is when an individual request is made to access the data. This is where looking up data based on a URL might make sense, but there is already a built-in CurrentNode property that finds the node matching the current URL (or more likely the current route since we are dealing with MVC) which in most cases is the best approach to finding a node. Each node has a ParentNode and ChildNodes properties that can be used to walk up or down the tree from there.
In this second stage, you can access the SiteMap data at any point after the Application_Start event such as within a controller action, in one of the built in HTML helpers, an HTML helper template in the /Views/Shared/DisplayTemplates/ directory, or a custom HTML helper. This is the point in the application life cycle which you might call the lines SiteMaps.Current.FindSiteMapNode(rawUrl) or (more likely) SiteMaps.Current.CurrentNode to get an instance of the node so you can inspect its Attributes property (the custom attributes).
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
var currentNode = MvcSiteMapProvider.SiteMaps.Current.CurrentNode;
string permission = currentNode.Attributes.ContainsKey("permission") ? currentNode.Attributes["permission"].ToString() : string.Empty;
string programs = currentNode.Attributes.ContainsKey("programs") ? currentNode.Attributes["programs"].ToString() : string.Empty;
string agencies = currentNode.Attributes.ContainsKey("agencies") ? currentNode.Attributes["agencies"].ToString() : string.Empty;
// Do something with the custom attributes of the About page here
return View();
}
The most common usage of custom attributes is to use them from within a custom HTML helper template. Here is a custom version of the /Views/Shared/DisplayTemplates/SiteMapNodeModel.cshtml template that displays the custom attributes. Note that this template is called recursively by the Menu, SiteMapPath, and SiteMap HTML helpers. Have a look at this answer for more help if HTML helper customization is what you intend to do.
#model MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel
#using System.Web.Mvc.Html
#using MvcSiteMapProvider.Web.Html.Models
#if (Model.IsCurrentNode && Model.SourceMetadata["HtmlHelper"].ToString() != "MvcSiteMapProvider.Web.Html.MenuHelper") {
<text>#Model.Title</text>
} else if (Model.IsClickable) {
if (string.IsNullOrEmpty(Model.Description))
{
#Model.Title
}
else
{
#Model.Title
}
} else {
<text>#Model.Title</text>
}
#string permission = Model.Attributes.ContainsKey("permission") ? Model.Attributes["permission"].ToString() : string.Empty
#string programs = Model.Attributes.ContainsKey("programs") ? Model.Attributes["programs"].ToString() : string.Empty
#string agencies = Model.Attributes.ContainsKey("agencies") ? Model.Attributes["agencies"].ToString() : string.Empty
<div>#permission</div>
<div>#programs</div>
<div>#agencies</div>

How would you implement a partial request & response, like the youtube api, using ServiceStack?

In the Youtube API, there is the power to request a "partial feed".
This allows the app developer to tailor the size and sturcture of the data returned, by specifying which "fields" to return.
i.e. GET api/person/1?fields=(id,email) would return a DTO containing only the id and the email fields, not the whole person response.
How would you attempt this using ServiceStack? Is there some way to attach a callback to the serialiser to control which properties to include in the response object?
From my experience servicestack only returns fields that actually has data. If my experience is correct then all you would need to do is figure out the best way to architect the request so that it is asking for specific data to return, this way you would only populate the response with data requested thus servicestack would only return that.
I implemented this for an API that only returns JSON.
First I created two structs to (de)serialize and interpret the "fields" query argument recursive syntax:
FieldSelector, which specifies a field and possibly its children FieldSelection enclosed between parenthesis;
FieldsSelection, which is a comma-separated list of FieldSelector.
I've used structs instead of classes because, AFAIK, you can't override class (de)serialization from/to URLs in ServiceStack. With structs you can do it by overriding ToString (serializer) and providing a constructor accepting a string as parameter (deserializer).
Then you include this on every request DTO that returns JSON:
FieldsSelection Fields { get; set; }
On a custom ServiceRunner<T>.OnAfterExecute you serialize the response DTO to JSON, parse it with ServiceStack.Text's JsonObject and apply the fields selection recursively with a method like this:
private static JsonObject Apply(this JsonObject json, FieldsSelection fieldMask)
{
IEnumerable<string> keysToRemove = json.Keys.ToList().Except(fieldMask.Keys);
foreach (var key in keysToRemove)
json.Remove(key);
foreach (var selector in fieldMask.Selectors.Values.Where(s => s.HasSubFieldsSelection))
{
var field = json[selector.Field];
if (field == null)
continue;
switch (field[0])
{
case '{':
json[selector.Field] = Apply(json.Object(selector.Field), selector.SubFieldsSelection).ToJson();
break;
case '[':
var itensArray = json.ArrayObjects(selector.Field);
for (int i = 0; i < itensArray.Count; i++)
itensArray[i] = Apply(itensArray[i], selector.SubFieldsSelection);
json[selector.Field] = itensArray.ToJson();
break;
default:
throw new ArgumentException("Selection incompatible with object structure");
}
}
return json;
}
Then you return the result as your response DTO. I've also implemented negative fields selectors (fields=-foo selects all DTO fields except foo), but you get the idea.
Look at ServiceStack.Text.JsConfig properties, they have all the hooks and customizations ServiceStack's text serializers support. Specifically the hooks that allow you to custom deserialization are:
JsConfig<T>.DeserializeFn
JsConfig<T>.RawDeSerializeFn
JsConfig<T>.OnDeserializedFn
We were able to implement said filtering by adding custom service runner and using some reflection in it to construct ExpandoObject with required field set by response DTO. See this for more info on service runners.

How do you deal with the fact, that URLs are case sensitive in xPages?

How do you deal with the fact, that URLs are case sensitive in xPages even for parameters? For example URL:
my_page.xsp?folderid=785478 ... is not the same as ...
my_page.xsp?FOLDERID=785478
How to make, for example, a proper check that params contain some key e.g.
param.containsKey("folderid") which desnt work when there is 'FOLDERID' in URL.
I'd suggest defining a couple convenience #Functions:
var #HasParam = function(parameter) {
var result:boolean = false;
for (var eachParam : param.keySet()) {
if (eachParam.toLowerCase() == parameter.toLowerCase()) {
result = true;
break;
}
}
return result;
};
var #GetParam = function(parameter) {
var result = "";
if (#HasParam(parameter)) {
for (var eachParam : param.keySet()) {
if (eachParam.toLowerCase() == parameter.toLowerCase()) {
result = param.get(eachParam);
break;
}
}
}
return result;
};
Then you can safely query the parameters without caring about case. For bonus points, you could add requestScope caching so that you can skip looping through the keySet if you're examining a parameter that you've previously looked at during the same request.
you may use this function:
context.getUrlParameter('param_name')
then test if it's null or not.
make sure to decide for one,so either upper or lowercase
other than that i'd suggest something like
KeyValuePair<string,string> kvp = null;
foreach(KeyValuePair<string,string> p in param)
{
if(UPPERCASE(p.Key) == UPPERCASE("folderid"))
{
kvp = p;
break;
}
}
syntax isn't correct and idk the uppercase method in c# right now,but you get the point
The easiest answer is ofcourse the obvious. Be sure that the parameters you are using througout your application are always the same on every url you are generating and know what to expect. A good approach to accomplish this is to create a ssjs function which generates url's for you according to the objects you submit.
In this function you could check which object you are receiving and with the use of keywords and so forth generate the correct url. This way generating twice a url with the same input parameters should always generate the exact same url.
another option would be just to double check with a bit of code like this
var key = "yourkey";
if(param.contains(#uppercase(key)) || param.contains(#lowercase(key)){
// do stuff
}
But should not be necesarry if the url you are parsing is generated by your own application
Edit after post of topic starter
Another option would be to grap the url directly from from the facescontext and to convert it to a string first. When it is a string you can parse the parameters yourself.
You can combine server side substitution/redirection to get around the issue that David mentioned. So a substitution rule will redirect incoming patern like this:
http://myhost/mypage/param (/mypage/* => which converts to - /dbpath/mypage.xsp?*) - substitution is tricky so please handle with care.
Also I believe I read somewhere that context.getUrlParameter is not case sensitive - can someone please confirm this.
Hope this helps.

Resources