How do I use the old XmlSerializer with Web API, instead of the DataContractXmlSerializer? - c

I have a Web API site that has, up until now, supported only JSON.
I'm trying to get it to work with XML, and I've run into issues.
There are a couple of things about the DataContract XmlSerializer that bug me. First, it imposes an order on the XML elements, and managing the namespaces is a mess.
It was suggested that I configure my site to use the older XmlSerializer. The code to enable this is supposed to be simple, just set the UseXmlSerializer flag on the XmlFormatter:
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
...
}
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
...
}
}
And it doesn't work.
If I do not set UseXmlSerializer = true:
If I submit a request with "Accept: application/json", I get JSON
If I submit a request with "Accept: application/xml", I get XML
If I do set UseXmlSerializer = true:
If I submit a request with "Accept: application/json", I get JSON
If I submit a request with "Accept: application/xml", I get JSON
Which makes no sense at all.
Something that may be related - we're using Swagger to create API documentation pages:
If I do not set UseXmlSerializer = true:
The Swagger IO Response Content Type dropdown includes "application/xml"
If I do set UseXmlSerializer = true:
The Swagger IO Response Content Type dropdown does not include "application/xml"
Seeing that, I tried explicitly adding support for xml in WebApiConfig.Register:
public static void Register(HttpConfiguration config)
{
config.Formatters.XmlFormatter.UseXmlSerializer = true;
config.Formatters.XmlFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
...
}
This makes no difference. The Swagger UI still does not include the xml media types, and requests that ask for XML still return JSON.

Related

Wildcards in camel route

I have rest controller with request mapping as follows:
#PostMapping(value = "fpl/generate/{legIdentifier:.+}"
My camel route is defined as from("direct:/fpl/generate/").
The controller calls web service, web service calls FluentEndpointInvoker class which calls route defined above.
public class FluentEndpointInvoker {
#EndpointInject(uri = BASE_ENDPOINT_URI)
private FluentProducerTemplate producer;
#Value("${server.servlet.context-path}")
private String contextRoot;
public <T, R> T request(Class<T> type, R request, HttpHeaders headers) {
return producer.withProcessor(exchange -> {
exchange.getIn().setBody(request, request.getClass());
headers.forEach((key, value) -> exchange.getIn().setHeader(key, value));
String endpoint = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()
.getRequestURI();
exchange.getIn().setHeader(ROUTE_ENDPOINT_HEADER, "direct:".concat(endpoint.replaceFirst(contextRoot, "")));
}).request(type);
}
}
The endpoint that is generated is something like direct:///fpl/generate/LH.1234.30Jun2016.FRA.BOG.X1. How can I configured wildcards in camel route so that endpoint can get called
Well, if you are not forced to use an endpoint with the full URI, you could simplify the case.
Instead of creating a dynamic endpoint, you could send all messages to the endpoint direct:///fpl/generate and set the full request URI as header on the message.
That way you have a simple route endpoint to use and the URI header to make decisions etc based on the full URI.

How do I get raw request body using servicestack with content-type set to application/x-www-form-urlencoded?

I have my DTO
Route("/testing","POST")]
public class TestingRequest:IRequiresRequestStream
{
public Stream RequestStream { get; set; }
}
and my service
public async Task<object> Post(TestingRequest req)
{
var rawbody = base.Request.GetRawBody();
var data = req.RequestStream.ReadFully();
var strdata = UTF8Encoding.UTF8.GetString(data);
...
}
And I found that when calling it, rawbody or data is not empty only if content-type is not application/x-www-form-urlencoded. If the content type is application/x-www-form-urlencoded, rawbody and data will be empty.
How do I get the raw request body as a whole (string) when the caller set the content-type to be application/x-www-form-urlencoded?
My current env is ubuntu 16.04 with dotnet core 1.1, don't know if it matters
ServiceStack uses a forward only request stream that unless you tell ServiceStack to buffer the Request Stream with a pre-request filter:
PreRequestFilters.Add((httpReq, httpRes) => {
httpReq.UseBufferedStream = true;
});
Either ServiceStack reads or you can read by having your Request DTOs implement IRequiresRequestStream to tell ServiceStack to skip reading the Request Body and that you're going to in your Services.
application/x-www-form-urlencoded requests are special in that if IRequest.FormData is accessed it will trigger reading the Request Body. You can tell ServiceStack to skip reading the FormData when it creates the Request with:
SetConfig(new HostConfig {
SkipFormDataInCreatingRequest = true
});

