Best way to create C# restful (webAPI or wcf) which can get complex object and return another complex object in GET method?
my request:
data: {
"Code":value,//Numerical Value
"vCode":value,//String Value
"arr":[{"bCode": value},{"bCode": value},...]
}
and my response:
data: {
"Code"://value,"vCode": //value,
"Arr":[{"doc":{"tCode": 1,"cCode":1,"Type": 1,"Value": value},....], "FA":value,
"VN":value}
This following reference may be useful for Creating and Implementing WCF REST Service REST services
http://www.compilemode.com/2015/10/creating-wcf-rest-service-step-by-step.html
Related
I'm trying the built-in Authentication of ServiceStack. My approach is 'OrmLiteAuthRepository' so users' information are stored in Sql Server instead of the default in memory storage. I use Postman to test the endpoints.
My target is receiving user rows, updating user information, creating users, deleting an user row. Those are the endpoints I found in Postman after importing (I didn't create those endpoints):
GET 'http://localhost:47391/api/register',
PUT 'http://localhost:47391/api/json/reply/Register'
POST 'http://localhost:47391/api/json/reply/Register'
I tested POST, Sql Server automatically created the tables to store user data. And the data could be written into Sql Server so I have no problem with POST.
But with PUT, isn't it for updating the existing row? I append '/{id}' to the end. But it created a new row in the database instead of updating the existing one. How does it work?
With GET, I got no implementation error.
{
"ResponseStatus": {
"ErrorCode": "NotImplementedException",
"Message": "Could not find method named Get(Register) or Any(Register) on Service RegisterService",
"StackTrace": " at ServiceStack.Host.ServiceExec`1.Execute(IRequest request, Object instance, Object requestDto, String requestName)\r\n at ServiceStack.Host.ServiceRequestExec`2.Execute(IRequest requestContext, Object instance, Object request)\r\n at ServiceStack.Host.ServiceController.<>c__DisplayClass11.<>c__DisplayClass13.<RegisterServiceExecutor>b__10(IRequest reqCtx, Object req)\r\n at ServiceStack.Host.ServiceController.ManagedServiceExec(ServiceExecFn serviceExec, IService service, IRequest request, Object requestDto)\r\n at ServiceStack.Host.ServiceController.<>c__DisplayClass11.<RegisterServiceExecutor>b__f(IRequest requestContext, Object dto)\r\n at ServiceStack.Host.ServiceController.Execute(Object requestDto, IRequest req)\r\n at ServiceStack.HostContext.ExecuteService(Object request, IRequest httpReq)\r\n at ServiceStack.Host.RestHandler.GetResponse(IRequest request, Object requestDto)\r\n at ServiceStack.Host.RestHandler.ProcessRequestAsync(IRequest httpReq, IResponse httpRes, String operationName)"
}
}
How to implement it? I assume I consider the user a normal Web Service entity? and create 'UserService', and requests like:
[Route("/register")]
public class User : IReturn<UserResponse>
{
...
}
BUT there isn't a model class like 'User' due to the tables are created by ServiceStack itself, how to solve this?
Or is there something I am not aware of. Thanks.
The error message:
Could not find method named Get(Register) or Any(Register) on Service RegisterService
Is saying you're trying to call the built-in ServiceStack Register Service instead of your Service. But the Register Services isn't enabled by default, your AuthFeature likely explicitly enables it, either with:
Plugins.Add(new RegistrationFeature());
Or on the AuthFeature:
Plugins.Add(new AuthFeature(...) {
IncludeRegistrationService = true
});
If you don't want to enable ServiceStack's built-in Register Service you'll need to remove the registration where it's enabled.
If you instead want the Register Service registered at a different path, you can specify a different route with:
Plugins.Add(new RegistrationFeature {
AtRestPath = "/servicestack-register"
});
We have some custom collections such as this:
[Serializable]
public class OccupationCollection : Collection<Occupation>
{
}
We use these in objects like the following:
private OccupationCollection _occupations;
public OccupationCollection CurrentOccupations
{
get
{
if (this._occupations == null)
return new OccupationCollection();
else
return _occupations;
}
}
Now we are making a call to a WCF service, passing objects that contain these type of lists. The lists always end up being null in the service.
I'm pretty sure this has somthing to do with serialization or something like that.
What would the simplest solution that would require minimal changes to the existing objects to get this to work?
Have you hosted your service over HTTP?
If yes, can you use fiddler to check the HTTP traffic and confirm whether serialized version of the parameter is being sent across the wire? If yes, there can be a parameter mismatch in contract between server and client.
Also is the object holding OccupationCollection decorated with Serializable/DataContract attribute? If you have DataContract attribute, ensure that the properties that need to be serialized are marked with Datamember attribute.
More details out here..
http://blog.functionalfun.net/2009/09/if-your-wcf-service-is-unexpectedly.html
I have web services written on WCF. I use request/response pattern and don't use FaultException. I return an error code in response contract as string. I need to expose error codes for clients in order to clients can handle exceptions.
For example:
Var r = client.DoSomething();
Switch (r.ErrorCode)
{
Case ERROR_CODES.SomeCode:
//TODO:
}
Clients are WS-*, not only .Net.
UPDATE:
Sorry, my English is elementary. I've tried to explain in a different way.
When I use class File, I know that this class can throws some exceptions, for example, FileNotFoundException or DirectoryNotFoundException. If I create a File service How can I tell client that my service can returns "FileNotFound" or other error codes?
We generally try and use FaultContracts.
When we cannot we use a Response object that inherits from ResponseBase. ResponseBase has 2 properties, StatusCode and StatusMessage.
In your case ErrorCode, just add this property to your data contract.
I'm logging using log4net, and I want to log a id that is unique for each serice method call. I dont need it unique across service calls, just within a method call. Is there any built in id i can use in wcf? I don't want to manually create a guid or something at the start of the method call.
e.g.
wcfMethod(int x)
{
log("xxx");
somework
log("yyy");
}
private log(string message)
{
var frame = new StackFrame(1);
var method = frame.GetMethod();
var type = method.DeclaringType;
var name = method.Name;
var log = LogManager.GetLogger(type);
// LOG ID HERE
ThreadContext.Properties["MessageId"] = OperationContext.Current.IncomingMessageHeaders.MessageId; // SOMETHING HERE
}
I've tried OperationContext.Current.IncomingMessageHeaders.MessageId but thats always null.
I've read about wcf instance correlation but i don't need something that complicated (e.g. unique across different method calls).
Please if anyone can help that would be much apprieciated. Thanks in advance.
Plain SOAP or REST has no such identification included in messages. You must use some additional feature or transport protocol (for example MSMQ) supporting identifications of messages. In case of MessageId you have to use SOAP service with WS-Addressing and this information must be passed from client.
I tried using updatelistitems web service of the sharepoint. but could not find how to give the input data in the xml format along with the soap request.
Thanks in advance
Since it's an integration you are doing I'd recommend using an ADO.NET adapter for SharePoint and connect through a WCF service (soap/wsdl). It will save you a lot of time and if done correctly your integration wont be proprietary.
Check this ready-made wcf service, http://www.bendsoft.com/downloads/camelot-wcf-service/, installation instructions here http://blog.bendsoft.com/category/integrations/wcf-services/.
It's open source but ships with support for the Camelot XML format, which bundles the schema along with the content if you query for data, check the example schema here http://www.bendsoft.com/downloads/sharepoint-web-parts/xml-pusher/.
To insert data into SharePoint with the WCF service you can simply do something like this
$SharePointNonQuery = new SharePointNonQuery(array(
'sql' => "INSERT INTO contactform (title,email,company,message) VALUES ('John Doe','john.doe#example.com','Johns Company','A test message!')",
'method' => 'ExecuteNonQuery',
'connString' => 'sharepoint_connection',
'sharedKey' => constant("WSDL_SHARED_KEY")
));
The example is obviously made in PHP ( http://blog.bendsoft.com/2011/04/camelot-php-tools-1-1-for-sharepoint-released/ ) but it's equally easy to create a class in Objective-C and send your command as SQL via SOAP and execute the SQL command in the WCF service.
Hope this helps!
----------- Edits below this line -----------
Querying the suggested WCF service from Objective-C would result in something like this
WSMethodInvocationRef soapReq = createSOAPRequest(url, method, namespace, params, paramOrder, reqHeaders);
The Url is the location of the wcf service, ie. http://yourserver.com/wcf/camelot.wcf
The method is the method IN the wcf service you want to use. The Camelot WCF service have a few default methods. Suitable in this scenario would be the ExecuteNonQuery method which takes the following arguments; sql, connString and sharedKey.
bool ExecuteNonQuery(string sql, string connString, string sharedKey);
The params is the arguments listed above, they should be sent as an associative array (NSDictionary I assume).
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
#"INSERT INTO YourList (title,email,company,message) VALUES ('John Doe','john.doe#example.com','Johns Company','A test message!')", #"sql",
#"connString", #"SharePointConnectionString",
#"sharedKey", #"YourPreferredKey",
nil];
The ExecuteNonQuery is a bool method it will return true or false to the soapReq method in the Objective-C application.