Using an interface as an out parameter in WCF web service - c#

My situation is this. On every call to my web service, I have an out parameter which is an error object. If there is no error, the object indicates so by being empty. If there is an error then different properties are populated such as a "HasError" field, "ErrorMessage", "PrettyMessage", etc. What I'm trying to do with this now is create different types of Error object which all implement an Error interface which I've defined already. I then want to be able to have the out parameter be something like "out IMyError error" and then be able to set that error object either to one of my implementations of that interface depending on what kind of error I get in my method. The problem I'm getting is that serialization doesn't seem to like this. The method runs fine, but I don't get any data back on the client end. Here is some code to hopefully clarify.
My interface
public interface IMyError
{
bool HasError { get; set; }
string ErrorType { get; set; }
string PrettyErrMsg { get; set; }
}
An example class implementation
[Serializable]
[DataContract]
public class AspError : IMyError
{
public AspError(Exception exception)
{
this.HasError = true;
this.PrettyErrMsg = "An ASP Exception was thrown";
this.ExceptionMsg = exception.Message;
this.StackTrace = exception.StackTrace;
}
[DataMember(Name = "has_error", IsRequired = true)]
public bool HasError { get; set; }
[DataMember(Name = "error_type", IsRequired = true)]
public string ErrorType
{
get
{
return "ASP";
}
set
{
}
}
[DataMember(Name = "pretty_error", IsRequired = true)]
public string PrettyErrMsg { get; set; }
[DataMember(Name = "exception", IsRequired = true)]
public string ExceptionMsg { get; set; }
[DataMember(Name = "stack_trace", IsRequired = true)]
public string StackTrace { get; set; }
}
And the method in my WCF service
public bool MyMethod(out IMyError error)
{
error = new MyError() { HasError = false };
try
{
// do some code
}
catch (Exception exception)
{
error = new AspError(exception);
return false;
}
}
What I want is for that method, when an exception is caught, to return a AspError formatted as json the way it has worked in the past before I tried doing it as an interface. Or if an different implemented class of IMyError occurs then return THAT type formatted as json. I assumed it could work since they are both IMyError classes.

You need to send hint to client how to deserialize your interface. WCF does that with KnownTypeAttribute.
Your service should be changed to look like this:
[KnownType(typeof(AspError))]
public bool MyMethod(out IMyError error)
{
...
}
You can find more details in MSDN - http://msdn.microsoft.com/en-us/library/ms730167.aspx
And of course your client should have access to assembly with type AspError to be able to construct that type.

Related

Deserialize JSON property into different class properties

I am receiving API responses from 3rd party that have ambiguous types. For some methods it is:
{"error":{"message":"Resource is already part of this app","status_code":400}}
And on other calls it is:
{"error": "Resource is already part of this app" }
Is it possible to deserialize such responses into something like:
public class Response
{
[JsonProperty("error")]
public string Error { get; set; }
[JsonIgnore] //[JsonProperty("error")]
public ObjectError ObjectError { get; set; }
}
public class ObjectError
{
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("status_code")]
public string StatusCode { get; set; }
}
UPDATE
So I have ended up using object as catch all for deserialization.
[JsonProperty("error")]
public object Error { get; set; }
public string ErrorAsString => Error is string ? Error.ToString() : null;
public ObjectError ErrorAsObject => Error is string ? null : Error != null ? JsonConvert.DeserializeObject<ObjectError>(Error.ToString()) : null;
It's not ideal, I know.
You can do this easily using NetwosoftJson. Here you can check on how to deserialize into an object using it.
You could always parse the JSON object and check to see if it has specific fields. For example:
JObject json = JObject.Parse(jsonString);
if(json.HasKey("message"))
{
var result = JsonConvert.DeserializeObject<ObjectError>(jsonString);
// Do some stuff here
}
else
{
var result = JsonConvert.DeserializeObject<Response>(jsonString);
// Do some stuff here
}

Rest exception: Unable to find a constructor to use for type "myclass". A class should either have a default constructor

I know this question has been asked before, but I can't find an answer to solve this problem.
I'm making a request to a web service which returns a json, and then I save that json as an object in a list using json.net.
List<myclass> result;
var request = new RestRequest(url, Method.POST);
//Set the parameters of the request
//[...]
IRestResponse response = client.Execute(request)
Console.WriteLine(response.Content);
//response.Content = [{"nomPrecio":"string","nomPrecioEN":"string","IDrangoPrecio":0,"IDPoblacionMv":0,"NumOfertas":0,"NumOVotaciones":0,"Imagen":"anUrl"}]
//Everything works fine until here, and I can see the json is being received OK, but then...
result = JsonConvert.DeserializeObject<List<myclass>>(response.Content);
Then the console shows this message:
Rest Exception: Unable to find a constructor to use for type mynamespace.myclass. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path '[0].nomPrecio', line 1, position 14.
namespace mynamespace
{
public class myclass
{
public myclass()
{
}
public myclass(string nomPrecio, string nomPrecioEN, int IDrangoPrecio, int IDPoblacionMv, int NumOfertas, int NumOVotaciones, string Imagen)
{
this.nomPrecio = nomPrecio;
this.nomPrecioEN = nomPrecioEN;
this.IDrangoPrecio = IDrangoPrecio;
this.IDPoblacionMv = IDPoblacionMv;
this.NumOfertas = NumOfertas;
this.NumOVotaciones = NumOVotaciones;
this.Imagen = Imagen;
}
public string nomPrecio { get; set; }
public string nomPrecioEN { get; set; }
public int IDrangoPrecio { get; set; }
public int IDPoblacionMv { get; set; }
public int NumOfertas { get; set; }
public int NumOVotaciones { get; set; }
public string Imagen { get; set; }
}
}
What's more weird is that I make the same for other classes in the app and no one returns this error, all of them works.
I tried a lot of things like "json2csharp" but nothing works.
Any tip about what could I be doing wrong? Thanks
Some linker problem mb? Try to add for your class
[Preserve(AllMembers = true)]
That can happen when linker is set to "Sdk and user assemblies"

