Some characters in WCF REST parameter from UriTemplate are HTML encoded - c#

I have the following Web Service interface (a WCF service) method defined:
[WebInvoke(Method = "POST", UriTemplate = "sites/{siteId}/storage/{*filePath}", BodyStyle = WebMessageBodyStyle.Bare)]
[OperationContract]
FileUploadResultDto UploadMultipart(Stream fileData, string siteId, string filePath);
If I do a HTTP POST request to: "(host)/sites/1234/storage/%D0%BC%D0%B0%D0%BC%D0%B0.html" (this is the URL-encoded version of "мама.html")
Parameter filePath is read as "мама.html", which is wrong.
I'd like it to be directly read as "мама.html".
However, "м" is the html-encoded version of the "м" character.
Why is WCF/UriTemplate behaving this way? How can I directly obtain a direct representation of the string, and not have some of the character html-encoded? Is there a setting in web.config for this? Is it a bug?

Related

Error Deserializing WCF POST Request With Multiple Parameters

i did search before posting this question and none of the answers helped me
i am hosting a WCF web service on IIS and using GET and POST in it successfully. the error is only showing when calling a POST web method with multiple param
Example:
**//this fails**
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, UriTemplate = "test", BodyStyle = WebMessageBodyStyle.WrappedRequest)]
String test(String p, String credential);
**//this works**
[OperationContract]
[WebInvoke(Method = "POST",UriTemplate = "test")]
String test(String p);
the code behind simply returns a string for testing
I am receiving the following error:
The server encountered an error processing the request. The exception
message is 'Error in deserializing body of request message for
operation 'test'. The OperationFormatter could not deserialize any
information from the Message because the Message is empty (IsEmpty =
true)
i am testing the following web methods on POSTMAN
Parameters passed are random string:
p: tt
credetial: ghdhdj
WCF doesn’t support form-data by default, we merely pass the parameter by using the Wrapper element when we use Http_Post request.
As you mentioned, we should use the below code to wrap the multiple parameters.
[OperationContract]
[WebInvoke(BodyStyle =WebMessageBodyStyle.Wrapped)]
string GetData(string value,string value2);
And then we can pass the parameters with JSON format.
{"value":"ab","value2":"cd"}
Screenshot.
Feel free to let me know if the problem still exists.

WCF Rest GET with multiple parameters

I have a WCF Rest Service and an OperationContract defined in the following way:
[ServiceContract]
public interface IMyService {
[OperationContract]
[WebInvoke( Method = "GET", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "get/{filename}" )]
MyFileStructure GetFile( string filename );
}
The Server stores some files and the GetFile() retrieves some information from the specified file in the GET request and put them in a MyFileStructure instance which is then JSON parsed and returned back to the client.
E.g. the user can get data from the file named myfile.xml, just typing the following on the browser: http://localhost:[port]/MyService.svc/get/myfile
(the file extension is always ".xml").
This works fine.
The question:
I now need to customize this request a bit more, passing some parameters to my file request. Basically, each stored XML file contains a set of attributes, which can be different (in attribute names and size) from file to file. Please notice that a domain of these attributes does not exist. Each parameter in the request should refer to a specific attribute of the specified file, but since the attributes defined in the file are not known, I cannot use the following:
[OperationContract]
[WebInvoke( Method = "GET", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped, UriTemplate = "get/{filename}?param1={param1}" )]
MyFileStructure GetFile( string filename );
I would like to be able to pass my parameter list in a standard GET request, e.g.:
http://localhost:[port]/MyService.svc/get/myfile?myparam1=value1&myparam2_value2&...
but, at the same time being able to receive that as a single string/whatever object on the server side, in order to process the parameters based on the selected file. Is that possible? How?
You can receive a json object with any parameters you want or a key value pair object, like a dictionary, as it follows:
MyFileStructure GetFile( string filename, [FromBody]IDictionary<string, string> params);
And in javascript:
var parms = {};
parms['param1'] = "one";
parms['param2'] = "two";
$.ajax({
url: '/api/GetFile?filename=' + filename,
data: JSON.stringify(parms),
contentType: 'application/json'
type: 'POST',
dataType: 'json'
});
Hope it helps.
From the question I didn't understand if it is critical to use only GET requests or not.
But if it is, one more options could be usage of http headers. You can pass any header you want from the client, do then a GET request and then access these header in your service using weboperationcontext.current.incomingrequest.headers.
You can check here if some header specified and run some specific code for it, otherwise check another header and so on.
And yes WebAPI, is a kind of symbiosis of WCF and ASP.NET which is preferably now for RESTful API development. But you can use headers there as well.
I think it is not a good idea to use GET Http verb for method which takes a multiple parameters as input data. In this case I would recommend to use POST Http Verb. Something like that:
[OperationContract]
[WebInvoke( Method = "POST", ResponseFormat = WebMessageFormat.Json, BodyStyle = **WebMessageBodyStyle.WrappedRequest**,
UriTemplate = "/getFiles" )]
MyFileStructure GetFile( List<string> filenames );
It will be a little bit comfortable to use and it also has a serious advantages over sending data via Get request such as safety. If you will have a lot of parameters you may lost some of them because of max length of url (which equal to 2000 characters). Using post request you can avoid this problem
My best bet is use a post request to do it. You can use Stream as parameter in method and which will gain you to read and save file stream and other data also.

