WCF : Generate JSON not works - json

I'm using DevExpress, XAF, and XPO for my application. I need to expose my data from a webservice.
ASP.NET Web API V2 is not compatible with XPO objects... (If you found how to... I will take it!).
The DevExpress wizard can help me to generate a WCF Web Service project, where
MyContext inherits from XpoContext
MyService inherits from XpoDataServiceV3 (and the class have an attribute : [JSONPSupportBehavior])
I would get a list of my XPO Objects, for that, I wrote the next code
[WebGet]
public IQueryable<MyType> Get()
{
return new XPQuery<MyType>(new UnitOfWork());
}
I have found various properties on WebGet attribute : RequestFormat, ResponseFormat, BodyStyle, UrlTemplate. On Format properties, I have the choise between WebMessageFormat.Json and WebMessageFormat.Xml. Logically, I type WebMessageFormat.Json.
When I go on my favorite webbrowser or fiddler, I do this request:
GET http://localhost:51555/MyService.svc/Get HTTP/1.1
User-Agent: Fiddler
Host: localhost:51555
Content-Type: application/json
But this not works... The response is :
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Length: 24250
Content-Type: application/atom+xml;type=feed;charset=utf-8
Server: Microsoft-IIS/10.0
...
And content was wrote in XML.
We are okay, I have configure my query with format properties... :
[WebGet(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]

I've found ! On your WCF service global class, wrote the following code :
HttpContext.Current.Request.Headers.Add("Accept", "application/json;");

Related

Parsing AWS SNS notification in .net core 1.0

I have a VisualStudio17 serverless application project and am using .net core Web Api.
I want to confirm my SNS subscription, but I have a problem that AWS sends the POST request with header content-type set to text/plain; charset=UTF-8 while body is JSON.
Here's an example request from their documentation:
POST / HTTP/1.1
x-amz-sns-message-type: Notification
x-amz-sns-message-id: da41e39f-ea4d-435a-b922-c6aae3915ebe
x-amz-sns-topic-arn: arn:aws:sns:us-west-2:123456789012:MyTopic
x-amz-sns-subscription-arn: arn:aws:sns:us-west-2:123456789012:MyTopic:2bcfbf39-05c3-41de-beaa-fcfcc21c8f55
Content-Length: 761
Content-Type: text/plain; charset=UTF-8
Host: ec2-50-17-44-49.compute-1.amazonaws.com
Connection: Keep-Alive
User-Agent: Amazon Simple Notification Service Agent
{
"Type" : "Notification",
"MessageId" : "da41e39f-ea4d-435a-b922-c6aae3915ebe",
"TopicArn" : "arn:aws:sns:us-west-2:123456789012:MyTopic",
"Subject" : "test",
"Message" : "test message",
"Timestamp" : "2012-04-25T21:49:25.719Z",
"SignatureVersion" : "1",
"Signature" : "EXAMPLElDMXvB8r9R83tGoNn0ecwd5UjllzsvSvbItzfaMpN2nk5HVSw7XnOn/49IkxDKz8YrlH2qJXj2iZB0Zo2O71c4qQk1fMUDi3LGpij7RCW7AW9vYYsSqIKRnFS94ilu7NFhUzLiieYr4BKHpdTmdD6c0esKEYBpabxDSc=",
"SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",
"UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:123456789012:MyTopic:2bcfbf39-05c3-41de-beaa-fcfcc21c8f55"
}
Content-type: text, body JSON. This makes it parsing quite difficult, and a simple
public void Post([FromBody] string t) // or dynamic t for the matter
doesn't work and throws the Request was short circuited at action filter 'Microsoft.AspNetCore.Mvc.ModelBinding.UnsupportedContentTypeFilter'. exception.
Am I missing something? Why are they doing this and how do I work with this?
I made it work like I described in this answer, by adding text/plain to formats that JsonInputFormatter should format.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(config =>
{
foreach (var formatter in config.InputFormatters)
{
if (formatter.GetType() == typeof(JsonInputFormatter))
((JsonInputFormatter)formatter).SupportedMediaTypes.Add(
MediaTypeHeaderValue.Parse("text/plain"));
}
});
...
}

FOSOAuthServerBundle: Invalid grant_type parameter or parameter missing

