I don't understand why this happens. I'm consuming REST service via WCF. In case of error the service sends this kind of message body (I see in Fiddler):
{"Errors":["Some text"],"StatusCode":500}
I have created a class for this, and getting it via Message.GetBody(). StatusCode is filled, but Errors are always empty (not null, but empty). I have tried String[], List of String, also tried initialized backing field. What is wrong here?
Here is the class:
[DataContract(Name="root", Namespace="")]
public class ErrorResult
{
[DataMember]
public string[] Errors { get; set; }
[DataMember]
public int StatusCode { get; set; }
public override String ToString()
{
return String.Join(Environment.NewLine, Errors);
}
}
Here is the parsing:
public void AfterReceiveReply(ref Message reply, object correlationState)
{
var resp = reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;
if (resp != null && resp.StatusCode != HttpStatusCode.OK)
{
String message = null;
try
{
ErrorResult res = reply.GetBody<ErrorResult>();
if (res != null)
{
message = res.ToString();
}
}
catch { }
if (!String.IsNullOrEmpty(message))
{
throw new Exception(message);
}
}
}
I'm using [DataContract(Name="root", Namespace="")] because I was getting error "expecting element ErrorResult with namespace 'bla-bla-bla' but got element 'root' with namespace ''".
UPDATE. I have noticed the same problem with another entity, the one which was service result type. It contained 2 integer fields, and on return they were 0s, even though I could see in Fiddler and in Message.ToString() values there. I have fixed this by adding Name="..." to DataMember attributes, even though those names are the same as property names, except casing (JSON camel vs C# pascal). I was sure that C# parsing is case insensitive! Ok, but adding names to ErrorResult in subject still didn't fix the problem with array.
UPDATE 2. Ok, after not finding any solution, I did a workaround in an "ugly way". At least this started working at once.
XmlDocument doc = new XmlDocument();
doc.LoadXml(reply.ToString());
String message = String.Join(Environment.NewLine,
doc.SelectNodes("/root/Errors/item").OfType<XmlNode>().Select(n => n.InnerText));
I have also tried XMlSerialzer with Message.GetReaderAtBodyContents(), but got "error in XML" and gave it up.
Here is explained what to do to make List work. I usually use arrays (eg: string[] not Array class) to avoid problems or additional configuration and cast on client side.
Related
I've done hours of reading on this and tried many different solutions I've found on SO and else where and still haven't been able to get this working.
The situation is, I've written a web api rest service to provide an interface to a 3rd party application via COM. It's been in production now for a number of months and works flawless. I've been tasked by a new consumer to provide XML responses, up until now it's been working exclusively with JSON perfectly fine.
No matter what I try I can not get an XML response using Postman for testing.
In Post man I have both the content-type and accept header tags set to "application/xml"
So my controller is pretty simple
public class SampleController : ApiController
{
[HttpGet]
[Route("api/getobject")]
public HttpResponseMessage GetByGSTNumber(string token, string key)
{
DataConnection connection = null; new DataConnection();//Set up COM reference
DataObject dataObj = null;
try
{
connection = new DataConnection();
connection.login(token);
dataObj = new DataObject(connection);
Request.CreateResponse(HttpStatusCode.OK, dataObj);
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
}
finally
{
if (connection != null) { Request.RegisterForDispose(connection); }
if (dataObj != null) { Request.RegisterForDispose(dataObj); }
}
}
}
The Definition of my data object, again don't think overly complicated. It does do a bit of parsing under the hood to convert non CLR types to CLR types.
public class DataObject : IDisposable
{
internal DataConnection dbConnection;
internal dynamic _dataRecord;
public string Key
{
get { return _dataRecord.KeyField.ToString(); }
}
public string Property1
{
get { return _dataRecord.Property1Feild.ToString(); }
}
public DataObject(DataConnection connection)
{
dbConnection = connection;
}
public void OpenRecord(string key)
{
if (!_dataRecord.Seek(key))
{
throw new Exception("Record not found");
}
}
#region IDisposable Support
}
So what I've tried so far is change
return Request.CreateResponse(HttpStatusCode.OK, dataObj);
//Returns JSON even if specifying XML in request
to
return Request.CreateResponse(HttpStatusCode.OK, dataObj "application/xml");
// returns <ExceptionMessage>Could not find a formatter matching the media type 'application/xml' that can write an instance of 'DataObject'.</ExceptionMessage>
I've decorated my data object with [DataContract] and properties with [DataMember] tags but that resulted in no change.
I've created a parameterless constructor, which got me this, but no properties/values are in the xml
I've tried setting the XML Serializer in my Global.asax file but this had no noticeable effect.
What am I missing?
Update
Included application start where I've tried different combos of UseXMLSerializer = true/false with all the above changes
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;
}
I've also tried putting an XML serialize method on my data object and changing my create response call
public string XMLSerialize()
{
XmlSerializer xsSubmit = new XmlSerializer(typeof(DataObject));
using (var sww = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sww))
{
xsSubmit.Serialize(writer, this);
return sww.ToString(); // Your XML
}
}
}
return Request.CreateResponse(HttpStatusCode.OK, creditor.XMLSerialize(), "application/xml");
This results in a
There was an error generating the XML document.
System.InvalidOperationException
Exception aside, I'm not 100% sure that this would produce a correctly formatted result
Update
Have changed the properties on my business class to have an empty setter and now getting XML with properties in my response. Visual studio is complaining about empty setters but compiling, I'm guessing it's not the norm.
public string Key
{
get { return _dataRecord.KeyField.ToString(); }
set {} VS give me a wiggly green line on this but no info
}
For all those that helped. Thanks a ton.
Credit goes to Steve16351 for giving me the hint that got me going in the right direction.
So the problem was in the way I had defined my DataObject class. I needed two changes.
Firstly a paramaterless constructor.
Secondly, I needed to specify property setters. Initially didn't think I needed setters since I would never need to Post data using this class as it's underlying data source is read only.
I'm working on a self hosting rest api used to monitor de status of several servers.
I was tasked that, when everything is working correctly, I should only return
{"response":"ok"}
But, when there's an error on queried server, or servers, I must return
{ "response" : [ {"agent":"<server>:<port>","port":"<port>" ,"Error":"<Description of the error>"} ] }
I was thinking on building a helper class to build object on this schema and returning them over the rest api
public class HelperErrorResponseClass
{
public string agent { get; set; }
public string port { get; set; }
public string Error { get; set; }
}
This is no problem, the issue is, how to deal when everything it ok. I have this Api response helper class
public class Response
{
public string response { get; set; }
}
But I'm seeing that I'll need to change the response property to List<HelperErrorResponseClass> in order to send the error response. Do you think that, if I stringify the List<HelperErrorResponseClass> object with Json.Net it will be returned in the desired format?
Edit: Forgot to add that, I-m using Web Api to build the rest service.
UDPATE:
After further research, I found a way to work this out.
Following this post, I was able to rewrite the helper classes like this
[DataContract]
[KnownType(typeof(List<HelperErrorResponseClass>))]
public class Response
{
[DataMember]
public object response { get; set; }
}
[DataContract]
public class HelperErrorResponseClass
{
[DataMember(EmitDefaultValue = false)]
public string agent { get; set; }
[DataMember(EmitDefaultValue = false)]
public string port { get; set; }
[DataMember(EmitDefaultValue = false)]
public string error { get; set; }
}
This work to fulfill my and my client needs... except for one little thing. When I get the result from a List, and given that I added the KnownTypes directive, my response is now this
{"response":[{"__type":"HelperErrorResponseClass:#AppCommonLib","Error":"ERROR","InstanceId":"<InstanceId> : <Port>","PortType":"<PortType>"},{"__type":"HelperErrorResponseClass:#AppCommonLib","Error":"ERROR","InstanceId":"<InstanceId> : <Port>","PortType":"<PortType>"}]}
Any idea how to get rid of that __type property of the response? make that it must be explicit to only return the declared properties of the helper class?
Simplest way to deal with this is to set the return type on the handling function to string, then you can check for errors and do something like;
//pseudo code to give an idea
if (errorsList.Count() > 0)
{
return JsonConvert.SerializeObject(errorsList);
}
else
{
return JsonConvert.SerializeObject(new Response("ok"));
}
Now this being said... Unless the people providing requirements aren't at all flexible you should just redo the design. How about just returning the errors array and the person calling the API can infer that if it's length is 0 then everything is working OK. Seems pretty straight forward, right? You could also just put all the properties on one object and those fields would just come back as null or empty strings. Or you could change you serializer settings to exclude them if they don't have a value.
Keep things simple and use an anonymous type.
if (condition)
{
return JsonConvert.SerializeObject(new { response = new { agent = "x", port = "y", error = "z" }});
}
else
{
return JsonConvert.SerializeObject(new { response = "ok"});
}
More info:
https://msdn.microsoft.com/en-us/library/bb397696.aspx
I personally don't think you need a Response class, especially that it is of object type. IMHO, you've overcomplicated the very simple issue that you have. It is not only the __type, but also other info like HelperErrorResponseClass:#AppCommonLib that isn't supposed to be there.
Another Issue you have is the incorrect name of the HelperErrorResponseClass class. This is not a helper class. It is a standard data-object class.
A helper class is a class filled with static methods. It is usually used to isolate a "useful" algorithm.
This is how I would do it:
I'd get rid of the Response class.
I'd use your original simple HelperErrorResponseClass class, but rename it to something more meaningful like ErrorDetails.
I'd return the response like this:
.
if (errorsList.Count() > 0) {
return JsonConvert.SerializeObject(new { response = errorsList});
}
else {
return JsonConvert.SerializeObject(new { response = "ok"});
}
However, if you really want to stick to your updated solution, an easy way to get rid of the __type is simply removing it from the final serialized string:
if (errorsList.Count() > 0) {
string r = JsonConvert.SerializeObject(new { response = errorsList});
return r.Replace("__type", "");
}
else {
return JsonConvert.SerializeObject(new { response = "ok"});
}
I am attempting to get ServiceStack to return a list of objects to a C# client, but I keep getting this exception:
"... System.Runtime.Serialization.SerializationException: Type definitions should start with a '{' ...."
The model I am trying to return:
public class ServiceCallModel
{
public ServiceCallModel()
{
call_uid = 0;
}
public ServiceCallModel(int callUid)
{
this.call_uid = callUid;
}
public int call_uid { get; set; }
public int store_uid { get; set; }
...... <many more properties> ......
public bool cap_expense { get; set; }
public bool is_new { get; set; }
// An array of properties to exclude from property building
public string[] excludedProperties = { "" };
}
The response:
public class ServiceCallResponse
{
public List<ServiceCallModel> Result { get; set; }
public ResponseStatus ResponseStatus { get; set; } //Where Exceptions get auto-serialized
}
And the service:
public class ServiceCallsService : Service
{
// An instance of model factory
ModelFactory MyModelFactory = new ModelFactory();
public object Any(ServiceCallModel request)
{
if (request.call_uid != 0)
{
return MyModelFactory.GetServiceCalls(request.call_uid);
} else {
return MyModelFactory.GetServiceCalls() ;
}
}
}
The client accesses the service with:
JsonServiceClient client = new ServiceStack.ServiceClient.Web.JsonServiceClient("http://172.16.0.15/");
client.SetCredentials("user", "1234");
client.AlwaysSendBasicAuthHeader = true;
ServiceCallResponse response = client.Get<ServiceCallResponse>("/sc");
The "model factory" class is a DB access class which returns a list. Everything seems to work just fine when I access the service through a web browser. The JSON returned from the service starts:
"[{"call_uid":70...."
And ends with:
"....false,"is_new":true}]"
My question is, what here might be causing serialization/deserialization to fail?
Solution
Thanks to the answer from mythz, I was able to figure out what I was doing wrong. My misunderstanding was in exactly how many DTO types there are and exactly what they do. In my mind I had them sort of merged together in some incorrect way. So now as I understand it:
Object to return (In my case, called "ServiceCallModel": The actual class you wish the client to have once ServiceStack has done its job. In my case, a ServiceCallModel is a key class in my program which many other classes consume and create.
Request DTO: This is what the client sends to the server and contains anything related to making a request. Variables, etc.
Response DTO: The response that the server sends back to the requesting client. This contains a single data object (ServiceCallModel), or in my case... a list of ServiceCallModel.
Further, exactly as Mythz said, I now understand the reason for adding "IReturn" to the request DTO is so the client will know precisely what the server will send back to it. In my case I am using the list of ServiceCallModel as the data source for a ListView in Android. So its nice to be able to tell a ListViewAdapter that "response.Result" is in fact already a useful list.
Thanks Mythz for your help.
This error:
Type definitions should start with a '{'
Happens when the shape of the JSON doesn't match what it's expecting, which for this example:
ServiceCallResponse response = client.Get<ServiceCallResponse>("/sc");
The client is expecting the Service to return a ServiceCallResponse, but it's not clear from the info provided that this is happening - though the error is suggesting it's not.
Add Type Safety
Although it doesn't change the behavior, if you specify types in your services you can assert that it returns the expected type, e.g Change object to ServiceCallResponse, e.g:
public ServiceCallResponse Any(ServiceCallModel request)
{
...
}
To save clients guessing what a service returns, you can just specify it on the Request DTO with:
public class ServiceCallModel : IReturn<ServiceCallResponse>
{
...
}
This lets your clients have a more succinct and typed API, e.g:
ServiceCallResponse response = client.Get(new ServiceCallModel());
instead of:
ServiceCallResponse response = client.Get<ServiceCallResponse>("/sc");
See the New API and C# Clients docs for more info.
I have inherited a project from my predecessor which uses OpenRasta to host a webservice for my OpenSource colleagues to access for their applications. This is my first foray into OpenRasta
I've added a lot of additional features all of which is working via manual browser requests, although not 100% reliably but that's perhaps another question later. So I have embarked on creating a set of Unit Tests to test the functionality, which I should be doing anyway. I have successfully created a unit test or two for each GET request all of which are passing, but I am stuck on the test for the single POST I have in the project.
I'm getting a HTTP 415 Error '8-[2012-12-07 11:23:19Z] Information(0) Executing OperationResult OperationResult: type=RequestM ediaTypeUnsupported, statusCode=415.' from the output window. I've taken inspiration from a post by Nate Taylor http://taylonr.com/integration-testing-openrasta and have asked him the same question, which he has kindly replied to. I'm still trying to decipher his answer, and perhaps someone might be able to expand and fill in the gaps in my understanding?
Here is the code which I have been trying:
[Test]
public void AuthenticateUserJSONPOSTTest()
{
object content = new AuthenticationStructure { Username = "matthew.radford", Password = "obviously not going to tell you that bit and will replace with a domain test user when the time comes", AppId = 4 };
OpenRastaJSONTestMehods.POST<AuthenticationResult, AuthenticationStructure>("http://localhost/user", content);
}
[Test]
public static void POST<T, U>(string uri, object content)
{
const string LocalHost = "http://localhost/";
if (uri.Contains(LocalHost))
POST<T, U>(new Uri(uri), content);
else
throw new UriFormatException(string.Format("The uri doesn't contain {0}", LocalHost));
}
[Test, WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
public static void POST<T,U>(Uri serviceuri, object content)
{
using (var host = new InMemoryHost(new Configuration()))
{
var request = new InMemoryRequest()
{
Uri = serviceuri,
HttpMethod = "POST"
};
request.Entity.ContentType = MediaType.Json;
request.Entity.Headers["Accept"] = "application/json";
var serializer = new DataContractJsonSerializer(typeof(T), new [] { typeof(AuthenticationStructure) });
serializer.WriteObject(request.Entity.Stream, content);
request.Entity.Stream.Seek(0, SeekOrigin.Begin);
request.Entity.ContentLength = request.Entity.Stream.Length;
//Just a read test, not necessary for the output
byte[] readbyte = new byte[(int)request.Entity.ContentLength];
request.Entity.Stream.Read(readbyte, 0, (int)request.Entity.ContentLength);
request.Entity.Stream.Seek(0, SeekOrigin.Begin);
U readObject = (U)serializer.ReadObject(request.Entity.Stream);
request.Entity.Stream.Seek(0, SeekOrigin.Begin);
NUnit.Framework.Assert.AreEqual(content, readObject);
var response = new InMemoryResponse();
response.Entity.ContentType = MediaType.Json;
response.Entity.Headers["Accept"] = "application/json";
response = (InMemoryResponse)host.ProcessRequest(request);
int statusCode = response.StatusCode;
//this is where the test fails because the above response isn't correct and gives the 415 statusCode
NUnit.Framework.Assert.AreEqual(201, statusCode, string.Format("Http StatusCode Error: {0}", statusCode));
object returnedObject;
if (response.Entity.ContentLength > 0)
{
response.Entity.Stream.Seek(0, SeekOrigin.Begin);
//Just a read test, not necessary for the output
readbyte = new byte[(int)response.Entity.ContentLength];
response.Entity.Stream.Read(readbyte, 0, (int)response.Entity.ContentLength);
response.Entity.Stream.Seek(0, SeekOrigin.Begin);
returnedObject = serializer.ReadObject(response.Entity.Stream);
//return returnedObject;
}
}
}
Thanks in advance.
I've tried so many different things this morning to try and get this working. The first good step forward I made by trying to read the JSON stream as a string to actually see what the object was being serialized as.
To do that I found How to convert an Stream into a byte[] in C#? this set me off in the right direction to read the stream out to a string. I therefore came up with this line to write it to the output window:
Debug.WriteLine(Encoding.UTF8.GetString(StreamHelper.ReadToEnd(request.Entity.Stream), 0, (int)request.Entity.Stream.Length).ToString());
This was the result:
{"__type":"AuthenticationStructure","username":"matthew.radford","appid":4,"password":"###########"}
I realised there were two problems in this output. Firstly and simply, the password should be before the appid, which was easily fixed in the AuthenticationStructure class, where I had made a mistake. DataMember Order needed to equal 3 for AppId
[DataMember(Name="appid", Order=3)]
public int AppId { get; set; }
Secondly though, the default serialization includes a '__type' member at the beginning of the notation. This obviously then doesn't match my parameters on the POST method of my Handler:
[HttpOperation(HttpMethod.POST)]
public OperationResult Post(string username, string password, int appid)
At this point I looked at trying to remove the type notation from the JSON string. I found a good sites Deserialize array values to .NET properties using DataContractJsonSerializer which showed me both how to write the constructor to include alwaysEmitTypeInformation which they had set to false, but I wanted to Emit the type information, so changed it to true. And it also showed me how to create an Surrogate based on IDataContractSurrogate, which I called AuthenticationTypeSurrogate.
public class AuthenticationTypeSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
// "Book" will be serialized as an object array
// This method is called during serialization, deserialization, and schema export.
if (typeof(AuthenticationStructure).IsAssignableFrom(type))
{
return typeof(object[]);
}
return type;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
// This method is called on serialization.
if (obj is AuthenticationStructure)
{
AuthenticationStructure authenticationStructure = (AuthenticationStructure)obj;
return new object[] { authenticationStructure.Username, authenticationStructure.Password, authenticationStructure.AppId };
}
return obj;
}
public object GetDeserializedObject(object obj, Type targetType)
{
// This method is called on deserialization.
if (obj is object[])
{
object[] arr = (object[])obj;
AuthenticationStructure authenticationStructure = new AuthenticationStructure { Username = (string)arr[0], Password = (string)arr[1], AppId = (int)arr[2] };
return authenticationStructure;
}
return obj;
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
return null; // not used
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
return typeDeclaration; // Not used
}
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
return null; // not used
}
public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
{
return null; // not used
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
return; // not used
}
}
The serialization worked for this but now the Deserialization, which I never bothered to debug and get right because it still wasn't working because instead of creating a JSON it was serializing a object array, which you would expected because that was what the method GetObjectoSerialize method is returning. So I hit another brick wall unless I could work out how to change this into JSON.
So finally I thought well I'll just add the __type parameter into an overloaded POST method and pass on the other parameters to the original POST method.
[HttpOperation(HttpMethod.POST)]
public OperationResult Post(Type __type, string username, string password, int appid)
{
return Post(username, password, appid);
}
This was so nearly right but it still didn't work, so finally I created another overloaded method and passed the whole type:
[HttpOperation(HttpMethod.POST)]
public OperationResult Post(AuthenticationStructure astruct)
{
return Post(astruct.Username, astruct.Password, astruct.AppId);
}
And finally this worked. I'm not perfectly happy with it and would have liked to link straight into my existing method, but it works.
Thanks to everyone who looked and especially Nate, thanks for your original responses, and hopefully this will help people in the future.
In our application, we are receiving a collection (List) as an input parameter to a WCF webmethod, and that input parameter is passed as is, without transferring to any local member, to a StaticClass.StaticMethod. Inside the static method, the first line checks the count of the input parameter List to greater than Zero and the next line I am retrieving the first element (0th index), but it throws “Index out of range” Error while this application is tested using load runner.
At first glance it appears like a simple race condition, but the load runner access this WCF service via a website and there no way the website can pass empty collection.
Any thoughts?
// Code snippet
public static List<X> GetCashBalances(List<Y> IPReceivedAtWebMethod)
{
List<X> list = new List<X>();
if IPReceivedAtWebMethod== null) return list;
if IPReceivedAtWebMethod.Count <= 0) return list;
// The below line throws Index out of range error.
SomeValue s = AdminHelper.GetSomeValue(IPReceivedAtWebMethod[0].member1);
// …
}
The WCF Service method calling the above method is given below for reference,
public class CashService : ICashService
{
public ServiceResponse GenerateCashBalances(RequestToWCFService request)
{
ServiceResponse response = DataContractFactory.InstanceOfServiceResponse();
try
{
// This is the code calling the method I referred in the question which is throwing Index out of range Error
response._someList = StaticClass.GetCashBalancesReferredInQuestion(request._someList);
// I hope this would not do any harm to _someList
List<CashBalance> list = response._someList.ConvertAll(c => (CashBalance)c);
// Second call using the same collection however the list is not alterned inside this method too.
response.someActivity = AdminController.GetActivity(request._someList).ToString("O");
response.ResponseCode = WcfServiceCodes.OK_RESPONSE;
}
catch (Exception ex)
{
// log error
}
return response;
}
Detail about the Request object
[DataContract]
public class RequestToWCFService : BaseRequest
{
[DataMember]
public List<AccountGroup> _someList { get; set; }
}
I'd take a look at how the list is being used by the calling method. It'd be pretty easy to say:
var balances = GetCashBalances(_someList);
... where _someList is a static field somewhere that gets consumed by a variety of different methods. If anything removes an element from _someList, you're prone to have the problem you're reporting.
See if this helps:
var list = _someList.ToList(); // create a local copy.
var balances = GetCashBalances(list);