Getting an error in my Web API serialization

I have a WebApi with several controllers which return different results. For example, one controller returned an IEnumerable<Foo>, another a Bar, another an IEnumerable of IEnumerable etc., and all I had to do was:
return Ok(thething)
and everything worked fine, even complicated nested objects were serialized with no problem.
Now, The client asked that all results be returned in a Wrapper:
public class Wrapper
{
public bool Success { get; set; }
public int ErrorCode { get; set; }
public String ErrorMessage { get; set; }
public String Referrer { get; set; }
public Object Payload { get; set; }
}
Thought it would be trivial, but when I try to return it from the controller:
return Ok(new Wrapper { Success = true, Referrer = "me", Payload = thething)
I get a serialization error:
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
The inner exception message is:
'System.Linq.Enumerable+WhereSelectListIterator2[[EPiServer.Find.Api.SearchHit1[[DGTNext.Api.Data.Entities.ProductSummary,
DGTNext.Api.Entities, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null]], EPiServer.Find, Version=9.6.0.3185,
Culture=neutral,
PublicKeyToken=8fe83dea738b45b7],[DGTNext.Api.Data.Entities.ProductSummary,
DGTNext.Api.Entities, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null]]' with data contract name
'ArrayOfProductSummary:http://schemas.datacontract.org/2004/07/DGTNext.Api.Data.Entities'
is not expected. Consider using a DataContractResolver if you are
using DataContractSerializer or add any types not known statically to
the list of known types - for example, by using the KnownTypeAttribute
attribute or by adding them to the list of known types passed to the
serializer.
What am I doing wrong? Why did the Ok() function seem to be handle any object before, but has problems now?
Thanks.
Edit: as requested, a simple example of something causing the error:
class Foo
{
public int AnInt { get; set; }
}
public IHttpActionResult Get()
{
return Ok(new Wrapper { Success = true, Referrer = "me", Payload = new Foo { AnInt = 7 } });
}
Edit #2: well, I came up with kind of a solution, but it still raises some questions.
I made my Wrapper generic in the type of the payload.
public class Wrapper<T>
{
public bool Success { get; set; }
public int ErrorCode { get; set; }
public String ErrorMessage { get; set; }
public String Referrer { get; set; }
public T Payload { get; set; }
}
So now, this works:
public IHttpActionResult Get()
{
List<Foo> foos = new List<Foo>();
foos.Add(new Foo { AnInt = 7 });
foos.Add(new Foo { AnInt = 8 });
return Ok(new Wrapper<IEnumerable<Foo>> { Success = true, Referrer = "me", Payload = foos });
}
It returns:
{
"Success": true,
"ErrorCode": 0,
"ErrorMessage": null,
"Referrer": "me",
"Payload": [ { "AnInt": 7 }, { "AnInt": 8 } ]
}
And my "real" call:
public IHttpActionResult Get()
{
IEnumerable<ProductSummary> prods = db.getProductSummaries(shopId, culture, queryParams, paging);
return Ok(new Wrapper<IEnumerable<ProductSummary>> { Success = true, Referrer = "me", Payload = prods });
}
returns:
<WrapperOfArrayOfProductSummaryzc2y5_Pnl>
<ErrorCode>0</ErrorCode>
<ErrorMessage i:nil="true"/>
<Payload>
<d2p1:ProductSummary>
<d2p1:Culture i:nil="true"/>
<d2p1:Guid i:nil="true"/>
<d2p1:Id>2</d2p1:Id>
<d2p1:Name>Letto Asia</d2p1:Name>
<d2p1:ambient>
<d2p1:Id>1073741838</d2p1:Id>
<d2p1:Name>notte</d2p1:Name>
</d2p1:ambient>
etc.
So not bad, but this raises two questions:
To test the webapi, I'm calling it by putting an URL in the Firefox address bar and looking an the results in the browser. Why in the world does the first call return Json, and the second XML? As far as I know I'm just using default everything.
Why is the XML now adding that namespace to all the element names? Can I prevent this? When I was just returning the same thing without the wrapper, this didn't happen.
Add this code to global.asax below on Application_Start:
Update from .Ignore to .Serialize. It must work.
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
or you may look at this answer

Deserializing OData.Error messages