I'm trying to build a REST API using Symfony 3.1 and the FOSRestBundle, FOSUserBundle and FOSOAuthServerBundle. I managed to achieve this following the guide at https://gist.github.com/tjamps/11d617a4b318d65ca583.
I'm now struggling at the authentication process. When I make a POST request to the server for authentication (to localhost:8000/oauth/v2/token) with the parameters encoded in json in the request body:
{
"grant_type": "password",
"client_id": "1_myveryverysecretkey",
"client_secret": "myveryverymostsecretkey",
"username": "theuser",
"password": "thepassword"
}
The additional HTTP Headers are the following:
Accept: application/json
Cache-Control: no-store, private
Connection: close
Content-Type: */json
The client in the db table oauth2_client has the "password" grant_type a:1:{i:0;s:8:"password";}, as suggested by the guide.
The server is accepting the request, but I always get the response
{"error":"invalid_request","error_description":"Invalid grant_type parameter or parameter missing"}
Any suggestions what I am missing? Thanks!
I had the same problem. It seems fosOAuthBundle is not accepting json. if you send the query with form fields, it will work.
This is because FOSRestBundle uses a body listener which converts underscored keys to camel case. So the parameters that your OAuth2 server gets are not grant_type, but rather grantType, which it cannot process and so it gives you that error.
A solution for this would be to use a custom array normalizer on the body listener of fos rest.
really the FOSRestBundle Body Listener is the main 'cause' of this issue.
Array normalizer config
fos_rest:
body_listener:
array_normalizer: fos_rest.normalizer.camel_keys
it converts _ to camel case format.
The solution was remove it of my configuration by the moment.
calling again /oauth/v2/token endpoint:
{
"access_token": "NDBlZGViN2YwZGM5MTQ3ZTgwN2FhOGY4MDU4MTc1MTY2YzZmOThlMTdkM2JiZDJmMDVmNTg3MjU4N2JmODY3ZA",
"expires_in": 3600,
"token_type": "bearer",
"scope": null,
"refresh_token": "MDRiODllNjlhZWYxZjI5MjlhMzAxNGVhMDY5NjQxMmVmNDE5MzY3YzU0MGM0MDU1ZTVlY2Y2Zjg4ZTYyYzU3Mw"
}

WCF Behavior Extension to BizTalk WCF-WebHttp Send Port not populating Date Http Header

I have a BizTalk 2013 app (not R2) that needs to send a Json document to an external vendor's RESTful api. The vendor requires three Http headers:
Content-Type: application/json
Date: in ISO8601 UTC format
Authorization: custom auth. using a constructed string that includes the above Date value run through the HMACSHA1 hash
In BizTalk, my outbound (xml) message goes to the Send Port, there is a Custom Pipeline Component that transforms to Json using JSON.Net. So far, so good. To add the headers which are unique per message, I created a WCF Behavior extension that implements IClientInspector and IEndpointBehavior. In BeforeSendRequest(), I get a reference to the HttpRequestMessageProperty for the request.
I can successfully add to the Headers Collection a ContentType header and an Authorization header. I cannot add a Date header - no errors, just no header value when examining with Fiddler.
I read somewhere that Date is a restricted header and for a workaround I could use Reflection to get around it. E.g.
MethodInfo priMethod = headers.GetType().GetMethod("AddWithoutValidate", BindingFlags.Instance | BindingFlags.NonPublic);
priMethod.Invoke(headers, new[] { "Date", ISODateTimestamp });
That didn't work either. I'm really stumped with: 1. Why no Date header at all is on my request? 2. If there was one, how I could manipulate it as I need to give that is "restricted"?
I tried two different options: a WCF behavior extension:
public object BeforeSendRequest(ref Message request, System.ServiceModel.IClientChannel channel)
{
System.Diagnostics.Debug.Print("Entering BeforeSendRequest()");
try
{
HttpRequestMessageProperty httpRequest = null;
if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name))
{
httpRequest = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
}
WebHeaderCollection headers = httpRequest.Headers;
headers.Add(HttpRequestHeader.ContentType, "application/json");
headers.Add(string.Format("{0}:{1}", "Date", _taxwareHelper.ISODateTimestamp));
headers.Add("tweDate", _taxwareHelper.ISODateTimestamp);
headers.Add(HttpRequestHeader.Authorization, _taxwareHelper.AuthorizationString);
and a Custom Pipeline Component in a Send Pipeline
string httpHeaderValue =
new StringBuilder()
.Append("Content-Type: application/json")
.Append("\n")
//.Append(string.Format("Date:{0}", taxwareHelper.ISODateTimestamp))
//.Append("\n")
.Append(string.Format("Date:{0}", "Fri, 10 Jul 2015 08:12:31 GMT"))
.Append("\n")
.Append(string.Format("tweDate:{0}", taxwareHelper.ISODateTimestamp))
.Append("\n")
.Append(string.Format("Authorization:{0}", taxwareHelper.AuthorizationString))
.ToString();
pInMsg.Context.Write("HttpHeaders", "http://schemas.microsoft.com/BizTalk/2006/01/Adapters/WCF-properties", httpHeaderValue);
in either case, I can set the Content-Type, the Authorization and test date - tweDate just to test, but I can not set the actual Date header.
Yes indeed Date is a special HTTP Header that respresents the date and time at which the message originated but that doesn't prevent you from using it. BUT the date has to be in RFC 822 Format Tue, 15 Nov 1994 08:12:31 GMT
Another thing why do you make it the hard way by coding a behaviour while you could just use HTTPHeaders to add your custom header in your custom pipeline component plus this is so much cleaner
So try to convert your date in the correct format or use HTTPHeaders
http://www.codit.eu/blog/2013/04/30/using-httpheaders-with-wcf-webhttp-adapter-on-biztalk-2013/
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

