I have a RESTful WCF service that can return XML, JSON, or JSONP, depending on the arguments, e.g. /service.svc/stuff?format=xml or service.svc/stuff?format=json&callback=myCallback. To do this, I've created a custom Behavior, MethodEncoder and MethodEncoderFactory which handle wrapping the JSONP callback and chooses the writer based on the format argument. In my encoder's WriteMessage() method, I do something like
XmlWriter writer = IsXmlRequested() ? XmlDictionaryWriter.CreateTextWriter(stream) :
JsonReaderWriterFactory.CreateJsonWriter(stream)
message.WriteMessage(writer);
Then, I define my service methods as if they just return JSON but use my custom binding element:
[OperationContract, JSONPBehavior, WebGet(ResponseFormat = WebMessageFormat.Json,
UriTemplate = "stuff")
public List<Thing> GetStuff(){...}
And it almost works. When I ask for XML or JSON, I get something in the right format, but the XML isn't serialized as I expect. Here's what the XML looks like:
<root type="array">
<item type="object">
<FirstPropertyOnAThing>1</FirstPropertyOnAThing>
Whereas if I were to just set the WebMessageFormat to XML, I would get something like this:
<ArrayOfThings xmlns="...>
<Thing ...>
<FirstPropertyOnAThing>1</FirstPropertyOnAThing>
I definitely want the latter. I guess this is happening because the result is serialized to a dictionary when the Message object is created; my custom encoder is just deciding how to write that dictionary to the response stream. So it gets the encoding right, but not exactly the format, which has already been decided by the ResponseFormat.
First, is that right? If so, how can I fix this? For example, can I write my own WebMessageFormat? Or do I just have to give in and write separate methods (and URI templates) that have different ResponseFormat properties for /json/* and /xml/*?
Update: In .net 4, there's a WebOperationContext.Current.OutgoingResponse.Format property you can just set. I guess my issue boils down to: is there a way to accomplish that in .net 3.5?
Yes, there's a way to accomplish what you want in .NET 3.5, without writing separate methods.
This blog post deals with the situation you describe: varying the content-type of the response based in the incoming request. But, the post describes a slightly different approach to the solution.
In particular, the requester specifies the desired content type NOT in the request URL, but rather in the Accept header of the request.
The solution involves the use of a custom WebHttpBehavior that inspects the Accept header and formats the response appropriately. A very elegant solution, in my opinion. There's nothing you have to do in your business logic to get the adaptive formatting. Just attach the behaviour and it works.
Also check out the WCF REST Contrib library on CodePlex.
Related
Maybe this isn't even possible, but it seems silly that I can't figure it out (nor can find anything conclusive after searching).
With a MVC/C# Web API 2 project, your controllers can be documented using something like:
///<summary>
///This is something really cool that you should use. I want <b>this bold</b>.
///</summary>
[HttpPost]
public MyResponse MyMethod(SomeInput input)
{
....
}
When the API runs, the project automatically builds the help site, and I can see the above endpoint/method, and its description ( text), but I've head to figure out how to do any sort of styling to the summary. It appears that the HTML tags get striped from the help page's output. Notice in my example above, I have "this bold". I'm not so much concerned about bold, but more interested in being able to use unordered lists () and other basic HTML tags to just do some real basic formatting.
Is this even possible?
Is there a trick to it?
Is there some other markup/formatting I should be using?
Note - The actual endpoint that I'm trying to document at moment, happens to be a mime multipart form, and the framework won't document those out of the box. To get around this, I've created some helper methods in HelpPageConfigurationExtensions (to determine if the current endpoint view is one that requires custom documentation), in HelpPageApiModel.cshtml to determine if it should show the stock documentation or the custom docs, a helper library that contains the custom doc information, and a series of help functions that use some reflection to rapidly build HTML tables for the rest of the help page's documentation (e.g. the request and response objs). I'm mentioning this because maybe I just need to further extend my custom doc library to include (hard code) the value, and then in the view I can just #Html.Raw it -- opposed to trying to get the actual method's to output with formatting.
Thoughts?
Thanks!
I've been trying to figure this out for about a week now. It's time to ask S.O.
I have 4 overall goals here:
The controller code needs to use ViewModel request inputs for validation reasons. (Controller Snippet)
Client code for my API should use a nice model syntax. (Client Code Snippet)
For the swagger UI page, I would like the "Try me" interface to be usable. Either a bunch of text boxes, or a text area for a json blob to serialize and send over.
GET request
Client Code Snippet:
var response = client.GetUserProductHistory(new Models.UserProductHistoryRequest() {
Locale = "en-US",
UserPuid = "FooBar"
});
Controller Snippet
[HttpGet]
[HasPermission(Permissions.CanViewUserProductHistory)]
public JsonPayload<UserProductHistoryResponse> GetUserProductHistory([FromUri]UserProductHistoryRequest model)
{
JsonPayload<UserProductHistoryResponse> output = new JsonPayload<UserProductHistoryResponse>();
return output;
}
I have tried using [FromBody]. It looks great, but I get an error that says 'GET requests do not support FromBody'.
I tried using [FromUri], but then the generated client gives me like 15 method parameters per call in the generated client.
I tried using [FromUri], and operation filters so that the parameters would be condensed into Ref parameters (complex objects as defined by the spec). This actually worked decently for the client generation and the server side. Problem is, the UI for swagger looks really lame. A single TEXT box that you can't actually use very well. If I can figure out how to get the Swagger UI to change the appearance of the [FromUri] request to more closely match the [FromBody] UI, I will be in good shape here. Any ideas or pre-existing content that would point me in the right direction here?
Swagger is not the limitation - REST itself is. By definition of REST, web servers should ignore the incoming request body on all HTTP GET methods. ASP.NET enforces this convention, which is why you it doesn't allow you to use [FromBody] on the GET method.
When designing a REST API, the better practice is to use POST methods for an actual search. This will allow to use [FromBody], and as a bonus, Swagger will behave the way you want it to. See here for a supporting opinion: https://stackoverflow.com/a/18933902/66101
During WebAPI Response processing, we need to log the response/request body and headers with the security properties being skipped. With Newtonsoft Json, Since the actual response should contain the properties, "Ignore" attribute cannot be placed. We have implemented general filter to handle all Web API Methods that takes excluded property key names as list. Following are approaches tried.
Doing Jobject.Parse and traversing through JTokens and excluding.
Using JsonTextReader and applying conditions while reading.
Both are taking milli seconds time which was not acceptable because its just for logging.
Is there any Optimal way to acheive this?
Create a base class without the security property, use the base class for logging the data and the child one to have everything.
Or create an interface from your class and create a new class with Ignore attribute which you use to log the result.
My services are supposed to parse a SOAP request for an action ILogging/LogMessage which has 'log-entry' as the root element inside the SOAP body. For that, I have a method LogMessage that expects parameter of type LogMessageRequest.
LogMessageRequest has the MessageContract attribute set with WrapperName as log-entry:
[MessageContract(WrapperName = "log-entry")]
public class LogMessageRequest
{
...
}
I am also expecting another SOAP request for an action ILogging/LogException with 'log-entry' as root element in the SOAP body. For this, there's a method LogException and a param of type LogExceptionRequest.
The difference between both the SOAP actions is that a child element 'message' inside 'log-entry' is different (for LogMessage, 'message' is a string and for exception, it's a complex entity).
The issue:
Since both LogMessageRequest and LogExceptionRequest have the same wrapper names (log-entry), I'm getting an exception originating from LogException saying "log-entry has already been exported by LogMessage".
I tried using the same request class for both and have the 'message' of type object. But that refuses to work.
Any pointers? (there's no scope of changing the SOAP request by the way).
While there might be a way to match the different schemas with a common interface, I suggest a more prudent approach: build an adapter over one of the service interfaces to match the interface of the other.
This way, the ugliness is isolated and the application will only have to work with only one service interface.
I don't believe you can implement this using MessageContract/data contract from your description. You might be able to do it with XmlSerializer attributes, but it sounds like you will need to use the Message class in the operation contract and peek at the message xml to figure out which one it is. Can you post the relevant bits of the WSDL/SOAP definitions?
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 !