I have an ASP.NET application that uses the Azure AD Graph API. Often, when an invalid operation is performed against the Graph API, an exception is thrown.
The following code shows an invalid Graph API call that would trigger an exception:
// Query the Azure AD User
var userToUpdate = await activeDirectoryClient.Users.GetByObjectId("user#domain.net").ExecuteAsync();
// Set given name to an empty string (not allowed)
userToUpdate.GivenName = "";
try
{
// Update the user in Azure AD
await userToUpdate.UpdateAsync();
}
catch (Exception e)
{
// Return exception message
}
The message of the inner exception is a JSON string with forward slashes before each quotation mark. It looks something like this:
"{\"odata.error\":{\"code\":\"Request_BadRequest\",\"message\":{\"lang\":\"en\",\"value\":\"Invalid value specified for property 'givenName' of resource 'User'.\"},\"values\":[{\"item\":\"PropertyName\",\"value\":\"givenName\"},{\"item\":\"PropertyErrorCode\",\"value\":\"InvalidValue\"}]}}"
Attaching a screenshot of the Locals window where the exception message is found:
I would like to convert this JSON to a .NET object to return informative error details. I am using the JSON.NET library for this, and I am assuming that the JSON will deserialize to an ODataError object:
var error = Newtonsoft.Json.JsonConvert.DeserializeObject<ODataError>(e.InnerException.Message);
However, the deserialized object always has a value of null, which means that the conversion is not working as expected.
That being said, what class should the above JSON string map to? Also, should I be removing the forward slashes from the string for proper deserialization?
The reason you've got null after deserialization is your JSON object properties names differs from Microsoft.Azure.ActiveDirectory.GraphClient.ODataError class properties names - "odata.error" property can not be deserialized to Error property of Microsoft.Azure.ActiveDirectory.GraphClient.ODataError
As workaround I've added my own types for correct deserialization:
internal class ODataError
{
[JsonProperty("odata.error")]
public ODataErrorCodeMessage Error { get; set; }
}
internal class ODataErrorCodeMessage
{
public string Code { get; set; }
public ODataErrorMessage Message { get; set; }
public List<ExtendedErrorValue> Values { get; set; }
}
internal class ExtendedErrorValue
{
public string Item { get; set; }
public string Value { get; set; }
}
internal class ODataErrorMessage
{
public string Lang { get; set; }
public string Value { get; set; }
}
After that JSON message was properly deserialized:
...
try
{
await ADClient.Users.AddUserAsync(newUser);
return Result.Ok();
}
catch (DataServiceRequestException ex)
{
var innerException = ex.InnerException;
var error = JsonConvert.DeserializeObject<ODataError>(innerException.Message);
return Result.Fail(new Error(error.Error.Message.Value, error.Error.Code, ex));
}

JSON to C# object using Data Contracts - What am I missing here?

I am experiencing an error converting JSON to a strongly-typed class.
My JSON: {"listBoxID":"ctl00_ctl00_MainContentRoot_MainContent_lstBxSettings","sourceItemText":"Horizontal Bar","sourceItemValue":"Horizontal"}
DroppedItem droppedItem = JsonConvert.DeserializeObject<DroppedItem>(json);
/// <summary>
/// Outlines an object which is useful in simplifying how a CormantRadDock is created.
/// Instead of passing in lots of parameters, would rather just pass in an object that the
/// CormantRadDock knows how to interpret.
/// </summary>
[DataContract]
public class DroppedItem
{
private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
[DataMember(Name = "sourceItemText")]
public string Text { get; set; }
[DataMember(Name = "sourceItemValue")]
public string Value { get; set; }
[DataMember(Name = "listBoxID")]
public Reports ReportType { get; set; }
public DroppedItem() { }
public DroppedItem(string text, string value, string listBoxID)
{
Logger.DebugFormat("Text: {0}, Value: {1}, slidingPaneTitle: {2}", text, value, listBoxID);
Text = text;
Value = value;
ReportType = DetermineReportType(listBoxID);
}
private Reports DetermineReportType(string listBoxID)
{
if (listBoxID.Contains("lstBxHistorical"))
{
return Reports.HistoricalReport;
}
else if (listBoxID.Contains("lstBxCustom"))
{
return Reports.CustomReport;
}
else
{
return Reports.None;
}
}
}
The issue is with converting listBoxID to ReportType.
Uncaught Sys.WebForms.PageRequestManagerServerErrorException: Sys.WebForms.PageRequestManagerServerErrorException: Error converting value "ctl00_ctl00_MainContentRoot_MainContent_lstBxSettings" to type 'CableSolve.Web.Reports'
It occurs regardless of whether the if statement finds a hit or defaults to the else block. It does not occur if I do not attempt to pass the listBoxID parameter.
I'm missing something here. Are my DataMember names not doing anything? I thought they would map the listBoxID to the correct property.
Change to something like this:
public Reports ReportType { get; set; }
[DataMember(Name = "listBoxID")]
public string listBoxID
{
set
{
ReportType = DetermineReportType(value);
}
}
Because basically, you can convert that string to a Report without your helper method. The constructor is not being called on deserialization

Categories