After sending using [RelayCommand] in maui the setter in ViewModel receives truncated string in Maui - string

After sending using [RelayCommand] in maui the setter in ViewModel receives truncated string in Maui.
Example orginal string: "https://twit.memberfulcontent.com/rss/9039?auth=m9FZurRandomAuthonumbers6yB"
The value of URL is good here:
[RelayCommand]
async Task Tap(string Url)
{
System.Diagnostics.Debug.WriteLine("Tap Sending: " + Url);
await Shell.Current.GoToAsync($"{nameof(ShowPage)}?Url={Url}");
}
when recieving here URL is truncated:
namespace NerdNewsNavigator2.ViewModel;
[QueryProperty("Url", "Url")]
public partial class ShowViewModel : ObservableObject
{
#region Properties
readonly TwitService _twitService;
public ObservableCollection<Show> Shows { get; set; } = new();
public string Url
{
set
{ // value is truncated string from Tap above.
System.Diagnostics.Debug.WriteLine("ShowViewModel Recieving url: " + value);
_ = GetShows(value);
OnPropertyChanged(nameof(Shows));
}
}
// Code shortened for brevity
Example of passed string:
"https://twit.memberfulcontent.com/rss/9039"
It gets truncated at ?Auth
Any suggestions on what I may be doing wrong? Or suggestion on a better way to do this? It works fine for string that do not have a ? mark in them. One hundred percent working except on this specific type of string.
I was expecting the string not to be truncated.

Correct way to pass data between pages in MAUI:
When you navigate:
Dictionary<string, object> query = new()
{
{ nameof(MyModel), myModel }
};
await Shell.Current.GoToAsync($"{nameof(MyPage)}", query);
When you are navigated:
public void ApplyQueryAttributes(IDictionary < string , object > query)
{
myModel = query[nameof(MyModel)] as MyModel;
}
(This solves more than the problem with sanitization)

Related

How to return error message in Rest API when return value is a Byte array? [duplicate]

I have an Azure Function 2.x that reside on a static class that looks like this
[FunctionName("Register")]
public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post")]HttpRequest req, ILogger log)
{
MyTypeClass defReturn = new MyTypeClass();
HttpStatusCode defCode = HttpStatusCode.BadRequest;
/*
* Logics that might or might not changes
* defReturn and defCode value
*/
return StatusCode((int) defCode, JsonConvert.SerializeObject(defReturn))
}
How can i achieve the return StatusCode((int) defCode, JsonConvert.SerializeObject(defReturn)) part ? is there any such method or equivalent in Azure Functions 2.x ?
in Azure Functions 1.x i can do the equivalent with req.CreateResponse(defCode, defReturn) where req is HttpRequestMessage , but i'm trying to stick with 2.x template/standard
Additional explanation : The said Code should return HTTP 400 Bad Request with the defReturn as it's response body to the client. But when i change the defCode to HttpStatusCode.Accepted, it should return HTTP 202 Accepted with the same response body. How can i achieve this ?
Additional explanation#2 : (If i remember correctly) in ASP.NET Core 1.x i can exactly do like that, returning IActionResult by calling a static method StatusCode not StatusCodes (which is a static class that contains HTTP codes constants
Thank you
Quite late reply, but I was stumbling into the same problem today, so maybe this is helpful for other searchers
Option 1: Default Codes
This is stated in detail on the blog Here
Some codes like 200 and 400 are predefined and can be used by
return new OkObjectResult("Your message"); // 200
return new BadRequestObjectResult("Your error message"); // 400
These functions are not available for every known Status Codes but some of the most frequent.
Option 2: Manual setting Code
If you need specific codes, that are not provided by default, you can use the base classes and create them yourself.
To achieve the Teapot Response for example, you can just use
using Microsoft.AspNetCore.Http;
var result = new ObjectResult("Your message");
result.StatusCode = StatusCodes.Status418ImATeapot;
return result;
In this example, the Statuscode is used from the StatusCodes class, but you can use enter other codes as well (usually, just stick to these codes)
Also, the ObjectResult class offers additional formatting options, if needed.
You can create a model class in which you can define two properties, i.e. one form your status code and one for you Json object and later on you can return the complete model. Code sample would be like below:
public static class QueueTriggerTableOutput
{
[FunctionName("QueueTriggerTableOutput")]
[return: Table("outTable", Connection = "MY_TABLE_STORAGE_ACCT_APP_SETTING")]
public static Person Run(
[QueueTrigger("myqueue-items", Connection = "MY_STORAGE_ACCT_APP_SETTING")]JObject order,
ILogger log)
{
return new Person() {
PartitionKey = "Orders",
RowKey = Guid.NewGuid().ToString(),
Name = order["Name"].ToString(),
MobileNumber = order["MobileNumber"].ToString() };
}
}
public class Person
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public string Name { get; set; }
public string MobileNumber { get; set; }
}
on the receiving front, you can catch both the property.
P.S.- you have to change the return type of your function.
Hope it helps.

