Uri ToString() method decoding the Uri Query - c#

In my WebAPI project I have some problems with redirects. This is because the Uri.ToString() method behaves in a "defensive" way, in oter words, once the mentione method is called he decodes the safe parts of the query string.
Consider this failing unit test:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UriTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
// Arrange
const string expectedUrlRaw =
"http://localhost/abc?proxy=http%3A%2F%2Ftarget.nl%3Fparam1%3Dvalue1%26param2%3Dvalue2";
const string expectedUrlInHttpsRaw =
"https://localhost/abc?proxy=http%3A%2F%2Ftarget.nl%3Fparam1%3Dvalue1%26param2%3Dvalue2";
Uri expectedUri = new Uri(expectedUrlRaw);
Uri expectedUriInHttps = new Uri(expectedUrlInHttpsRaw);
// Act
string returnsUriInHttpsRaw = expectedUri.ToHttps().ToString();
// Assert
Assert.AreEqual(expectedUrlInHttpsRaw, returnsUriInHttpsRaw);
}
}
public static class StringExtensions
{
public static Uri ToHttps(this Uri uri)
{
UriBuilder uriBuilder = new UriBuilder(uri);
uriBuilder.Scheme = Uri.UriSchemeHttps;
uriBuilder.Port = 443;
return uriBuilder.Uri;
}
}
}
Now, I can't modify this behavior by constructing my own link from Uri properties as I have no control over it.
In my controller I do respond in the following way to a get message in order to redirect the call:
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Found);
response.Headers.Location = // my Uri object
This works fine until a certain point. If my redirect Uri contains a query that contains an encoded link, it will return the wrong result. (and this is probably because the Headers.Location is read by calling a ToString on that property.
Does anyone have an idea on how to overcome this problem?
Thanks

Uri.ToString() does decode URL encoded sequences. (like %20=> a white space).
The behavior also changes between different versions of the .net framework.
In short, don't use Uri.ToString(), use Uri.AbsoluteUri or Uri.OriginalString.
See the following article for an in-depth investigation
https://dhvik.blogspot.com/2019/12/uritostring-automatically-decodes-url.html

Related

Moq Framework to unit test a method that returns a task

I am new to this MOQ framework and honestly having a hard time with having to get my unit test to run. Basically, I have a C# application which basically does some uploads to APIs using PostAsync.
Now, since I can't (and should not) call the API during my unit test (as otherwise it would be an integration test), I added a wrapper method around it and allowing that method to return true by mocking it. But no matter what I do, it is returning false. I have gone through SO questions, but I am not sure what am I missing. I haven't used interfaces but am using classes with virtual methods.
Here is my sample code that I would want to test
public async Task<bool> CreateNoteBookDirectory (string url ,string bearertoken, JavaScriptSerializer jser,RestPostClass rest)
{
NoteBookDirectory jsnbdir = new NoteBookDirectory();
jsnbdir.path = "/JobNotebooks/ClientScoreDataInput";
var directorycreate = jser.Serialize(jsnbdir);
var content = new StringContent(directorycreate, Encoding.UTF8, #"application/json");
bool result=await rest.HttpPost(url, content, bearertoken);
return result;
}
This method is in the main class.
The RestPostClass class has the virtual method HttpPost, whose skeleton is somewhat like this
public async virtual Task<bool> HttpPost(String url, StringContent content, string bearertoken)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", bearertoken);
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(#"application/json"));
var postresult = await client.PostAsync(url, content);
bool result = parseResponse(postresult);
return result;
}
Now, in my unit test, I am trying to test the CreateNoteBookDirectory method and since do not want the post method to be called, mocking it.
Here is what I am doing in my sample unit test
Mock<DataBricksRestPost> mock = new Mock<DataBricksRestPost>();
mock.Setup(x => x.HttpPost("http://test.com", new StringContent("abc"), "token")).Returns(Task.FromResult(true));
Program prog = new Program();
var jser = new JavaScriptSerializer();
bool result= await prog.CreateNoteBookDirectory("http://test.com", "token", jser, mock.Object);
Assert.IsTrue(result, "Test failed");
It keeps returning false because apparently the mocking does not really happen properly.
What am I missing?
Any questions and I will try my best to clarify.
P.S: I have used the existing the "program" class as I am basically starting.
Mock returns false, because when you call HttpPost parameters don't match with ones that were set up. The second parameter is different.
You can set up mock like that:
mock
.Setup(x => x.HttpPost("http://test.com", It.IsAny<StringContent>(), "token"))
.Returns(Task.FromResult(true)); //or .ReturnsAsync(true);
It tells mocking framework, that second parameter can be any object of type StringContent.
Docs can be found here: https://github.com/Moq/moq4/wiki/Quickstart#matching-arguments

How do you properly add a C# stand alone script as a step in Octopus Deploy?

So, I'm looking into adding a C# stand alone script as part of a Step in a deployment process, but am having a really hard time finding a reference that show how to properly "format" the script for use by Octopus.
One thing I did find was that all references need to be explicit. So for instance, if the script makes use of HttpClient to make a GET request, you can't rely on a using statements to shorten the reference, but instead have to use the "fully qualified namespace".
So basically instead of being able to do this:
HttpClient client = new HttpClient();
You have to do it like this:
System.Net.Http.HttpClient client = new System.Net.HttpClient()
Okay, so I've modified my script to make explicit reference to any class or method within its given namespace.
Now, what happens if I have a custom class? How do I handle that? I'll illustrate what I mean with an example. Say I have the following:
using System;
namespace MyOctoScript
{
class Person
{
public string name { get; set; }
}
class Script
{
static System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
static System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
public const string endpoint = "some_valid_endpoint";
static async System.Threading.Tasks.Task Main(string [] args)
{
MyOctoScript.Person person = null;
// Use Http Client to fetch JSON from a given endpoint
System.Net.Http.HttpResponseMessage response = await client.GetAsync(endpoint);
// Parse JSON from response
string jsonString = await response.Content.ReadAsStringAsync();
// Store object in variable of type Person
person = serializer.Deserialize<MyOctoScript.Person>(jsonString);
}
}
}
Now, this script works as a console application. I want to ensure it works once I add it as a C# script that is part of a Step.
What changes (if any) do I need to make to the code above to achieve this?
Thanks to anyone in advance!
The Docs say Octopus supports C# via ScriptCS
https://octopus.com/docs/deployment-examples/custom-scripts#supported-script-types
https://github.com/scriptcs/scriptcs
So I'd assume (untested) you'd need to flatten it to something like this:
using System;
class Person
{
public string name { get; set; }
}
static System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
static System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
public const string endpoint = "some_valid_endpoint";
Person person = null;
// Use Http Client to fetch JSON from a given endpoint
System.Net.Http.HttpResponseMessage response = await client.GetAsync(endpoint);
// Parse JSON from response
string jsonString = await response.Content.ReadAsStringAsync();
// Store object in variable of type Person
person = serializer.Deserialize<Person>(jsonString);
tbh, I'm not sure how ScripCS handles Async, so there's some work to do to figure that out.
ScriptCS can be used to run standalone scripts or as a REPL, so you can test it locally.

How are args represented (if at all) in Web API Attribute Routing annotations and if they aren't, how are they discovered?

I am currently using attribute routings such as this:
[Route("api/InventoryItems/{ID}/{packSize:int}/{CountToFetch:int}")]
...and am using even longer ones (with more "pieces," or arguments), and in practice they work fine.
However, I need to refactor this (args masquerading as part of the path considered bad practice) so that the URI passed by the client is not like so:
//http://<machineName>:<portNum>/api/InventoryItems/<IDVal>/<packSizeVal>/<CountToFetchVal>
http://platypus:8675309/api/InventoryItems/42/24/50
...but rather like this:
//http://<machineName>:<portNum>/api/InventoryItems/?<argName>=<argVal>?<argName>=<argVal>?<argName>=<argVal>
http://platypus:8675309/api/InventoryItems?ID=42?packSize=24?CountToFetch=50
Currently I can grab the args passed within the "path" info and pass them from the Controller (where they arrive) to the Repository (which uses them to get the precise data required).
For example, this Controller method:
[System.Web.Http.Route("api/Departments/{ID:int}/{CountToFetch:int}/{dbContext=03}")]
public IEnumerable<Department> GetBatchOfDepartmentsByStartingID(int ID, int CountToFetch, string dbContext)
{
return _deptsRepository.Get(ID, CountToFetch, dbContext);
}
...has the values passed via a URI from the client assigned to the method parameters. What, if anything, do I need to change in this code for args passed in the URI via the "?=" method to also be assigned to the method parameters?
Can I do this, with those args simply stripped out of the Attribute Routing annotation, like so:
[System.Web.Http.Route("api/Departments")]
public IEnumerable<Department> GetBatchOfDepartmentsByStartingID(int ID, int CountToFetch, string dbContext)
{
return _deptsRepository.Get(ID, CountToFetch, dbContext);
}
?
...or possibly leave it as-is, with just the format of the URI changing (but nothing in the Controller)?
UPDATE
I wasn't expecting it to work, but I can definitely verify that leaving the server code as is, and replacing the URI with this jazz:
"?<argName>=<argVal>"
...does not work - it returns null without even hitting my Controller method!
UPDATE 2
With an URI like this:
http://localhost:28642/api/InventoryItems/PostInventoryItem?id=42?pack_size=12?description=ValuableDesc?vendor_id=venderado?department=42?subdepartment=85?unit_cost=2.50?unit_list=3.75?open_qty25.25?UPC_code=12345?UPC_pack_size=24?vendor_item=someVendorItem?crv_id=9898987?dbContext=03
...I can reach the Controller if I remove all args from the routing attribute and the method signature:
[Route("api/InventoryItems/PostInventoryItem")]
public void PostInventoryItem()
{
HandheldServerGlobals.SaveTypes st = HandheldServerGlobals.SaveTypes.CSV; //Works (C:\Program Files (x86)\IIS Express\SiteQuery3.csv created) // <-- I reach the breakpoint on this line, but...:
//commented out for now:
//_inventoryItemRepository.PostInventoryItem(id, pack_size, description, vendor_id, department, subdepartment, unit_cost, unit_list, open_qty, UPC_code, UPC_pack_size, vendor_item, crv_id, dbContext, st);
}
...but where/how do I get the args passed in the URI now?
By annotating the Controller method with "[FromURI]":
[Route("api/InventoryItems/PostInventoryItem")]
public HttpResponseMessage PostInventoryItem([FromUri] InventoryItem ii)
{
_inventoryItemRepository.PostInventoryItem(ii.ID, ii.pksize, ii.Description, ii.vendor_id, ii.dept,
ii.subdept, ii.UnitCost, ii.UnitList, ii.OpenQty, ii.UPC, ii.upc_pack_size, ii.vendor_item, ii.crv_id);
var response = Request.CreateResponse<InventoryItem>(HttpStatusCode.Created, ii);
string uri = Url.Link("DefaultApi", new { id = ii.ID });
response.Headers.Location = new Uri(uri);
return response;
}
...and by replacing all but the first "?" in the URI passed in with "&"

How to set HttpRequest.InputStream value using reflection

I am writing a unit test for a WebApi controller that reads a POST body from Request.InputStream. I need to set the inputstream property of HttpContext.Current.Request.InputStream, or set the inputstream's contents. Here is my unit test code so far, but it keeps throwing exceptions:
var originalStream = HttpContext.Current.Request.InputStream;
Stream newStream = new MemoryStream(ASCIIEncoding.Default.GetBytes("Test String"));
var propInfo = originalStream.GetType().GetProperty("CanWrite");
propInfo.SetValue(originalStream, true);
newStream.CopyTo(originalStream);
propInfo.SetValue(originalStream, false);
I get the following exception on the SetValue line:
ArgumentException: Property set method not found
Am I going about this all wrong? My controller reads the input stream and deserializes it into JSON, so I need to be able to insert data into that stream. I just don't know how to do it. Many thanks.
public abstract bool CanWrite { get; }. There is not setter on that property.. hence your error.
In your example, originalStream will be of type Stream. Wrap it in another stream for your test. You aren't testing the HttpRequest.InputStream, you're testing the deserialization ...
var originalStream = new StreamReader(HttpContext.Current.Request.InputStream);
var content = originalStream.ReadToEnd();
.. etc.
You may even consider skipping using the request input altogether since you aren't really testing that.
EDIT:
To expand a bit more. You should move this out of the controller action.. do something like the below:
public class YourController : Controller {
private readonly IStreamWrapper _streamWrapper;
public YourController(IStreamWrapper wrapper) {
_streamWrapper = wrapper;
}
public ActionResult MethodYouAreTesting() {
var result = _streamWrapper.Process(HttpRequest.InputStream);
}
}
public class Tests {
public void YourTestMethod() {
var controller = new YourController(new FakeStreamWrapper()); // mock perhaps?
// Asserts here for the controller action
}
public void YourWrapperTester() {
var wrapper = new RealStreamWrapper();
// test Process method here..
}
}
Then you can test the stream reading and the deserialization in isolation.
Does that make sense or have I made it more complex? :/
The InputStream is a read-only stream.
There are several ways. To start with the simplest:
Create an instance of a StreamReader and call ReadToEnd() on InputStream to get the data being sent.
Once you have the contents, you can modify as needed before doing your JSON conversion.
Again, this is the simplest, least elegant way to do what you want, but it should work.

Web-API Get with object

I created a Web-API and i would like to get all routes with parameters BeginAddress (string), EndAddress(string), BegineDate (Datetime). I created a new Class SearchRoute with these properties.
I can do a normal Getwith an id or a string but how to do a Get by giving an object? Is this possible?
Would it be possible to do a post/put with an object and than ask for a return?
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(url + userid);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
List<Route> list = await SerializeService.Deserialize<List<Route>>(content);
return list;
}
return null;
}
Web API Function
public List<Route> GetAllByCity(SearchRoute sr)
{
return RouteDAO.GetAllByCity(sr);
}
Update:
If i do this, the Post doesn't work but if i create a new controller it works.
[HttpPost]
// POST api/route
public void Post([FromBody]Route route)
{
RouteDAO.Create(route);
}
// POST api/route
[HttpPost]
public List<Route> Post([FromBody]SearchRoute sr)
{
return RouteDAO.GetAllByCity(sr);
}
I prefer sticking with GET even when using a complex object as a parameter. If you are concerned about the length of the URI then remember that:
Prefixing the property names for simple like complex objects is not necessary because the Web API object binding can auto resolve based on property names alone.
The maximum allowed URL length is 2083 characters which is more than sufficient in most cases.
If you we take your example
public class SearchRoute {
public string BeginAddress {get;set;}
public string EndAddress {get;set;}
public DateTime BeginDate {get;set;}
}
[HttpGet]
public List<Route> Get([FromUri]SearchRoute sr)
{
return RouteDAO.GetAllByCity(sr);
}
Uri when searching on
BeginAddress = "Some beginning";
EndAddress = "Some ending"
BeginDate = "2016-01-01T16:40:00"
Resulting query string:
?BeginAddress=Some beginning&EndAddress=Some ending&BeginDate=2016-01-01T16:40:00
Again, the properties will auto resolve even without the object prefix/qualifier and populate the object instance.
Add a domain info to the URL maybe another 50 or so characters
Add a controller name maybe another 30 or so characters
Add the query string = 82 characters
Note that I am not taking into account resolving the special characters like spaces to Url escaped character sequence
Total ≈ 162 characters give or take
Not bad considering that the maximum allowed URL length is 2083 characters, so you have used up only 7% of what is possible in this simple example.
This would probably be the preferred way of doing it because it conforms to the RESTful API standard where GET calls/verbs do not alter data and POST calls/verbs do.
You can pass an object by using a complex type in the URI. You need to help Web API by using the correctly formatted Query String. This would be an example:
?SearchRoute.BeginAddress=TheAddressValue&SearchRoute.EndAddress=TheAddressValue
However, if your Query String starts to become too big, you might be modeling the interaction incorrectly.
Then, in the server you should let Web API know that it should look in the URI for the values:
public List<Route> GetAllByCity([FromUri]SearchRoute sr)
{
return RouteDAO.GetAllByCity(sr);
}

Categories