WebAPI CORS not allowing Post requests

I have exhausted every resource I can find regarding Cors, and I still get the following error message when sending a request from Angular's $http service (via Chrome):
POST http://localhost:61459/api/LoginAuth/PostLoginByHandle 500 (Internal Server Error)
Get requests work just fine. I have found a hundred variations of similar instructions that seem to work for other people, but I just can't crack this one. I'll post my code.
WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
//jsonFormatter.SerializerSettings = new CamelCasePropertyNamesContractResolver();
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
}
}
From my understanding, this should be enough to allow any Cors request globally. In addition, I mark the controller with:
[EnableCors(origins: "*", headers: "*", methods: "*")]
I've tried modifying my web.config with various things I've found online, but I've read that it's not necessary when doing it programatically. Any idea what I'm doing wrong?
I've tagged this post as angularjs in case I'm doing something wrong there, as I'm very new to it. Here is my call:
$http.post("http://localhost:61459/api/LoginAuth/PostLoginByHandle",this.LoginRequest).success(function(data){
testCtrl.user = data;
console.log("Retrieved: " + data);
});
**Edit: I am able to hit the controller with Postman when I remove the top method. Any idea why these would conflict? Postman gives this error:
"Message": "An error has occurred.",
"ExceptionMessage": "Multiple actions were found that match the request: \r\nLoginByKey on type RecruitingTool.Web.Controllers.LoginAuthController\r\nPostLoginByHandle on type RecruitingTool.Web.Controllers.LoginAuthController"
Here is the controller code. I don't get why these would conflict:
[HttpPost]
public LoginResult LoginByKey(LoginRequest req)
{
LoginResult l = new LoginResult();
if (!string.IsNullOrEmpty(req.Key) &&
HttpContext.Current.Cache["credentials." + req.Username.ToUpper()].ToString() == req.Key)
{
l.Success = true;
}
else
{
l.Success = false;
l.ErrorMessage = "The credentials key is not valid.";
}
return l;
}
[HttpPost]
[EnableCors(origins: "*", headers: "*", methods: "POST")]
public LoginResult PostLoginByHandle(LoginRequest req)
{
LoginResult l = new LoginResult();
if (req.Username.ToUpper() == "TESTUSER" && req.Password == "test")
{
//change to authenticate against DB
l.Success = true;
l.CredentialsKey = Guid.NewGuid().ToString();
l.ErrorMessage = "";
HttpContext.Current.Cache["credentials." + req.Username.ToUpper()] = Guid.NewGuid().ToString();
}
else
{
l.Success = false;
l.ErrorMessage = "The username or password is not correct. Please check your information and try again.";
}
return l;
}
**Edit 2: The problem was a conflict between the two method's default routes. I'm not sure why that would be the case, but as soon as I specify an explicit route for both of them, it is resolved. I'm still interested to know the answer if anyone knows. Thanks all!
If you put a breakpoint in your Web API POST controller action is it not hitting it? HTTP 500 normally indicates some issue with your code (unhandled exception).
If it's not hitting that controller action it must be earlier in the pipeline. Have you tried just posting directly to your API method from something like POSTman? Very useful extension for Chrome..
1- Your method parameters are missing the [FromBody] attribute, so it should be like
public LoginResult PostLoginByHandle([FromBody]LoginRequest req)
2- Also, both methods have the exact same signature, in terms of the number of parameters and the data type of req parameters.
hope that helps.
WebAPI supports Convention based routing:
To find the action, Web API looks at the HTTP method, and then looks for an action whose name begins with that HTTP method name. For example, with a GET request, Web API looks for an action that starts with "Get...", such as "GetContact" or "GetAllContacts". This convention applies only to GET, POST, PUT, and DELETE methods. You can enable other HTTP methods by using attributes on your controller. We’ll see an example of that later.
Are you sure you don't have two methods in there, one called PostXXX and one called XXX? Or perhaps the one named POSTxxx is triggering convention-based routing. I've seen mention of "the conventions can result in conflicts in the routing table, matching incorrect actions."
Try renaming your methods to something other than Postxxx, Getxxx, ...
[PS Attribute-routing is much better]
I encountered same problem today. As it turns out the problem was in pre-flight OPTIONS request. I used this solution: Handling CORS Preflight requests to ASP.NET MVC actions
Additionally I added headers into response and removed Microsoft.AspNet.WebApi.Cors from project:
protected void Application_BeginRequest()
{
//CORS
if (Request.Headers.AllKeys.Contains("Origin"))
{
Response.Headers.Add("Access-Control-Allow-Origin", string.IsNullOrWhiteSpace(ConfigurationManager.AppSettings["CORS_ORIGIN"]) ? "*" : ConfigurationManager.AppSettings["CORS_ORIGIN"]);
Response.Headers.Add("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PUT, DELETE");
Response.Headers.Add("Access-Control-Allow-Headers", "Access-Control-Allow-Methods, Access-Control-Allow-Origin, Content-Type, Accept, X-Requested-With, Session");
//handle CORS pre-flight requests
if (Request.HttpMethod == "OPTIONS")
Response.Flush();
}
}