VaryByParam fails if a param is a list

I've got this action in MVC
[OutputCache(Duration = 1200, VaryByParam = "*")]
public ActionResult FilterArea( string listType, List<int> designersID, int currPage = 1 )
{
// Code removed
}
that fails to present the correct HTML with url like
http://example.com/en-US/women/clothing?designersID=158
http://example.com/en-US/women/clothing?designersID=158&designersID=13
Is this a know bug of OutputCache in .NET cause cannot recognize VaryByParam with a list param or am I missing something?
I too had the same issue in MVC3 and I believe it's still the same case in MVC5.
Here is the setup I had.
Request
POST, Content-Type:application/json, passing in an array of string as the parameter
{ "options": ["option1", "option2"] }
Controller Method
[OutputCache(Duration = 3600, Location = OutputCacheLocation.Any, VaryByParam = "options")]
public ActionResult GetOptionValues(List<string> options)
I tried every option possible with OutputCache and it just wasn't caching for me. Binding worked fine for the actual method to work. My biggest suspicion was that OutputCache wasn't creating unique cache keys so I even pulled its code out of System.Web.MVC.OutputCache to verify. I've verified that it properly builds unique keys even when a List<string> is passed in. Something else is buggy in there but wasn't worth spending more effort.
OutputCacheAttribute.GetUniqueIdFromActionParameters(filterContext,
OutputCacheAttribute.SplitVaryByParam(this.VaryByParam);
Workaround
I ended up creating my own OutputCache attribute following another SO post. Much easier to use and I can go enjoy the rest of the day.
Controller Method
[MyOutputCache(Duration=3600)]
public ActionResult GetOptionValues(Options options)
Custom Request class
I've inherited from List<string> so I can call the overriden .ToString() method in MyOutputcache class to give me a unique cache key string. This approach alone has resolved similar issues for others but not for me.
[DataContract(Name = "Options", Namespace = "")]
public class Options: List<string>
{
public override string ToString()
{
var optionsString= new StringBuilder();
foreach (var option in this)
{
optionsString.Append(option);
}
return optionsString.ToString();
}
}
Custom OutputCache class
public class MyOutputCache : ActionFilterAttribute
{
private string _cachedKey;
public int Duration { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.Url != null)
{
var path = filterContext.HttpContext.Request.Url.PathAndQuery;
var attributeNames = filterContext.ActionParameters["Options"] as AttributeNames;
if (attributeNames != null) _cachedKey = "MYOUTPUTCACHE:" + path + attributeNames;
}
if (filterContext.HttpContext.Cache[_cachedKey] != null)
{
filterContext.Result = (ActionResult) filterContext.HttpContext.Cache[_cachedKey];
}
else
{
base.OnActionExecuting(filterContext);
}
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.HttpContext.Cache.Add(_cachedKey, filterContext.Result, null,
DateTime.Now.AddSeconds(Duration), System.Web.Caching.Cache.NoSlidingExpiration,
System.Web.Caching.CacheItemPriority.Default, null);
base.OnActionExecuted(filterContext);
}
}

