Posting to a WCF Data Service 5 - json

I am trying to POST to a EntityFramework backed WCF Data Service but I am getting:
415 Unsupported Media Type
I followed this guide to put the headers into my jQuery POST http://blogs.msdn.com/b/astoriateam/archive/2012/04/11/what-happened-to-application-json-in-wcf-ds-5-0.aspx
These are my headers:
POST /webservices/service/service.svc/Activities HTTP/1.1
Host: www.url.com
Connection: keep-alive
Content-Length: 138
Origin: http://www.url.com
X-Requested-With: XMLHttpRequest
MaxDataServiceVersion: 3.0
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5
Content-Type: application/json; charset=UTF-8
Accept: application/json;odata=verbose
Referer: http://www.url.com/sites/site/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,en-GB;q=0.6
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
This site helped with using EntityFramework with WCF Data Services - http://blogs.msdn.com/b/writingdata_services/archive/2011/06/15/entity-framework-4-1-code-first-and-wcf-data-services.aspx
function AddActivity() {
var activity = {
activity:
{
"Title": "Test From Code",
"Detail": "Code Example",
"Started": "2012-06-21T09:00:00",
"UserId": 17
}
};
var url = 'http://www.url.com/webservices/service/service.svc/Activities';
$.ajax({
type: "POST",
url: url,
data: activity,
dataType: "json",
contentType: "application/json; charset=utf-8",
beforeSend: function (xhr) {
xhr.setRequestHeader("Accept", "application/json;odata=verbose");
xhr.setRequestHeader("MaxDataServiceVersion", "3.0");
},
success: function (data) {
alert('Success');
},
error: function (err) {
alert('Fail\n' + err.statusText);
}
});
}
I can read data JSON data OK

WCF Data Services 5.0 does not support application/json content type for request (or response) payloads if the payload has version V3 (DataServiceVersion). In the above case you didn't specify the DataServiceVersion (which you always should by the way). The server in such case has to infer the version somehow, the WCF Data Service server will assume the maximum version it and the client understands. Since the server itself understands V3 and you specified MaxDataServiceVersion: 3.0 which means the client also understands V3 it assumes the payload is a V3 payload.
V3 payloads currently don't support application/json, they only support application/json;odata=verbose.
See this blog post for more explanation: http://blogs.msdn.com/b/astoriateam/archive/2012/04/11/what-happened-to-application-json-in-wcf-ds-5-0.aspx. It talks about GET but the same things applies to POST and such as well.
So to fix your problem, modify your client to either send DataServiceVersion: 2.0 (or 1.0) if that would be correct, or even better modify it to send Content-Type: application/json;odata=verbose (which will work no matter the payload version).

Just did this with minimal hacking, using WCF without any WCF Data Services components.
Posting a minimal type back as JSON will automap the values, for example, this JSON
{
"name": "1234567890",
"amount": "2500.00"
}
can post back to this service
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json)]
public string MethodName(Message message)
where Message is C# class:
public class Message
{
public string name { get; set; }
public string amount { get; set; }
}
Pretty simple. Then, if you want to process it as an OData request, you have more complex JSON being posted, example (ODataV2):
{
"d": [
{
"__metadata": {
"id": "http://host:54470/ods.svc/MethodName('1234567890')",
"uri": "http://host:54470/ods.svc/MethodName('1234567890')",
"type": "SomeCompany.Helper.Services.RequestMessage.Message"
},
"name": "1234567890",
"amount": "2500.00",
}
]
}
If you create corresponding C# classes, this OData will map to those classes. These classes work with the OData.
public class Message
{
public ODataResult[] d;
}
public class ODataResult
{
public ODataMetaData __metadata;
public string name { get; set; }
public string amount { get; set; }
}
public class ODataMetaData
{
public string id;
public string uri;
public string type;
}
The json posted OData will map to this Message class. This gives you a way to 'shape' the incoming message as needed, which might be required in some cases.
Not saying this is better than using WCF Data Services, just saying it will work.

Related

Spring MVC to send responsebody object to ajax post gives 406 error

