Serialization of non-required fields in protobuf-net - c#

I have a working java client that is communicating with Google, through ProtoBuf serialized messages. I am currently trying to translate that client into C#.
I have a .proto file where the parameter appId is an optional string. Its default value in the C# representation as generated by the protobuf-net library is an empty string, just as it is in the java representation of the same file.
message AppsRequest {
optional AppType appType = 1;
optional string query = 2;
optional string categoryId = 3;
optional string appId = 4;
optional bool withExtendedInfo = 6;
}
I find that when I explicitly set appId to "" in the java client, the client stops working (403 Bad Request from Google). When I explicitly set appId to null in the java client, everything works, but only because hasAppId is being set to false (I'm uncertain as to how that affects the serialization).
In the C# client, I always get 403 responses. I don't see any logic behind the distinction between not setting a value, and setting the default value, that seems to make all the difference in the java client. Since the output is always a binary stream, I am not sure if the successful java messages are being serialized with an empty string, or not serialized at all.
In the C# client, I've tried setting IsRequired to true on the ProtoMember attribute, to force them to serialize, and I've tried setting the default value to null, and explicitly set "", so I'm quite sure I've tried some configuration where the value is being serialized. I've also played around with ProtoBuf.ProtoIgnore and at some point, removing the appId parameter altogether, but I haven't been able to avoid the 403 errors in C#.
I've tried manually copying the serialized string from java, and that resolved my issues, so I'm certain that the rest of the HTTP Request is working, and the error can be traced to the serialized object.
My serialization is simply this:
var clone = ProtoBuf.Serializer.DeepClone(request);
MemoryStream ms = new MemoryStream(2000);
ProtoBuf.Serializer.Serialize(ms, clone);
var bytearr = ms.ToArray();
string encodedData = Convert.ToBase64String(bytearr);
I'll admit to not being quite sure about what DeepClone does. I've tried both with and without it...

It sounds like we want to force it to be excluded; for a first thing to try, you could try using the "detectmissing" option in the code-generation. This is possible from the IDE and command-line, but differently (let me know which you are using and I'll add more).
Another similar option is to add (in a partial class) a bool {memberName}Specified {get;set;}. There is an existing open report of an oddity involving default empty strings, that I am looking at.

Related

How to assert that a API response has a specific structure?

I have a SpecFlow test definition set up where I want to assert that the response have a specific collection of fields in its response. For example I have this particular expected response from the API:
{
isActive: false,
lastProcessed: "2020-11-03T19:03:16.537"
}
What I want to verify is that the response contains those two fields, not necessarily the value those fields contain. I tried the following method:
Assert.NotNull(response.Model.isActive);
Assert.NotNull(response.Model.lastProcessed);
But I'm getting an error using this method:
Do not use Assert.NotNull() on value type 'bool'
How else can I make sure the response structure is as expected other than using "NotNull()"?
The Solution:
Following the accepted answer, I serialized the model returned from the API call into JSON and parsed it into a JObject. Then I used the ContainsKey() method to assert it.
JObject jObject = JObject.Parse(JsonConvert.SerializeObject(response.Model));
Assert.True(jObject.ContainsKey("isActive"));
I don't know what packages you use for sending requests and deserialization but if you could get the response content as a raw json string you could then use Newtonsoft.JSON to parse the response into a JObject with JObject.Parse(responseContent). JObject has a method called ContainsKey(propertyName) which determines whether there is a field of a specified name in the object. You could assert if it returns true for the desired property names.
Edit
Regarding Greg's answer below. The original error is in fact caused by bool not being a nullable type and making it nullable in the model would fix the error. However, this solution is not ideal. In some cases null can be a valid value returned by the API and this would generate false negatives. e.g. if we recieved:
{
isActive: null,
lastProcessed: "2020-11-03T19:03:16.537"
}
then Assert.NotNull(response.Model.isActive) would yield a negative test result even though the field is present in the json, and that's what we wanted to check.
So theoretically if we are 100% sure that null will never be returned by the API itself, then we could do it that way, but it won't be a universal method. Also not very descriptive of what we are trying to achieve ;)
Since the isActive property is a bool you'll need to assert that it is false. If instead you want a true or false value, and then something to represent that it is missing, use a nullable boolean instead in your DTO:
public class YourDTO
{
public bool? isActive { get; set; }
...
}
Then you can assert isActive is null, true or false.
Alternative: If you cannot update the original data transfer object, then this might be a good use case for writing your own code to call the web service and map the JSON response to your own DTO used just for your tests.
This could be a large amount of work, however. The advantage is your test code is truly decoupled from the code it tests. I've done this with applications that use a database as well. It is extra work, but it allows your tests to use whatever data structure makes sense for the test.

What is the best way to serialize, return and consume complex C# object from an MVC3 app?

Here's my situation: I have an MVC3 app that has some very complex C# objects, and those get rendered to a views in this application. However, I have a new requirement: a console application (that I am also writing) will run under a scheduler, and it needs to pull these objects from this MVC3 app, and then do something else with these complex objects.
Since I have control over both apps, I can share a library of the complex objects between them. All of these objects are marked [Serializable]. However, I cannot figure out an easy way to serialize these objects and send them from the MVC3 app to the Console app.
I tried simple JavaScriptSerializer and using the HttpClient to read the string, then deserialize it on the console-app end of things, but unfortunately it doesn't deserialize the data correctly. Everything is null. I can inspect the string on a breakpoint when it arrives at the console app, and all the data is there, in the string, but it just doesn't get de-serialized correctly.
Is there an easier way to do this? I don't care what the serialization method is. The data doesn't have to be passed as JSON and no other application but mine is going to consume these objects. But so far I can't figure out the easiest way to produce/consume these objects.
I know I can go down the whole "create a web service contract" and use data annotations route, but I was hoping there was an easier, less time-consuming way of doing it.
Using Json.NET:
Server-Side
string serializedObject = JsonConvert.SerializeObject(yourComplexObject);
// Send the string to the client...
Client-Side
In the client, you don't even have to know the deserialized object's type, you can take advantage of anonymous objects and dynamic:
string serializedObject = // ... Fetch from server
dynamic complexObject = JsonConvert.DeserializeObject(serializedObject);
// ...
string id = complexObject.UserId;
P.S.: Please note that the object's methods or state is not going to get serialized, only the public properties are.
Can your action just return your object? If so, your client code would look something like (using HttpClient)
var result = client.GetAsync(url).Result;
var myObj = await result.Content.ReadAsAsync<T>();

How to remove the OData innererror from production services

The spec for Error Response says:
The value for the innererror name/value pair MUST be an object. The contents of this object are service-defined. Usually this object contains information that will help debug the service. The innererror name/value pair SHOULD only be used in development environments in order to guard against potential security concerns around information disclosure.
The spec is right, in asp.net the innererror property gives a useful info such as the stacktrace, but I really don't want to share this info with my API clients
As of yet, I haven't found a way of removing this property from the response, is it even possible?
Yes, it is possible, but is quite cumbersome.
You need to do four things:
Firstly, you should derive your own OData error serializer from the default implementation. The difference from the default ODataErrorSerializer will be to override the method containing the following code:
bool includeDebugInformation = oDataError.InnerError != null;
Change it to
bool includeDebugInformation = oDataError.InnerError == null;
or simply setting the value to false in your overridden implementation. Let's say your own OData error serializer is called MyODataErrorSerializer.
Then you need to derive your own OData serializer provider from the default one. The difference from the DefaultODataSerializerProvider will be to change the following code:
private static readonly ODataErrorSerializer _errorSerializer = new ODataErrorSerializer();
to your own error serializer:
private static readonly ODataErrorSerializer _errorSerializer = new MyODataErrorSerializer();
Let's say your own serializer provider is called MyODataSerializerProvider.
After that, do the similar thing to ODataMediaTypeFormatters. Derive a MyODataMediaTypeFormatters from DefaultODataMediaTypeFormatters which uses MyODataSerializerProvider instead of DefaultODataSerializerProvider.
Finally, add the following code to your Web API OData implementation:
config.Formatters.InsertRange(0, ODataMediaTypeFormatters.Create());

How MVC Web Application gets parameters without Request.Querystring?

I'm working on a MVC web application. I need to download a file which I've stored as a byte[] stream in DB and its working fine. What I used to do on a button click I call a JS function and that calls a function in the C# backend and eventually download the file. Following is my JQuery code.
var DownloadDRR = function ()
{
var InvoiceId = $(".invoiceid").text();
location.href = location.origin + "/Invoicing/DownloadDRR?InvoiceId=" + InvoiceId;
}
And in the backend I normally get query string like this
Request.Querystring("InvoiceId");
But accidental I've discovered in my application if I write the following it still gets the InvoiceId without using Request.QueryString().
public FileResult DownloadDRR(int InvoiceId)
{
InvoicingService Invoices = new InvoicingService(Client);
byte[] RawExcel = Invoices.GetExcelService(InvoiceId);
MemoryStream stream = new MemoryStream(RawExcel);
stream.Seek(0, SeekOrigin.Begin);
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "test.xlsx");
}
Can anyone explain why please?
MVC specifically automates a lot of that binding (model binding is the term used).
myurl.com/MyController/MyMethod?something=a&anotherthing=1234
//example 1
public ActionResult MyMethod(string something, string anotherthing)
//example2
public ActionResult MyMethod(string something, int anotherthing)
It works for both examples. Even though your querystring technically only contains string values, MVC will try to parse it to the desired type.
The only thing you need to pay attention to is that the querystring parameter names match the method's parameter names. The rest is done automagically :)
//example3
public ActionResult MyMethod(int something, int anotherthing)
In this example, something cannot be converted, as "a" cannot be put into an int. The method call will fail (expect an ASP.Net error page). However, there are ways around this:
If the type is nullable, the method will still be called, and null will be the value. int? something will be set as null if conversion fails, and this makes sure the method still gets called.
You can make it an optional parameter: MyMethod(int anotherthing, int something = 0). Notice the inversion of the parameters. Optional parameters must always be placed after normal (required) parameters! This will make sure that, when something either cannot be converted (or simply isn't part of the querystring), it will receive the default value you specified (in my example, 0)
Some remarks:
You can write custom modelbinders that go way deeper than just converting a value. However, this is not default MVC behavior. It's still good to know you can add it if you need it.
Not all parameters are always part of the querystring. If you make a POST request (as opposed to the more lax GET request), you won't see a querystring. The values are still passed, but not as part of the requested URL. This is a topic you can find tons of information on via Google.

PHP SOAP returning wrong values to clients

I'm trying to return a value from my (MySQL) database to a C# client, through SOAP.
The server is written in PHP, which I think is where everything goes wrong:
class foo{
function bar()
{
$result = $connection->query("SELECT value
FROM table
WHERE id='$id'");
$row = $result->fetch_assoc();
return (int)$row['value'];
}
}
Will turn up as 0 in C#, no matter what value was in the database. However,
class foo{
function bar()
{
return 5;
}
}
turns up as the correct value (5 in this case) in C#.
What am I missing here? It seems that data from the database will not be returned to C# correctly, but any other ways of writing the same value (statically) in PHP, will succeed.
EDIT:
I've now set up a PHP SOAP-client, which receives the same values as C# does. So it seems that the problem is with the PHP SOAP-server, communicating the wrong values to the client (whenever the value to return is not static).
EDIT:
I have now found the solution to my problem; My WSDL file had a complex request type, even though it only required a simple type. Changing this complex type to a simple type, fixed the problem. Do not ask me why - it does not make the slightest sense to me!
You are asking for an associative array. This may return more that one item.

Categories

Resources