WebAPI can't call Put method

I have the following code on server:
public class UploadController : ApiController
{
public void Put(string filename, string description)
{
...
}
public void Put()
{
...
}
and try to call it from client:
var clientDescr = new HttpClient();
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("filename", "test"));
postData.Add(new KeyValuePair<string, string>("description", "100"));
HttpContent contentDescr = new FormUrlEncodedContent(postData);
clientDescr.PutAsync("http://localhost:8758/api/upload", contentDescr).ContinueWith(
(postTask) =>
{
postTask.Result.EnsureSuccessStatusCode();
});
but this code calls second put method (without parameters). Why and how to call first put method correctly?
You have several options here:
You can either choose to pass the parameters in the query string, by just changing the URI to:
http://localhost:8758/api/upload?filename=test&description=100
or you can have Web API parse the form data for you by changing your action to look like this:
public void Put(FormDataCollection formData)
{
string fileName = formData.Get("fileName");
string description = formData.Get("description");
}
You can also choose to create a class that has a fileName and a description property and use that as your parameter and Web API should be able to bind it correctly for you.

Unable to Serialize into XML

I am trying to Serialize the content of some text into an XML file (performed when a user saves their selections), and then will later deserialize it (when the user chooses to display their saved selection).
I have been following the following tutorial on serialization.
I have also tried to do this via LINQ to XML but was either getting namespace errors, or the tool returned no errors, but did not work (with the same problem as described below).
The problem I am having is that my code is not returning any errors, but the function is not working (I have a label control that allows me to see that the 'catch' is being returned). I am building the tool in Expression Blend, using C#.
Here is my SaveSelection.cs Class
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Xml.Serialization;
using System.Xml;
namespace DYH
{
public class SaveSelections
{
[XmlAttribute("Title")]
public string Title
{ get; set; }
[XmlElement("Roof")]
public string RoofSelection
{ get; set; }
[XmlElement("Cladding")]
public string CladdingSelection
{ get; set; }
[XmlElement("MixedCladding")]
public string MixedCladdingSelection
{ get; set; }
[XmlElement("FAJ")]
public string FAJSelection
{ get; set; }
[XmlElement("GarageDoor")]
public string GarageDoorSelection
{ get; set; }
[XmlElement("FrontDoor")]
public string FrontDoorSelection
{ get; set; }
}
}
Here is my C# code
// Save Selection Button
private void Button_SaveSelection_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
try
{
// Save selections into the SavedSelections.xml doc
SaveSelections userselection = new SaveSelections();
userselection.Title = TextBox_SaveSelection.Text;
userselection.RoofSelection = Button_Roof_Select.Text;
userselection.CladdingSelection = Button_Cladding_Select.Text;
userselection.MixedCladdingSelection = Button_MixedCladding_Select.Text;
userselection.FAJSelection = Button_FAJ_Select.Text;
userselection.GarageDoorSelection = Button_GarageDoor_Select.Text;
userselection.FrontDoorSelection = Button_FrontDoor_Select.Text;
SerializeToXML(userselection);
// XDocument xmlSaveSelections = XDocument.Load("../SavedSelections.xml");
//
// XElement newSelection = new XElement("Selection", //xmlSaveSelections.Element("Selections").Add(
// //new XElement("Selection",
// new XElement("Title", TextBox_SaveSelection.Text),
// new XElement("RoofSelection", Button_Roof_Select.Text),
// new XElement("CladdingSelection", Button_Cladding_Select.Text),
// new XElement("MixedCladdingSelection", Button_MixedCladding_Select.Text),
// new XElement("FAJSelection", Button_FAJ_Select.Text),
// new XElement("GarageDoorSelection", Button_GarageDoor_Select.Text),
// new XElement("FrontDoorSelection", Button_FrontDoor_Select.Text));
//
//// xmlSaveSelections.Add(newSelection);
//// xmlSaveSelections.Save("../SavedSelections.xml");
SelectionLabel.Text = "Your selection has been saved as " + "'" + TextBox_SaveSelection.Text + "'. We suggest you write down the name of your selection.";
}
catch(Exception ex)
{
throw ex;
SelectionLabel.Text = "There was a problem saving your selection. Please try again shortly.";
}
}
// Saves SaveSelection.cs to XML file SavedSelections.xml
static public void SerializeToXML(SaveSelections selection)
{
XmlSerializer serializer = new XmlSerializer(typeof(SaveSelections));
TextWriter textWriter = new StreamWriter(#"/SavedSelections.xml");
serializer.Serialize(textWriter, selection);
textWriter.Close();
}
I have left evidence of one of my previous attempts commented out so you can see a previous format I tried.
My issue is that when I try to use the tool, the SelectionLabel.Text returns "There was a problem saving your selection. Please try again shortly." so I know that the code is returning the catch and not executing the 'try'.
Any help??
Edit 18/6/2012: The below code was the code that worked as per correct answer to question.
public void Button_SaveSelection_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
string roofSelection = TextBox_SaveSelection.Text + "_RoofSelection";
string claddingSelection = TextBox_SaveSelection.Text + "_CladdingSelection";
string mixedCladdingSelection = TextBox_SaveSelection.Text + "_MixedCladdingSelection";
string fajSelection = TextBox_SaveSelection.Text + "_FAJSelection";
string garageDoorSelection = TextBox_SaveSelection.Text + "_GarageDoorSelection";
string frontDoorSelection = TextBox_SaveSelection.Text + "_FrontDoorSelection";
try
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
// Gives us 6Mb of storage space in IsoStore
Int64 isoSpaceNeeded = 1048576 * 6;
Int64 currentIsoSpace = store.AvailableFreeSpace;
// If space needed is greater than (>) space available, increase space
if (isoSpaceNeeded > currentIsoSpace)
{
// If user accepts space increase
if (store.IncreaseQuotaTo(currentIsoSpace + isoSpaceNeeded))
{
IsolatedStorageFileStream file = store.CreateFile("SavedSelections.txt");
file.Close();
// Stream writer to populate information in
using (StreamWriter sw = new StreamWriter(store.OpenFile("SavedSelections.txt", FileMode.Open, FileAccess.Write)))
{
appSettings.Add(roofSelection, Button_Roof_Select.Text);
sw.WriteLine(roofSelection);
appSettings.Add(claddingSelection, Button_Cladding_Select.Text);
sw.WriteLine(claddingSelection);
appSettings.Add(mixedCladdingSelection, Button_MixedCladding_Select.Text);
sw.WriteLine(mixedCladdingSelection);
appSettings.Add(fajSelection, Button_FAJ_Select.Text);
sw.WriteLine(fajSelection);
appSettings.Add(garageDoorSelection, Button_GarageDoor_Select.Text);
sw.WriteLine(garageDoorSelection);
appSettings.Add(frontDoorSelection, Button_FrontDoor_Select.Text);
sw.WriteLine(frontDoorSelection);
}
SelectionLabel.Text = "Your selection has been saved as " + "'" + TextBox_SaveSelection.Text + "'. We suggest you write down the name of your selection.";
}
}
}
SelectionLabel.Text = "Your selection has been saved as " + "'" + TextBox_SaveSelection.Text + "'. We suggest you write down the name of your selection.";
}
catch //(Exception ex)
{
//throw ex;
SelectionLabel.Text = "There was a problem saving your selection. Please try again shortly.";
}
}
It looks like your issue is because you're trying to a file, but that file did not come from a FileSaveDialog initiated by a user action. You're running into a security feature of Silverlight where you're not allowed access to the local file system. Instead, try writing to IsolatedStorage. However, be aware that end users can completely (as well as selectively) disable application storage so you'll need to handle those exceptions as well.
Here's a quick article on how to use IsolatedStorage.