There are similar links but I haven't found any solution to work for me, so I was wondering if someone could give me a working example for my scenario. I am doing an ajax get to retrieve data from the server side, so I can create charts dynamically on the client side. Do I need to include MappingJacksonHttpMessageConverter? If that's the answer can someone provide an example i can follow for that?
Java:
#RequestMapping(value="/getReportData.html", method=RequestMethod.GET, produces="application/json")
public #ResponseBody Reports getReport1Data(HttpServletRequest request)
{
System.out.println("Report 1 Page GET Method");
ModelAndView mv = new ModelAndView("report1");
if((Reports)request.getSession().getAttribute(USER_SESSION_REPORTS) != null){
reports = (Reports)request.getSession().getAttribute(USER_SESSION_REPORTS);
System.out.println("--------> Report 1 Page with session data");
return reports;
}
else{
System.out.println("--------> Report 1 Page with NO session data");
}
mv.addObject("report1", reports.getReport1());
return null;
}
Javascript:
function getData(){
$.ajax({
url: "getReportData.html",
type: "GET",
contentType: "application/json",
dataType: JSON,
success: function(report1){
console.log("success: " + report1.utilRatio.decRatio);
},
error: function(report1){
console.log("error: " + report1.utilRatio.decRatio);
}
});
}
Response Headers:
Content-Language: "en",
Content-Length: "1110"
Content-Type: "text/html;charset=utf-8"
Server: "Apache-Coyote/1.1"
Request Headers:
Accept: "/"
Accept-Language: "en-US,en;q=0.5"
Accept-Encoding: "gzip,deflate"
Content-Type: "application/json"
X-Requested-With: "XMLHttpRequest"
It looks like your request headers are wrong. You can remove the contentType setting since you are not sending data to the server and change dataType to the string value "json" instead of the variable JSON.
Also, your response headers are wrong. Just make sure you are always returning a Reports object. And you probably want to remove the html extension from that endpoint since you're just returning an object.
spring uses #ResponseBody annotaion for returning data as json .it will implicitly call the MappingJacksonHttpMessageConverter .so you need to use it.
#RequestMapping(value = "/getjson", method = RequestMethod.POST, produces = "application/json")
#Transactional
public void getJson(HttpServletRequest request, HttpServletResponse response, #RequestParam("type") String type)
throws DatatypeConfigurationException, IOException, JSONException {
JSONObject json = new JSONObject();
Map<String, String[]> parameterMap = request.getParameterMap();
List<Chart> chart=myService.getChart();
if (Chart.size()>0) {
json.put("status", "SUCCESS");
JSONArray array = new JSONArray();
for (Chart chartData: chart) {
JSONObject object = new JSONObject();
object.put("id", chartData.getRaasiId());
object.put("name", chartData.getName());
array.put(object);
}
json.put("options", array);
}
}
}
response.setContentType("application/json");
System.out.println("response======" + json.toString());
PrintWriter out = response.getWriter();
out.write(json.toString());
}
============
on the html
jQuery
.ajax({
url : controllerUrl,
dataType : 'text',
processData : false,
contentType : false,
type : 'GET',
success : function(response) {
success : function(response) {
marker = JSON.stringify(response);
json = jQuery.parseJSON(marker);
json = JSON.parse(json);
alert(json.status);
}
});
for reference:
https://rwehner.wordpress.com/2010/06/09/2-ways-to-create-json-response-for-ajax-request-in-spring3/

spring mvc post refuses json

I am using Spring-boot 1.1.7.RELEASE and trying to get my POST to work for my controllers. However, when I submit the html I get the exception:
org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:152)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:183)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:98)
I believe that Spring-Boot will pickup the HttpMediaType automatically and handle the json requests/reponse. Certainly all my GET's seem to be working.
Here is my Controller:
#Controller
#Transactional
#RequestMapping("/{officeName}/office")
public class OfficeController extends BaseController {
#RequestMapping( method = RequestMethod.GET)
#ResponseBody
public OfficeDTO get(#PathVariable String officeName) {
return officeDTOConverter.convert( officeService.findByURL( officeName ) );
}
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public OfficeDTO addOffice(#RequestBody Office office) {
return officeDTOConverter.convert( officeService.save( office ) );
}
}
And here is my post:
headers:
Content-Type application/json;charset=UTF-8
Accept application/json, text/javascript, */*; q=0.01
json:
{"address":{"addressTypeRefDto":{},"status":"ACTIVE","city":"Glenwood Springs","addressId":2,"seqId":null,"addressLine1":"123 Somewhere Lane","zip":"12345-789","addressLine2":"Suite 100","zipLong":"12345-789","addressLine3":"line 3","state":"CO","addressType":"OFFICE"},"contacts":[{"contactTypeRef":{"contactDescriptionShort":"Office Phone"},"contactId":1,"contactInfo":"555-555-1234","status":0},{"contactTypeRef":{"contactDescriptionShort":"Office Email"},"contactId":2,"contactInfo":"","status":0},{"contactTypeRef":{"contactDescriptionShort":"Office Fax"},"contactId":3,"contactInfo":"","status":0},{"contactTypeRef":{"contactDescriptionShort":"Office Emergency"},"contactId":4,"contactInfo":"","status":0}],"workDays":[{"dayOfWeek":"MONDAY","active":"true","startTime":"7:00","endTime":"7:00","breakStartTime":"7:00","breakEndTime":"7:00"},{"dayOfWeek":"TUESDAY","active":"true","startTime":"7:00","endTime":"7:00","breakStartTime":"7:00","breakEndTime":"7:00"},{"dayOfWeek":"WEDNESDAY","active":"true","startTime":"7:00","endTime":"7:00","breakStartTime":"7:00","breakEndTime":"7:00"},{"dayOfWeek":"THURSDAY","active":"true","startTime":"7:00","endTime":"7:00","breakStartTime":"7:00","breakEndTime":"7:00"},{"dayOfWeek":"FRIDAY","active":"true","startTime":"7:00","endTime":"7:00","breakStartTime":"7:00","breakEndTime":"7:00"},{"dayOfWeek":"SATURDAY","active":"true","startTime":"7:00","endTime":"7:00","breakStartTime":"7:00","breakEndTime":"7:00"},{"dayOfWeek":"SUNDAY","active":"true","startTime":"7:00","endTime":"7:00","breakStartTime":"7:00","breakEndTime":"7:00"}],"officeType":"GENERAL","officePhone":"555-555-1234","name":"Office Name","uRL":"test","status":"ACTIVE","servicePackageType":"BASIC","languageId":"English","ein":"123"}
and the response:
{"timestamp":1417717964314,"status":500,"error":"Internal Server Error","exception":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'application/json;charset=UTF-8' not supported","path":"/mycompany/office"}
I am using the GET json to populate the html, and then taking those values to build a json string to submit via KendoUI:
transport : {
read : {
type : "POST",
beforeSend: function(req) {
req.setRequestHeader('Accept', "application/json");
},
url : function(options) {
return "/" + officeURL + "/office"
},
beforeSend: function(xhr){
xhr.setRequestHeader(header, token);
},
contentType : "application/json",
dataType : 'json'
},
parameterMap : function(data, type) {
if (type == "read") {
return createOfficeJSON();
}
}
If anyone sees any reason why the media type is being rejected, or if I have a mistake somewhere I would appreciate any help you can offer.

Web api return null value

I have an Web Api controller for access data from server:
public class ServicesController : ApiController
{
[AcceptVerbs("POST")]
public IList<TreeMenuItem> LoadMetadata()
{
List<TreeMenuItem> itemsMenu = new List<TreeMenuItem>();
TreeMenuItem dataSource = new TreeMenuItem("1", "DataSources", null);
itemsMenu.Add(dataSource);
return itemsMenu;
}
}
which is call by an angularJS controler:
angular.module('App').
controller('TreeMenuController', ["$scope", "$http", treeMenuController]);
function treeMenuController($scope, $http) {
var baseUrl = "api/Services/LoadMetadata";
$http.post(baseUrl)
.then(function (result) {
$scope.roleList = result.data;
});
};
In browser network I have:
Request Method:POST
Status Code:200 OK
Request Headersview source
Accept:application/json, text/plain, */*
Accept-Encoding:gzip,deflate
Content-Length:2
Content-Type:application/json;charset=UTF-8
Request Payload
{}
Response Headers:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Content-Length: 4
in Response tab: [{}].
What is wrong?
I make it work:
The big help was the response message when I put in browser the address for accessing api services (api/Services/LoadMetadata):
The error answer was in an xml file where I found that was problem with serialization of the object TreeMenuItem. The advice was to decorate with DataContract the class and DataMember the class properties - like in WCF. After I did that (was need to add reference in project to System.Runtime.Serialization), everything was perfect.

