Adding parameter to Restful service POST without breaking clients - c#

I have a rest web service, which suppose has a method
int foo(Mytype x)
it has many clients, i wanted to add a parameter to to Mytype and change how foo behaves, As this is a restful service so all clients also implement there version of Mytype any ideas how to accomplish this without breaking any clients?

Have u considered versioning? E.g. server/ver2/foo
I think your only other option would be to implement the new functionality, such that it is not required, e.g.
foo (MyType myType) {
if (myType.newField exists) {
// do new stuff
}
}
One final note, i would recommend combining both approaches, which will ensure that foo is backwards compatible.

I think the safest way would be to go with versioning.
You could provide a new version for MyType which can be handled using the MIME type description in the Accept and Content-Type headers, i.e. like so:
application/json+foo;application,v=2
Another possibility would be to handle the version via the URI/URL, i.e.
https://api.yourhost.com/v2/MyType
Like Damo already said you then have to handle the use of the different resource representations on the server.

I added a int to my POST request and it didn't break anything. So looks like adding a parameter to rest service request class is not really "Breaking Changes" but removing one will certainly break the clients.

Related

Restrict Metadata for Custom Content-Type in ServiceStack

Have a few custom content-types registered via
ContentTypeFilters.Register(contentType, StreamSerializer, StreamDeserializer);
and would like to restrict the display for routes on the metadata page. These content-types are only meant to be used with request dto's which are restricted to InternalNetworkAccess. Just looking to not clutter up the public facing metadata page with stuff that isn't necessary.
For the builtin contentTypes you would just add it under the RestrictAttribute. Is there a similar feature hidden somewhere else that isn't documented yet maybe for the custom types?
It looks like perhaps I could customize the MetadataFeature plugin and possibly restrict which request dto's get the content type and which don't. But I only just recently noticed that, and not sure how well that would turn out (also don't really know yet how to remove the MetadataFeature and safely replace with my own).
Essentially I only want this custom contentType visible on the metadata page for the requestDtos restricted to InternalNetworkAccess.
Any ideas?
Edit:
Also am still on ServiceStack v3, but still interested in possibilities for v4.
You can prevent your custom type from showing up in the metadata using.
If your content type is application/yourformat you would use:
SetConfig(new HostConfig {
IgnoreFormatsInMetadata = new HashSet<string>{ "yourformat" }
});
So I found that the initial class handling the metadata requests was ServiceStack.MetadataFeature an IPlugin. This actually controls both the layout of the underlying example request/response page (for each content-type) as well as the overall "/metadata" page.
From this small segment
private IHttpHandler GetHandlerForPathParts(String[] pathParts)
{
var pathController = string.Intern(pathParts[0].ToLower());
if (pathParts.Length == 1)
{
if (pathController == "metadata")
return new IndexMetadataHandler();
return null;
}
...
}
is where the handler for the actual "/metadata" page is sent off. You don't find the actual construction of the ContentTypes per request until you get down a little further, inside IndexMetadataHandler's parent class BaseSoapMetadataHandler in the method
protected override void RenderOperations(HtmlTextWriter writer, IHttpRequest httpReq, ServiceMetadata metadata)
An internal control is created (IndexOperationsControl) which has a method RenderRow, which is where all the magic occurs. Here you'll see some obvious checks for the "Operation" (which is another word for the Dto now) and ContentType like
if (this.MetadataConfig.IsVisible(this.HttpRequest, EndpointAttributesExtensions.ToFormat(config.Format), operation))
So all that needs to be done is create your own class of IndexOperationsControl and handle the config.Format in the RenderRow method. The config.Format is simply everything after the forward slash in the ContentType you registered, so if it was "application/x-my-type" the config.Format String will be "x-my-type". Operation is simply the class name of the RequestDto. Unfortunately because the class is marked internal it means you pretty much have to copy it completely instead of using inheritance. In order to keep a 1:1 likeness with how the pages are generated by default you'll also need a copy of the internal classes ListTemplate, TableTemplate, and XsdTypes (used in construction of IndexOperationsControl).
After this you simply need your own IndexMetadataHandler and overload RenderOperations (you can use inheritance for this one) to create your new IndexOperationsControl. Also we'll need our own MetadataFeature equivalent IPlugin but we'll need to copy it completely and modify GetHandlerForPathParts to return our new IndexMetadataHandler. The only other thing to do is remove MetadataFeature and add our own as a plugin.
// removing default metadata feature
Plugins.RemoveAll(x => x is MetadataFeature);
// add our custom one
Plugins.Add(new CustomMetadataFeature());
Voila, you can display custom ContentTypes exactly how you want per RequestDto.

Updating Code, Find All Objects Of Type

Task:
Rip through all the code in the entire solution and wrap all webservice method-calls in another ws method-call that accepts a GUID (it's a login scenario)
Background :
Hundreds of web services, add token security. As explained to me when I was assigned to the task, we do it this way because if, in the future , some changes to security etc have to be made we can just do it in the WrappermethodClass in stead of having to change hundreds of web services
Tried and failed :
Find all references : too much data , returned more than 1000 hits , most of which are useless as they're only object references.
Rename WS so all references beak, build the project I'm working on and fix as I go : works well with the services not integral to the functionality but as soon as I do it with an important one it's like I shot the Solution through the brain, everything's f****d and and VS just gives up trying.
Current Solution :Open all relevant docs, Find ,select All Open Docs, skip through.
Question : How do I do this as efficiently as possible?
Code (before) :
wsGeneric wsGen = new wsGeneric();
wsGen.DoSomething();
Code (after) :
WrapperMethodClass.DoCheck takes params of (Action, GUID),
wsGeneric wsGen = new wGeneric();
wrapperMethodClass.DoCheck((g) =>
{ wsGen.UserInfo.token = g.ToString();
wsGen.DoSomething();
},Shell.token.Value);
Don´t you have some sort of interface or class where you changed the method signature already?
If you changed your webservice and your Code still compiles i´d say you did something wrong or i don´t understand the question.
Update:
I still don´t get it.
I think you have these options:
Change the method signature (all calls should be broken now, fix all the errors vs gives you and you should be done)
Find all references (of the method, not your webservice-class) and change the calls
If above isn´t possible use "Find in Files" and search for the method-name
If all your webservices inherit from an interface or base class you can refactor this method to add a parameter, all inheriting classes will also have the parameter.
If you pass a login object to each webservice, you can add a GUID element to this object and you're done.
It would be a lot easier if you showed us some code, some function interfaces that you have to change and how.
A better solution may be to just use PostSharp to add the checks to your services. This will solve your business problem (you only need to update your aspects) and is much less error prone then your current approach since you don't have to wory about some new developer forgetting to make the call to DoCheck.
Not having to find all references is a side benefit.

When do I need a datacontract in WCF and would it be better to use a simple type?

I am having some trouble designing my WCF service. Bassically I need the service to recieve an XML document. The xml maps to a class that was generated from xsd.exe. I was originally just had this:
public void AddDocument(string xmlString)
Then I would deserialize the xml into the generated class. I was told this is a bad idea because I am doing extra work since wcf will do the serialization for me if I just use the document class as a parameter like this:
public void AddDocument(MyGeneratedClass document)
I'm new to WCF but if I do it this way I thought I would have to create a datacontract for MyGeneratedClass. The generated class is 20,000+ lines so this would take forever.
Do I need a DataContract? Anyway I think I am missing something so I hope this makes sense and if anyone can point me in the right direction I would greatly appreciate it. Thanks!
I would use simple types if your method only requires one or two parameters, and will return only a single simple type value.
As a general rule:
If you need to pass in more than just a few (less than 5) simple types - use some kind of a Request object, otherwise your call gets unwieldy.
If you need to return more than one single simple type value, use a Response object to bundle up those values.
I would try to avoid sending and receiving XML and parse it - try to send back and forth real well structured (data) objects - much easier to deal with and type-safe and all !

Is this a namespacing problem?

I am experiencing a strange behavior with very basic web service development. This question might be dumb but I think someone would be able to explain this observation.
I am developing a web service with a web method, MyWebMethod
MyWebMethod(MyEnum Param, .....)
Where,
public enum MyEnum : int
{
Type_1 =1;
Type_2 =2;
Type_3 =3;
}
Now I am using my client to communicate with this service but for every request type, Type_1, Type_2 etc the service captures it as Type_1. As an example, if I create a break point at MyWebMethod in my web service, I see Type_1 as param1 type. I guess this is a problem with Namespacing. I cannot see any other defects on the code. Any Idea based on the experiences?
When enum is serialized, only its string representation is transferred through wire (names), not the values. I believe thats the reason you are getting the wrong values.
Check out this 2 articles for more info
WebServices_and_Enums
Using enum in web service parameter
If this is a WCF web service and a .NET 2.0 client generated with wsdl.exe for each value type in the method signature there will be a boolean parameter added called XXXSpecified which you need to set to true. Check this blog post for more details.
I guess your enum does not need to inherit from int. You are providing name and value in the enumeration, that should suffice. I am assuming all your code is .NET 2.0. As test , return an enumeration value from the webservice. Just to make sure XML Serialization is working as expected when the service is hit directly by the browser.

Can't transfer list<T> to web service?

I have the same classes on my server and on my web service.
I have the following WebMethod:
[WebMethod]
public int CreateOrder(List<Purchase> p, string username)
{
o.Add(new Order(p,username));
return o.Count;
}
However the following code, run at server:
protected void CartRepeater_ItemCommand(object source, RepeaterCommandEventArgs e)
{
List<Purchase> l = ((List<Purchase>)Session["Cart"]);
if (e.CommandName == "Order")
{
localhost.ValidateService WS = new localhost.ValidateService();
WS.CreateOrder(l, Session["username"].ToString());
}
}
gives the following error: Argument '1': cannot convert from 'System.Collections.Generic.List<Purchase>' to 'localhost.Purchase[]'.
How can I transfer the list<Purchase> object to the web service?
When using web services like that, by default List<T> gets converted into an array (T[]). Convert your list into an array by doing .ToArray() before passing it to the method.
Another option is to change the web service code generation settings to use lists instead of arrays.
It seems you also have duplicate classes, both a local one called Purchase and the one that's generated over the web service, also called Purchase. Even though they have the same name, they're two different types (their namespaces are different). You'll either have to stick to one set of types, or use something like Automapper to map between your two sets of types.
If you're using svcutil to generate the client proxy classes, you can use the collectionType option to force the proxies to use a type other than the default array. This is certainly what gets used for generating proxies to WCF services; I'm not 100% sure if it's used with ASMX services.
Anyway, this is achieved by doing:
svcutil.exe /collectionType:System.Collections.Generic.List`1 [service url]
It is because the webservice uses SOAP to transfer the data, which is an XML protocol.
It knows nothing about .NET lists or many other fancy objects.
So in your case, it is actually transferring an array, and as Matti already said the solution is then simply to use an Array instead.
You can't serialize List<T> into xml, the <T> bit will obviously turn into a badly formed xml tag.
You could make a new object that inherits from List<T>, which will then serialize nicely and go through your web service, this is a minefield of best practice no-nos but you need to compromise sometimes.
localohost.ValidateService is a proxy class, with his own namespaces for classes: then "Order" is not the same as "localhost.Order"
if your calling web service from an other method in ther same web service class,
try this:
tihs.CreateOrder(l, Session["username"].ToString());

Categories

Resources