RPC call to external server

I am a new bie on GWT, I wrote an application on abc.com, I have another application i.e. xyz.com, xyz.com?id=1 provides me a data in json format, I was thinking to find a way that how to get that json file in abc.com via RPC call, because I have seen tutorials in which RPC calls are used to get data from its server. any help will be appreciated.
EDIT
I am trying to implement this in this StockWatcher tutorial
I changed my code slightly change to this
private static final String JSON_URL = "http://localhost/stockPrices.php?q=";
AND
private void refreshWatchList() {
if (stocks.size() == 0) {
return;
}
String url = JSON_URL;
// Append watch list stock symbols to query URL.
Iterator iter = stocks.iterator();
while (iter.hasNext()) {
url += iter.next();
if (iter.hasNext()) {
url += "+";
}
}
url = URL.encode(url);
MyJSONUtility.makeJSONRequest(url, new JSONHandler() {
#Override
public void handleJSON(JavaScriptObject obj) {
if (obj == null) {
displayError("Couldn't retrieve JSON");
return;
}
updateTable(asArrayOfStockData(obj));
}
});
}
before when I was requesting my url via RequestBuilder it was giving me an exception Couldn't retrieve JSON but now JSON is fetched and status code is 200 as I saw that in firebug but it is not updating on table. Kindly help me regarding this.
First, you need to understand the Same Origin Policy which explains how browsers implement a security model where JavaScript code running on a web page may not interact with any resource not originating from the same web site.
While GWT's HTTP client and RPC call can only fetch data from the same site where your application was loaded, you can get data from another server if it returns json in the right format. You must be interacting with a JSON service that can invoke user defined callback functions with the JSON data as argument.
Second, see How to Fetch JSON DATA

How to get custom SOAP header from WCF service response in Silverlight?

I'm trying to get custom response message header in Silverlight application.
on server-side new MessageHeader added to response headers:
OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("headerName", "headerNS", "The header value"));
and I can see this header in Fiddler:
s:Envelope [
xmlns:s=http://schemas.xmlsoap.org/soap/envelope/
]
s:Header
headerName [ xmlns=headerNS ] The
header value
But, I can't find a way to read header value in Silverlight application service callback:
using (new OperationContextScope(proxy.InnerChannel))
{
var headers = OperationContext.Current.IncomingMessageHeaders;
// headers is null :(
}
Does anyone encountered with similar issue?
Getting SOAP headers in responses on Silverlight isn't as easy as it should be. If you use the event-based callbacks, you're out of luck - it just doesn't work. You need to use the Begin/End-style operation call, like in the example below.
void Button_Click(...)
{
MyClient client = new MyClient();
IClient proxy = (IClient)client; // need to cast to the [ServiceContract] interface
proxy.BeginOperation("hello", delegate(IAsyncResult asyncResult)
{
using (new OperationContextScope(client.InnerChannel))
{
proxy.EndOperation(asyncResult);
var headers = OperationContext.Current.IncomingMessageHeaders;
// now you can access it.
}
});
}
Notice that you cannot use the generated client (from slsvcutil / add service reference) directly, you need to cast it to the interface, since the Begin/End methods are not exposed (explicitly implemented) on the client class.
To get headers from http request try to use Client HTTP stack.
The easies way to do it is to register the prefix, for example:
WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);

Resources