Web Api, howto improve performance with ProtoBuf

It takes nearly 50 seconds to load a big chunk of 35 MB Json when accessing the Api. So to improve performance I added the WebApiContrib.Formatting.ProtoBuf to my project. The data is displayed in a Kendo UI Grid.
What am I missing here? A dataType or type in the View, or anything like that? And are there other or better ways to improve the performance?
Here some snippets->
POCO-Class:
[ProtoContract]
public partial class KDAuftraege
{
[ProtoMember(1)]
public int AngebotsNummer { get; set; }
[ProtoMember(2)]
public Nullable<int> BesuchsNummer { get; set; }
[ProtoMember(3)]
public Nullable<int> Kennummer { get; set; }
[ProtoMember(4)]
View:
var remoteDataSource = new kendo.data.DataSource({
transport: {
read: {
url: '/api/WebApiAuftraege'
},
},
Controller:
public IQueryable<KDAuftraege> GetKDAuftraeges()
{
//return db.KDAuftraeges.Take(500);
return db.KDAuftraeges;
}
WebApi:
config.Formatters.Add(new ProtoBufFormatter());
Headers:
Cache-Control no-cache
Content-Length 36227588
Content-Type application/json; charset=utf-8
Date Sat, 07 Jun 2014 09:23:54 GMT
Expires -1
Pragma no-cache
Server Microsoft-IIS/8.0
X-AspNet-Version 4.0.30319
X-Powered-By ASP.NET
X-SourceFiles =?UTF-8?B?YzpcdXNlcnNcb2xkc3BvcnRcZG9jdW1lbnRzXHZpc3VhbCBzdHVkaW8gMjAxM1xQcm9qZWN0c1xWaXM0XFZpczRcYXBpXFdlYkFwaUF1ZnRyYWVnZQ==?=
Anfrage-HeaderQuelltext anzeigen
Accept */*
Accept-Encoding gzip, deflate
Accept-Language de,en-US;q=0.7,en;q=0.3
Connection keep-alive
Cookie __RequestVerificationToken=wMVQWPOkXsB2XDIFN_07RJDtKqN_90dLRYaBYJGsFSGEHTcQ1S6e15mPiWrvkMHS8HrAlHYAI0OVSkqtPQHFVMP5DxoyccijSktL_KsoEFU1; .AspNet.ApplicationCookie=RTQ61CfArDWHlWN06eOpZiZY6NmFGp0SwCCuR8bQCtnItSz6S8YTasQu4-uoRQCc-XqWDCZmtOpEb-b0SyIioQPomkm1BrKywMcVwt3bF_JBxORKGg-UNSHyPvFyBohiS1sJ354LpRHIjrPIA8rUexvZih4VrK9lvHu_sm21ncNXXV7jATKAjTdX7J3XvfxRsF11fhgDNtpXPEWxQPjD7Rkj5yvdqI-vbfr9tfQbszUR1O3oOjYcRxUvvVrJ7xnt-caxt-o_Kut1dixLEA241pMGPCHfetWK73Yp148K3X9By6ylHFOTEjjDwHZyHLIrBwwOZ-ujnaOf20jQzeZXaF16bHxeadLYuKK-Z2DpdzaJXPzZd2pBbzHJMFX7USfZmp7OZzLpOitLCMovGHwdRiLD0F2NR1a0iTHCgiZLvA8
Host localhost:19275
Referer http://localhost:19275/MvcAuftraege
User-Agent Mozilla/5.0 (Windows NT 6.3; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0
X-Requested-With XMLHttpRequest
Options 1: Change your view DataSource code (I think this is right)
var remoteDataSource = new kendo.data.DataSource({
transport: {
read: {
type: 'GET',
headers: { Accept: 'application/x-protobuf' },
url: '/api/WebApiAuftraege'
},
},
Option 2: Change your Web API Formatters so that ProtoBuf is the default:
config.Formatters.Insert(0, new ProtoBufFormatter());

Using JSON RPC in Struts2 along with Spring Security causes the request to be forbidden(403)

I use the following JavaScript/jQuery function to make a remote procedure call through JSON.
<s:url var="testJsonUrl" action="testJsonAction"/>
<script src="../js/jquery-1.8.0.min.js" type="text/javascript"></script>
var timeout;
var request;
$(document).ready(function(){
$("#countryComboBox").change(function(){
if(!request)
{
request = $.ajax({
datatype:"json",
type: "POST",
data: JSON.stringify({jsonrpc:'2.0', method:'getStateTables', id:'jsonrpc', params:[$("#countryComboBox").val()]}),
contentType: "application/json-rpc; charset=utf-8",
url: "<s:property value='#testJsonUrl'/>",
success: function(response)
{
alert(JSON.stringify(response.result));
},
complete: function()
{
timeout = request = null;
},
error: function(request, status, error)
{
if(status!=="timeout"&&status!=="abort")
{
alert(status+" : "+error);
}
}
});
timeout = setTimeout(function() {
if(request)
{
request.abort();
alert("The request has been timed out.");
}
}, 30000);
}
});
});
This function invokes the method getStateTables() in an action class which is as follows.
#Namespace("/admin_side")
#ResultPath("/WEB-INF/content")
#ParentPackage(value = "json-default")
public final class TestAction extends ActionSupport
{
#Autowired
private final transient SharableService sharableService = null;
private static final long serialVersionUID = 1L;
List<StateTable> stateTables;
public TestAction() {}
#SMDMethod
public List<StateTable> getStateTables(#SMDMethodParameter(name="countryId")Long countryId) {
stateTables = sharableService.findStatesByCountryId(countryId);
return stateTables;
}
#Action(value = "testJsonAction",
results = {
#Result(name = ActionSupport.SUCCESS, type = "json", params = {"enableSMD", "true"})},
interceptorRefs = {
#InterceptorRef(value = "json", params = {"enableSMD", "true", "includeProperties", "result\\[\\d+\\]\\.stateId, result\\[\\d+\\]\\.stateName", "excludeNullProperties", "true"})})
public String executeAction() throws Exception {
return SUCCESS;
}
#Action(value = "Test",
results = {
#Result(name = ActionSupport.SUCCESS, location = "Test.jsp"),
#Result(name = ActionSupport.INPUT, location = "Test.jsp")},
interceptorRefs = {
#InterceptorRef(value = "defaultStack", params = {"params.acceptParamNames", "", "params.excludeMethods", "load", "validation.validateAnnotatedMethodOnly", "true"})})
public String load() throws Exception {
//This method is just used to return an initial view on page load.
return ActionSupport.SUCCESS;
}
}
This method causes a list of states corresponding to the countryId supplied by the jQuery function as a parameter to this method to be returned which is delegated to the jQuery function as a JSON response.
This works fine unless Spring Security comes into picture. While using Spring Security, the request is forbidden.
The header information can be viewed as follows.
Request URL:http://localhost:8080/TestStruts/admin_side/testJsonAction.action
Request Method:POST
Status Code:403 Forbidden
Request Headers
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Content-Length:73
Content-Type:application/json-rpc; charset=UTF-8
Cookie:JSESSIONID=0C4D7DFD269F1D5F7315A39971A75961; sbOverlayID=42887237
Host:localhost:8080
Origin:http://localhost:8080
Referer:http://localhost:8080/TestStruts/admin_side/Test.action
User-Agent:Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.76 Safari/537.36
X-Requested-With:XMLHttpRequest
Request Payload
{jsonrpc:2.0, method:getStateTables, id:jsonrpc, params:[2]}
id: "jsonrpc"
jsonrpc: "2.0"
method: "getStateTables"
params: [2]
Response Headers
Content-Length:1149
Content-Type:text/html;charset=utf-8
Date:Tue, 28 Jan 2014 22:12:55 GMT
Server:Apache-Coyote/1.1
X-Content-Type-Options:nosniff
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block
My Spring Security configurations are exactly the same as mentioned in this question.
Where to look into Spring Security?
Found something. It is because I'm using a CSRF token (configured in the spring-security.xml file as <csrf/>) which is required to be sent along with the AJAX request. it worked, when I used a jQuery function along with <meta> tags as mentioned here.
If you using JSON, then it is not possible to submit the CSRF token
within an HTTP parameter. Instead you can submit the token within a
HTTP header. A typical pattern would be to include the CSRF token
within your meta tags. An example with a JSP is shown below:
<html>
<head>
<meta name="_csrf" content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" content="${_csrf.headerName}"/>
<!-- ... -->
</head>
<!-- ... -->
You can then include the token within all your Ajax requests. If you
were using jQuery, this could be done with the following:
$(function () {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
});