I've just started playing with the REST starter kit, and I've hit a road block trying to build my own service. I'm trying to create a service for account management, and I can't get the service to serialize my objects, throwing the following error:
Unable to deserialize XML body with root name 'CreateAccount' and root namespace '' (for operation 'CreateAccount' and contract ('Service', 'http://tempuri.org/')) using DataContractSerializer. Ensure that the type corresponding to the XML is added to the known types collection of the service.
Here's the actual code for the service (based off of the 'DoWork' method that came with the project):
[WebHelp(Comment = "Creates a Membership account")]
[WebInvoke(UriTemplate = "CreateAccount", RequestFormat = WebMessageFormat.Xml)]
[OperationContract]
public ServiceResponse CreateAccount(CreateAccount request)
{
try
{
// do stuff
return new ServiceResponse()
{
Status = "SUCCESS",
ErrorMessage = ""
};
}
catch (Exception ex)
{
return new ServiceResponse()
{
Status = "ERROR",
ErrorMessage = ex.Message + "\n\n" + ex.StackTrace
};
}
}
And last, but not least, here's the object that's causing all the trouble:
public class CreateAccount
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public bool SignUpForNewsletter { get; set; }
public string Password { get; set; }
}
Am I missing anything stupid?
Thanks in advance!
It turns out I was missing an extra value in the [DataContract] attribute on the business object.
Should be [DataContract(Namespace = "")]
It appears the problem is a namespace clash between your method name "CreateAccount" and your input type "CreateAccount".
Also, you have to mark your CreateAccount type as a DataContract like so:
[DataContract]
public CreateAccount
{
[DataMember]
public string LastName { get; set; }
...
}
If you want to keep the same name, you can specify a namespace for the CreateAccount class.
I noticed you have a return type as well. Ensure the return type is marked with the DataContract attribute as well. Also, specify the return format like so:
ResponseFormat = WebMessageFormat.Xml
If you don't have it already, I think a [DataContract] attribute above your CreatAccount class.
I had a similar problem, but I did have the DataContract attribute. What I was missing though was the xmlns="http://uri.org" attribute from the root element when trying to read the xml back into the object.
e.g.
<Root_Element xmlns="http://uri.org"><Child_Element/>...</Root_Element>
Related
I receive some jsons that are supposed to represent a legal object of some class.
I wish to validate this is indeed so. So I deserialized the strings to see if this succeeds. This is very time consuming as the strings gets very large in some cases and there are many of them. I am therefore looking for a different approach.
I thought of creating a regExp from the class's definition and check that the jsons I receive are compatible. Is there a way to generate a regExp from a C# class?
Any other suggestions will help as well.
Thanks
Use JSON schema validator by newtonsoft , more details here
public class JsonSchemaController : ApiController
{
[HttpPost]
[Route("api/jsonschema/validate")]
public ValidateResponse Valiate(ValidateRequest request)
{
// load schema
JSchema schema = JSchema.Parse(request.Schema);
JToken json = JToken.Parse(request.Json);
// validate json
IList<ValidationError> errors;
bool valid = json.IsValid(schema, out errors);
// return error messages and line info to the browser
return new ValidateResponse
{
Valid = valid,
Errors = errors
};
}
}
public class ValidateRequest
{
public string Json { get; set; }
public string Schema { get; set; }
}
public class ValidateResponse
{
public bool Valid { get; set; }
public IList<ValidationError> Errors { get; set; }
}
I am writing an application where I do need to handle some scenarios beforehand in my controller class like certain property must have been provided otherwise status code would be BadRequest. Here is my class lookalike.
public class MyClass
{
[Required]
public IEnumerable<NewObject> NewObjects { get; set; }
}
public class NewObject : INewObject
{
public NewObject(string typeName, IEnumerable<Property> properties)
{
TypeName = typeName;
Properties = properties;
}
[JsonProperty(Required = Required.Always)]
public string TypeName { get; }
public IEnumerable<IProperty> Properties { get; }
}
public interface IProperty
{
string Name { get; }
object Value { get; }
}
Now though I have marked TypeName as required property and if I do not pass that in json content while sending request from swagger, json deserialization doesn't fail. I tried to search but I got an answer that setting Required to Always should work.
Below is the Json Content I am passing through swagger:
{
"NewObjects": [
{
"Properties": [
{
"Name": "string",
"Value": ''
}
]
}
]
}
I wrote below piece of code too by looking at one of the solution:
var config = new HttpConfiguration();
var jsonFormatter = config.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Error;
config.MapHttpAttributeRoutes();
Still it's not working:
Note: I am using Newtonsoft.Json version 11.0.1
This seems to be swagger issue because when I serialize input C# object and when again deserialize it, I am getting proper error.
For example in my controller class if I say:
var input2 = JsonConvert.DeserializeObject<MyClass>(JsonConvert.SerializeObject(input))
Then input2 throws an exception.
You can take a look at FluentValidation. If I am not mistaken it is designed to validate data in jsons forms specifically.
using FluentValidation;
public CertainActionValidator()
{
RuleFor(x => x.PropertyName).NotEmpty()
}
You can add plenty of additional conditions in there.
Here: Recommended ServiceStack API Structure and here: https://github.com/ServiceStack/ServiceStack/wiki/Physical-project-structure are recommendations for how to structure your projects for C# clients to reuse DTOs.
Apparently this is done by including a dll of the DTO assembly. I have searched the web for one example, just Hello World that uses a separate assembly DTO for a C# client in ServiceStack. Perhaps I should be able to break this out myself but so far it has not proven that easy.
Almost all client descriptions are for generic and non-typed JSON or other non-DTO based clients. No one appears interested in typed C# clients like I am (even the ServiceStack documentation I have found). So I thought this would be a good question even if I figure it out myself in the end.
To be clear, I have built and run the Hello World example server. I have also used a browser to attach to the server and interact with it. I have also created a client empty project that can call
JsonServiceClient client = new JsonServiceClient(myURL);
Then I tried to copy over my DTO definition without the assembly DLL as I don't have one. I get ResponseStatus is undefined.
Clearly there is something missing (it appears to be defined in ServiceStack.Interfaces.dll) and if I could create a dll of the DTO I think it would resolve all references.
Can anyone give insight into how to create the DTO assembly for the simple Hello World?
Edited to add code:
using ServiceStack.ServiceClient.Web;
namespace TestServiceStack
{
class HelloClient
{ public class HelloResponse
{
public string Result { get; set; }
public ResponseStatus ResponseStatus { get; set; } //Where Exceptions get auto-serialized
}
//Request DTO
public class Hello
{
public string Name { get; set; }
}
HelloResponse response = client.Get(new Hello { Name = "World!" });
}
}
Where the ResponceStatus is undefined.
I was able to find the missing symbol ResponseStatus by adding:
using ServiceStack.ServiceInterface.ServiceModel;
Here is the full code that built. Keep in mind that I found out something else in the process. Once this built it then failed because I was using a DTO from a .NET 4.0 environment in a .NET 3.5 environment. But that is an unrelated issue. Also note that this test code does nothing with the response, it is just an example to get the build working.
using ServiceStack.ServiceClient;
using ServiceStack.ServiceInterface;
using ServiceStack.Text;
using ServiceStack.Service;
using ServiceStack.ServiceHost;
using ServiceStack.WebHost;
using ServiceStack;
using ServiceStack.ServiceClient.Web;
using RestTestRoot; // This is the name of my DTO assembly. You will need to insert your own here.
using ServiceStack.ServiceInterface.ServiceModel;
namespace WebApplicationRoot
{
class HelloClient
{
JsonServiceClient hello_client;
//Request DTO
public class Hello
{
public string Name { get; set; }
}
//Response DTO
public class HelloResponse
{
public string Result { get; set; }
public ResponseStatus ResponseStatus { get; set; } //Where Exceptions get auto-serialized
}
//Can be called via any endpoint or format, see: http://mono.servicestack.net/ServiceStack.Hello/
public class HelloService : Service
{
public object Any(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
//REST Resource DTO
[Route("/todos")]
[Route("/todos/{Ids}")]
public class Todos : IReturn<List<Todo>>
{
public long[] Ids { get; set; }
public Todos(params long[] ids)
{
this.Ids = ids;
}
}
[Route("/todos", "POST")]
[Route("/todos/{Id}", "PUT")]
public class Todo : IReturn<Todo>
{
public long Id { get; set; }
public string Content { get; set; }
public int Order { get; set; }
public bool Done { get; set; }
}
public HelloClient(){
// ServiceStack gateway = new ServiceStack.ClientGateway(
// location.protocol + "//" + location.host + '/ServiceStack.Examples.Host.Web/ServiceStack/');
hello_client = new JsonServiceClient("http://tradetree2.dnsapi.info:8080/");
hello_client.Get<HelloResponse>("/hello/MyTestWorld!");
}
}
}
I have a WCF Service that based on Writing Highly Maintainable WCF Services. Requests are processed using a CommandService:
[WcfDispatchBehaviour]
[ServiceContract(Namespace="http://somewhere.co.nz/NapaWcfService/2013/11")]
[ServiceKnownType("GetKnownTypes")]
public class CommandService
{
[OperationContract]
public object Execute(dynamic command)
{
Type commandHandlerType = typeof(ICommandHandler<>).MakeGenericType(command.GetType());
dynamic commandHandler = BootStrapper.GetInstance(commandHandlerType);
commandHandler.Handle(command);
return command;
}
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
{
var coreAssembly = typeof(ICommandHandler<>).Assembly;
var commandTypes =
from type in coreAssembly.GetExportedTypes()
where type.Name.EndsWith("Command")
select type;
return commandTypes.ToArray();
}
}
Everything works great (thanks Steve) but now I need to add the ability to upload a file to the service. From what I've read and based on errors received during testing, WCF needs to use a [MessageContract] when uploading a file using a Stream. So I've decorated my command class and put the non-Stream members into the message header, and updated my binding definition to use streaming:
[MessageContract]
public class AddScadaTileCommand
{
[MessageHeader(MustUnderstand = true)]
public int JobId { get; set; }
[MessageHeader(MustUnderstand = true)]
public string MimeType { get; set; }
[MessageHeader(MustUnderstand = true)]
public string Name { get; set; }
[MessageBodyMember(Order = 1)]
public Stream Content { get; set; }
}
Unfortunately when I call the service with a file to upload I get an error:
There was an error while trying to serialize parameter
http://somewhere.co.nz/NapaWcfService/2013/11:command. The
InnerException message was 'Type 'System.IO.FileStream' with data
contract name
'FileStream:http://schemas.datacontract.org/2004/07/System.IO' is not
expected.
So I added a new method to the service specifically for the file upload request:
[OperationContract]
public void Upload(AddScadaTileCommand addScadaTileCommand)
{
Type commandHandlerType = typeof(ICommandHandler<>).MakeGenericType(typeof(AddScadaTileCommand));
dynamic commandHandler = BootStrapper.GetInstance(commandHandlerType);
commandHandler.Handle(addScadaTileCommand);
}
This works perfectly, unless I change the AddScadaTileCommand parameter to dynamic in the method definition, in which case I get the same error as above. This appears to indicate that the [MessageContract] attributes are not applied or ignored when using dynamic as the type of the parameter. Is there any way to resolve this or will I need to create separate methods for requests that involve streams?
I'm writing a WCF webservice and passing in a complex type as a parameter of the method. The complex type looks like this:
[DataContract(Namespace = "")]
public class MyRequest
{
[DataMember()]
public string TransactionId { get; set; }
[DataMember(IsRequired = true)]
public bool IsRollback { get; set; }
[DataMember(IsRequired = true)]
public OrderType OrderType { get; set; }
[DataMember(IsRequired = true)]
public ICustomerId CustomerId { get; set; }
[DataMember()]
public long OrderId { get; set; }
[DataMember()]
public AnotherComplexType PurchaseInfo { get; set; }
The webservice method looks like this:
[ServiceKnownType(typeof(CustomerIdByName))]
[ServiceKnownType(typeof(CustomerIdByAccount))]
public OrderResult Execute(MyRequest order) {
}
The Interface looks like this:
[KnownType(typeof(CustomerIdByAccount))]
[KnownType(typeof(CustomerIdByName))]
public interface ICustomerId{
string GetId();
}
When I make a request using the SOAP end point, everything works just great. But when passing the request to the REST end point, I get the serialization error.
This is the request I'm using
<MyRequest>
<CustomerId>
<AccountId>59251</AccountId>
</CustomerId>
<IsRollback>false</IsRollback>
<OrderId>0</OrderId>
<OrderType>OrderSubscription</OrderType>
<PurchaseInfo>
<ObjectId>196521</ObjectId>
</PurchaseInfo>
<TransactionId>ABC123</TransactionGuid>
</MyRequest>
Since I had been stuck at this point for too long, I then changed the ICustomerId member to be an abstract class that implements ICustomerId. Again the SOAP end point works fine but sending the request to the rest end point I get an error that states "Cannot create abstract class"
What am I missing or doing wrong here?
Is this failing because the interface is nested in the complex type and not a direct parameter of the webservice method? I've used webservices that receive interfaces as parameter and with the KnownType decorators they work just fine. Same question applies to the abstract class, is this not working because the abstract class is nested within a member of the MyRequest complex type?
This is the error message I am getting:
Element CustomerId from namespace cannot have child contents to be deserialized as an object. Please use XmlNode[] to deserialize this pattern of XML
Have you tried decorating your interface as a RESTful method?
[ServiceContract]
public interface IMyRequest
{
[OperationContract]
[WebInvoke(
UriTemplate = "Requests/GetID",
Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped)]
string GetId(MyRequest myRequest);
...
Also:
make sure that the [DataMember] properties match your request payload. Everything that gets passed in your XML request payload must be caught by the serializer in the service. I recommend keeping the naming the same, but you can map it using [DataMember(name="MyProperty")]. Also, your [DataContract] must be mapped to match the name of the parent node of your XML payload like [DataContract(Name="MyRequest")] but only if the class is named differently than the xml node. Do this, and it will deserialize your xml into the server side object/dto
The error you're getting sounds like it's complaining about the complex type inside of your DataContract. Your complex type needs to be decorated for serialization the same as your MyRequest type.
Also ensure your REST endpoint is bound to webHttpBinding