Does ServiceStack support binary responses?

Is there any mechanism in ServiceStack services to return streaming/large binary data? WCF's MTOM support is awkward but effective in returning large amounts of data without text conversion overhead.
I love service stack, this litle code was enough to return an Excel report from memory stream
public class ExcelFileResult : IHasOptions, IStreamWriter
{
private readonly Stream _responseStream;
public IDictionary<string, string> Options { get; private set; }
public ExcelFileResult(Stream responseStream)
{
_responseStream = responseStream;
Options = new Dictionary<string, string> {
{"Content-Type", "application/octet-stream"},
{"Content-Disposition", "attachment; filename=\"report.xls\";"}
};
}
public void WriteTo(Stream responseStream)
{
if (_responseStream == null)
return;
_responseStream.WriteTo(responseStream);
responseStream.Flush();
}
}
From a birds-eye view ServiceStack can return any of:
Any DTO object -> serialized to Response ContentType
HttpResult, HttpError, CompressedResult (IHttpResult) for Customized HTTP response
The following types are not converted and get written directly to the Response Stream:
String
Stream
IStreamWriter
byte[] - with the application/octet-stream Content Type.
Details
In addition to returning plain C# objects, ServiceStack allows you to return any Stream or IStreamWriter (which is a bit more flexible on how you write to the response stream):
public interface IStreamWriter
{
void WriteTo(Stream stream);
}
Both though allow you to write directly to the Response OutputStream without any additional conversion overhead.
If you want to customize the HTTP headers at the sametime you just need to implement IHasOptions where any Dictionary Entry is written to the Response HttpHeaders.
public interface IHasOptions
{
IDictionary<string, string> Options { get; }
}
Further than that, the IHttpResult allows even finer-grain control of the HTTP output where you can supply a custom Http Response status code. You can refer to the implementation of the HttpResult object for a real-world implementation of these above interfaces.
I had a similar requirement which also required me to track progress of the streaming file download. I did it roughly like this:
server-side:
service:
public object Get(FooRequest request)
{
var stream = ...//some Stream
return new StreamedResult(stream);
}
StreamedResult class:
public class StreamedResult : IHasOptions, IStreamWriter
{
public IDictionary<string, string> Options { get; private set; }
Stream _responseStream;
public StreamedResult(Stream responseStream)
{
_responseStream = responseStream;
long length = -1;
try { length = _responseStream.Length; }
catch (NotSupportedException) { }
Options = new Dictionary<string, string>
{
{"Content-Type", "application/octet-stream"},
{ "X-Api-Length", length.ToString() }
};
}
public void WriteTo(Stream responseStream)
{
if (_responseStream == null)
return;
using (_responseStream)
{
_responseStream.WriteTo(responseStream);
responseStream.Flush();
}
}
}
client-side:
string path = Path.GetTempFileName();//in reality, wrap this in try... so as not to leave hanging tmp files
var response = client.Get<HttpWebResponse>("/foo/bar");
long length;
if (!long.TryParse(response.GetResponseHeader("X-Api-Length"), out length))
length = -1;
using (var fs = System.IO.File.OpenWrite(path))
fs.CopyFrom(response.GetResponseStream(), new CopyFromArguments(new ProgressChange((x, y) => { Console.WriteLine(">> {0} {1}".Fmt(x, y)); }), TimeSpan.FromMilliseconds(100), length));
The "CopyFrom" extension method was borrowed directly from the source code file "StreamHelper.cs" in this project here: Copy a Stream with Progress Reporting (Kudos to Henning Dieterichs)
And kudos to mythz and any contributor to ServiceStack. Great project!

Resources