WCF “At most one body parameter can be serialized without wrapper elements”

I'm trying to consume my WCF webservice via an MVC Web Application. I add a service reference via the Add service reference dialogue in VS. Now when I try to call my proxy.LoginUser I get the error message:
Operation 'GetAlertNotification' of contract 'IService1' specifies multiple request body parameters to be serialized without any wrapper elements. At most one body parameter can be serialized without wrapper elements. Either remove the extra body parameters or set the BodyStyle property on the WebGetAttribute/WebInvokeAttribute to Wrapped.
The only problem is there is no body parameters in GetAlertNotification?? If I understand correctly if I pass all parameters via the query string this should not be an issue? Here is the signature of GetAlertNotification in IService:
[OperationContract]
[WebInvoke(Method = "GET", UriTemplate = "/GetAlertNotification?project={project}&currentversion={currentversion}&EncryptData={EncryptData}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
AlertNotification GetAlertNotification(string project, string currentversion, bool EncryptData = false);
Edit:
I replaced WebInvoke by WebGet and still get the error message above. How can it say it has multiple request body parameters with everything specified in the UriTemplate? I'm I not understanding what the error is saying? Anynone?

WCF Rest Change name of root return element

I have
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Xml,
RequestFormat = WebMessageFormat.Xml,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "projects/{projectname}")]
[return: MessageParameter(Name = "ProjectId")]
Guid CreateProject(String projectName);
But this still returns
<guid
xmlns="http://schemas.microsoft.com/2003/10/Serialization/">00000000-0000-0000-0000-00000000</guid>
How do I replace "guid" with ProjectId?
public Guid CreateProject(String projectName)
{
return Guid.Empty;
}
If I change the OperationContract BodyStyle to WrappedResponse I get:
<CreateProjectResponse
xmlns="http://tempuri.org/">
<ProjectId>00000000-0000-0000-0000-000000000</ProjectId>
</CreateProjectResponse>
Which is almost what I want, but I don't want unnecessarily wrapped.
You become what you have defined. You have defined to receive empty GUID in XML form and it returns to you empty GUID in XML. All is right.
Maybe you need convert GUID to some kind of string id, then Guid.NewGuid().ToString("N")
When you aren't expecting xml then you need to fit attributes and use HTTP Accept Header from client, for example for Json Accept: application/json, or string - plain/text
UPD:
Now a little bit clear. Your actually ask how to change XML structure. I recommend you for standard types use standard XML structure because you already have implemented from the box XML formatters. Anyway when you need to change formatters, you can do it by extending the WebHttpBehavior and overriding the WebHttpBehavior.GetReplyDispatchFormatter method to return our own custom implementation of System.ServiceModel.Dispatcher.IDispatchFormatter (as example read here)
I want also just mention, that you are using WCF 4 REST, and this technology is legacy. When you are dealing not with legacy project or maintenance, then I recommend you to use ASP.NET Web API, because this and many other things could be done there much more easier.
Easiest solution is to
return new XElement("ProjectId", Guid.Empty);
Not really what I wanted, but it works.

Why does this simple RESTful WCF Service fail with a 404 error?

Using this tutorial:
http://www.codeproject.com/Articles/105273/Create-RESTful-WCF-Service-API-Step-By-Step-Guide
I can get a Directory Listing to display when I run the app from VS 2010.
I can paste the URL that is displayed in the IE Browser that VS invokes (http://localhost:4841/) into an outside-of-VS instance of IE and see the same thing. However, if I append "/xml/123" or "/json/123" as shown in the tutorial, so that the URI is "http://localhost:4841/xml/123" or "http://localhost:4841/json/123", I get:
*Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /json/123/
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.272*
If I append "RestServiceImpl.svc" to the URI, so that it is "http://localhost:4841/RestServiceImpl.svc" I get the "Service" page ("This is a Windows© Communication Foundation service. Metadata publishing for this service is currently disabled."); but appending "/xml/123" or "/json/123" leads to the same 404.
The most pertinent code (taken straight from the 5-star tutorial) is:
[ServiceContract]
public interface IRestServiceImpl
{
[OperationContract]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Xml,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "xml/{id}")]
string XMLData(string id);
[OperationContract]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "json/{id}")]
string JSONData(string id);
}
I think you're missing the actual service, eg. RestServiceImpl.svc
So it would be something like:
http://localhost:35798/RestServiceImpl.svc/xml/123
Okay, I solved the dilemma by replacing, in Web.config, this:
<service name="RestService.RestServiceImpl"
...with this:
<service name="REST_JSON_POC.RestServiceImpl"
(and the other place where "RestService" appears with "REST_JSON_POC")
("RestService" is what the tutorial had, but REST_JSON_POC is the name of my project)
But now I have another problem, for which I will create a new question (the "xml/123" now works fine, but "json/123" prompts to download a file, named "123" which has the expected string in it).

Categories