WebAPI: A callback parameter was not provided in the request URI

I am executing a post method in my API using fiddler I get error "A callback parameter was not provided in the request URI.". However, this works for get method.
I have seen several answers to this question, and as per the error I need to specify a callback parameter. However, I'm not sure how to do this using fiddler.
In response to one of those answers from Can I make a jQuery JSONP request without adding the '?callback=' parameter in URL? . I've tried the following in fiddler and I get the same error..
url: http://velopoint-api.localhost.dev/api/v1/tasks?callback=foo
header:
User-Agent: Fiddler
Host: velopoint-api.localhost.dev
ContentType: application/json; charset=utf-8
Authorization: basic "UNQUOTED"
Content-Length: 47
jsonp: true
jsonpCallback: jsonCallback
dataType: jsonp
request body
{ "Title":"New Task", "DueDate":"20-jul-2014" }
Startup
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
// Change Formater to use CamelCasePropertyNamesContractResolver
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().FirstOrDefault();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
/* Support JsonP */
//register JSONP media type formatter
config.Formatters.Insert(0, new JsonpMediaTypeFormatter(jsonFormatter));
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
...
}
After playing around a little, I've finally figured it wasn't actually routing to the post method.
My header is now
User-Agent: Fiddler
Host: velopoint-api.localhost.dev
Authorization: basic UNQUOTED
Content-Length: 224
Content-Type: application/json
I fixed the problem by specifying Route attribute to my Post method and passing an empty string to the pattern parameter, both on the Get and the Post (as I already have the RoutePrefix attribute specified on the class.
[RoutePrefix("api/v1/tasks")]
[VeloPointAuthorise(perUser: true)]
public class TaskController : BaseApiController
{
[HttpGet]
[Route(template:"", Name = "TaskRoute")]
public HttpResponseMessage Get(int page = 0)
{
....
}
[HttpPost]
[Route(template:"")]
public HttpResponseMessage Post([FromBody] OrganiserTaskModel model)
{
....
}

SmartGWT RestDataSource to Spring REST Controller GET returns 415 Error

This problem has been bugging me all day, and I have spent a lot of time here, and on Google trying to find the right answer and trying lots of different fixes.
I have a Spring MVC Controller which is defined here:
#RequestMapping(value = "/searchAndCount", method = RequestMethod.GET, produces = "application/json", headers =
{ "Accept=application/json", "Content-Type=application/json" }, consumes = "application/json")
public #ResponseBody
RequestResults<?> searchAndCount(#RequestBody SearchInvoiceDTO searchInvoiceDto)
{
RequestResults<?> requestResults = invoiceApprovalService.searchAndCount(searchInvoiceDto);
return requestResults;
}
I know that with most gets, simple parameters can get sent back, but in this case, I found it better to put all my search criteria in one object and send that over. Which is why I am doing a #RequestBody.
Per previous fixes, I made sure that this has both headers that may be needed to accept JSON output.
The JSON String looks like:
String s1 = "{\"userId\":3, \"ddUserId\":301010651, \"customerCode\":\"QA\", \"customerId\":8}";
And yes, I have used the Jackson ObjectMapper tool verify this code will map correctly from this String to an Object, and vice-versa. When I look at the POJO, it does implement Serializable, and it does have a default constructor.
The Junit test works awesome and does return data:
MockHttpServletRequestBuilder requestBuilder =
MockMvcRequestBuilders.get("/invoices/searchAndCount").contentType(MediaType.APPLICATION_JSON)
.content(test);
this.mockMvc.perform(requestBuilder).andDo(print());
This does make the call to the Controller, I can see from the output what the headers are, and I can see that I actually get back real data which is great. So I feel there is not much more I can do from he controller side.
The real call to the controller comes from the SmartGWT RestDataSource.
The RequestMethod is defined in the datasource, here is the init method:
private InvoiceDataSource(String id)
{
setID(id);
setClientOnly(false);
// set up FETCH to use GET requests
OperationBinding fetch = new OperationBinding();
fetch.setOperationType(DSOperationType.FETCH);
fetch.setDataProtocol(DSProtocol.POSTMESSAGE);
fetch.setDataFormat(DSDataFormat.JSON);
DSRequest fetchProps = new DSRequest();
fetchProps.setHttpMethod("GET");
fetch.setRequestProperties(fetchProps);
// set up ADD to use POST requests
OperationBinding add = new OperationBinding();
add.setOperationType(DSOperationType.ADD);
add.setDataProtocol(DSProtocol.POSTMESSAGE);
// ===========================================
DSRequest addProps = new DSRequest();
addProps.setHttpMethod("POST");
add.setRequestProperties(addProps);
// set up UPDATE to use PUT
OperationBinding update = new OperationBinding();
update.setOperationType(DSOperationType.UPDATE);
update.setDataProtocol(DSProtocol.POSTMESSAGE);
// ===========================================
DSRequest updateProps = new DSRequest();
updateProps.setHttpMethod("PUT");
// updateProps.setContentType("application/json");
update.setRequestProperties(updateProps);
// set up REMOVE to use DELETE
OperationBinding remove = new OperationBinding();
remove.setOperationType(DSOperationType.REMOVE);
DSRequest removeProps = new DSRequest();
removeProps.setHttpMethod("DELETE");
remove.setRequestProperties(removeProps);
// apply all the operational bindings
setOperationBindings(fetch, add, update, remove);
init();
}
The Fetch is set to POSTMESSAGE which seems to be the best way to pass data using transformReponse.
#Override
protected Object transformRequest(DSRequest dsRequest)
{
// gets the correct URL - (web-app-root)/rest/invoices/searchAndCount
postProcessTransform(dsRequest);
System.out.println("InvoiceDataSource: transformRequest: START");
dsRequest.setContentType("application/json");
JavaScriptObject jso = dsRequest.getData();
// setting more headers, but this doesn't seem to change anything
dsRequest.setAttribute("Access-Control-Allow-Origin", "*");
dsRequest.setAttribute("Content-Type", "application/json");
dsRequest.setAttribute("Accept", "application/json");
String s1 = JSON.encode(jso);
System.out.println("InvoiceDataSource: transformRequest: FINISH: s1=" + s1);
return s1;
}
Since I know I have he correct URL, I also know I am spitting out that the variable "s1" also has the correct JSON data, and again I did test that JSON to make sure it would hit the controller correctly.
I also do have dependencies for Jackson as defined in the pom.xml file. I also have the message converters set in the springmvc-servlet.xml file. If these were not correct, the unit test would not work. However, if you need to see the pom.xml file or the springmvc-servlet.xml file, please let me know.
I have been researching and trying lots of things all day long now, and so far ... no luck.
I hope I have provided enough information, but if you need more, please let me know.
Ultimately, I hope I can tweak my SmartGWT RestDataSource to pass in the correct data to this controller to actually get data out of it.
UPDATE:
When I run this with Jetty in Eclipse, I am using Firefox 23.0.1 to open to my web-app.
Within the Console in Eclipse, here is what I can see:
[WARN] 415 - GET /rest/invoices/searchAndCount (127.0.0.1) 1440 bytes
Request headers
Host: 127.0.0.1:8888
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: GLog=%7B%0D%20%20%20%20left%3A22%2C%20%0D%20%20%20%20top%3A11%2C%20%0D%20%20%20%20width%3A705%2C%20%0D%20%20%20%20height%3A855%2C%20%0D%20%20%20%20priorityDefaults%3A%7B%0D%20%20%20%20%20%20%20%20Log%3A4%0D%20%20%20%20%7D%2C%20%0D%20%20%20%20defaultPriority%3A3%2C%20%0D%20%20%20%20trackRPC%3Atrue%0D%7D
Connection: keep-alive
If-Modified-Since: Thu, 01 Jan 1970 00:00:00 GMT
Response headers
Content-Type: text/html; charset=iso-8859-1
Content-Length: 1440
Accept: application/json
Notice that the Request header:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
does not show application/json
Also, the Request header: "Content-Type" is not present
When I use Chrome, the result is:
[WARN] 415 - GET /rest/invoices/searchAndCount (127.0.0.1) 1440 bytes
Request headers
Host: 127.0.0.1:8888
Connection: keep-alive
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36
DNT: 1
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
If-Modified-Since: Thu, 01 Jan 1970 00:00:00 GMT
Cache-Control: max-age=0
Response headers
Content-Type: text/html; charset=iso-8859-1
Content-Length: 1440
Accept: application/json
When I run from the JUnit test, there is always the "Content-Type:application/json" present. So, it seems that although I tell the SmartGWT RestDataSource that I am using JSON in several places ... the web-services call is not creating the right header.
UPDATE:
I added the following code to the SmartGWT RestDataSource transformRequest method:
Map<String, String> httpHeaders = new HashMap<String, String>();
httpHeaders.put("Accept", "*/*");
httpHeaders.put("Content-Type", "application/json");
dsRequest.setHttpHeaders(httpHeaders);
I can add the "Accept" Request-Header and I still got the 415 Unsupported Media error Message.
When I add the "Content-Type" Request-Header, then I get a 400 BAD REQUEST error message.
I get this in the console now:
[WARN] 400 - GET /rest/invoices/searchAndCount (127.0.0.1) 1418 bytes
Request headers
Host: 127.0.0.1:8888
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
If-Modified-Since: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: application/json
Response headers
Content-Type: text/html; charset=iso-8859-1
Content-Length: 1418
This took a lot of work, but I finally figured it out.
The verb GET doesn' work for my needs ... I am an idiot. No amount of work is going to make a GET work with sending my parameters as an object in the request data. So let me show the code that I had to change to make this work.
The SmartGWT RestDataSource, I changed;
fetchProps.setHttpMethod("GET"); to fetchProps.setHttpMethod("POST");
an I still kept: fetch.setDataProtocol(DSProtocol.POSTMESSAGE);
// set up FETCH to use GET requests
OperationBinding fetch = new OperationBinding();
fetch.setOperationType(DSOperationType.FETCH);
fetch.setDataProtocol(DSProtocol.POSTMESSAGE);
fetch.setDataFormat(DSDataFormat.JSON);
// ===========================================
DSRequest fetchProps = new DSRequest();
fetchProps.setHttpMethod("POST");
fetchProps.setContentType("application/json");
fetch.setRequestProperties(fetchProps);
Also in the same RestDataSource, in the "transformRequest" method, I added:
Map<String, String> httpHeaders = new HashMap<String, String>();
httpHeaders.put("Content-Type", "application/json");
httpHeaders.put("Accept", "application/json");
dsRequest.setHttpHeaders(httpHeaders);
This made sure that whatever browser I was using, these two headers are being manually set.
The Spring MVC Controller resides in the same web-app, so for now, this avoids any SOP cross-site domain issues until I can test that out. In the meantime, the header for my control looks like:
#RequestMapping(value = "/searchAndCount", method = RequestMethod.POST, headers =
{ "Accept=application/json", "Content-Type=application/json" })
public #ResponseBody
ArrayList<?> searchAndCountPOST(#RequestBody SearchInvoiceDTO searchInvoiceDto)
This work great to get called, and it returned my data. The original unit test I had was doing a MockMVc.get which worked and through me off. It must have recognized data coming through and changed the GET to a POST to make it work. My unit test was changed to a POST now, and that also works well.
I hope all this work pays off for someone else!