I'm using jqGrid to display some data to the user. One of the features needed is for users to be able to search the grid. I'm using a WCF Data Service to get this data and return it.
When I first started jqGrid development I just needed pagination (no searching required) and was successfully able to use the following method:
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public String GetStuff(int? page, int? rows)
{
// pagination going on in here
}
Then I needed the search so I added a param of type string like so:
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public String GetStuff(int? page, int? rows, string search)
{
// more stuff going on in here
}
and then I get a 400 Bad Request error. I assume its because of the search param, I just don't understand why int works (and even bool worked) but string doesn't. Does it have anything to do with the fact that WCF Data Services are RESTful?
I of course double-checked the query string that jqGrid sends (via ajax) and it matches the param name. There are some extra query string params that jqGrid sends that are not used (ignoring them worked fine with my original pagination code)
String literals in the query URL must be quoted with single quotes. So the above service operation would be called like this:
/GetStuff?page=1&rows=10&search='John'
Does your query string look like that?
Related
I'm curious what is the best way of returning the same resource but using different DTOs.
For example, I have a user class:
public class User {
private String name;
private String surname;
private String age;
}
The list of users is available under url:
/users
Some other view needs list of users but without age, so, I would like to return list of UserDTO.
public class UserDTO {
private String name;
private String surname;
}
What is the proper way of defining url?
/userDtos - this is bad, because I can have more than one DTOs for representing users,
/users/dto - this is also bad
/users?name=true,surname=true - this one is also bad, it indicates that we are filtering the result, but we are not; we're just filtering fields.
For sure someone already had this problem before, but I couldn't find anything on the Internet.
A similar concept is called partial response which provide an option to let client to specify which fields to include in the response using the query parameters like:
:
/user?fields=name,surename
Basically you define a syntax for you own query language to represent a selection of fields. Here and Google Cloud API are some examples.
By taking this concept to a more coarse-grained level , you can use query parameter "view" to define different predefined combination of fields such as:
/users //default view if no "view" query parameter is specified
/users?view=admin //maybe this view will not show age field
/users?view=hr //maybe this view only show the fields that are accessible to HR
While performing testing RESTFUL Web Service using POSTMAN, I encountered the below error :
415 UnSupported Media Type
Currently in my code, I'm using MediaType.TEXT_PLAIN. This is due to one of the answer from page enter link description here telling that if you need to return integer, you need to use TEXT_PLAIN.
May I know is the data that I provide in the web service is compatible with TEXT_PLAIN or not.
#POST
#Path("/post")
#Produces(MediaType.TEXT_PLAIN)
public int adaptiveAuth( #FormDataParam("uuid") String uuID,
#FormDataParam("browserinfo") String browserInfo,
#FormDataParam("ipint") long ipInt,
#FormDataParam("lat") double latiTude,
#FormDataParam("longitude") double longiTude,
#FormDataParam("sessionid") String sessionID,
#FormDataParam("spid") String spID,
#FormDataParam("tr") int tR,
#FormDataParam("jsnum") int jsNum,
#FormDataParam("fingerprint") String fingerPrint,
#FormDataParam("methodset") MethodClass[][] methodSet) throws SQLException{
The way I tested in Postman are describe as below:
Solution.
1. Remove header value in SOAP UI.
2. I was unable to process an array in Jersey. Instead of process MethodClass [][] methodSet, I'm sending the value one by one.
3. I also change back from MediaType.MULTIPART_FORM_DATA to MediaType.PLAIN_TEXT
My code is working now.
Thanks for the help.
I've an entity with an ID of
public string ID {get;set;}
activities/1
(which comes from RavenDB).
I'm registering the following routes in my ServiceStack AppHost
Routes
.Add<Activity>("/activities")
.Add<Activity("/activities/{id}");
I'm using a backbone app to POST and PUT to my REST Service.
What happens out-of-the-box:
id property is serialized into the json as "activities/1"
id property is encoded into route as "activities%2F1"
ServiceStack gives precedence to the URL based id property, so my string gets the encoded value which is no use to RavenDb directly.
The options I'm aware of:
Change backbone to post to "/activities" and let the JSON Serialiser kick in
Change RavenDb ID generation to use hyphens rather than slashes
Make my Id property parse for the encoded %2F on set and convert to a slash
Both have disadvantages in that I either lose RESTfulness in my API, which is undesirable, or I don't follow RavenDb conventions, which are usually sensible out-of-the-fox. Also, I've a personal preference for having slashes.
So I'm wondering if there are any other options in servicestack that I could use to sort this issue that involve less compromise? Either Serialiser customisation or wildcard routing are in my head....
I have the same problem with ASP.Net WebAPI, so I don't think this is so much a ServiceStack issue, but just a general concern with dealing with Raven style id's on a REST URL.
For example, let's say I query GET: /api/users and return a result like:
[{
Id:"users/1",
Name:"John"
},
{
Id:"users/2",
Name:"Mary"
}]
Now I want to get a specific user. If I follow pure REST approach, the Id would be gathered from this document, and then I would pass it in the id part of the url. The problem here is that this ends up looking like GET: /api/users/users/1 which is not just confusing, but the slash gets in the way of how WebAPI (and ServiceStack) route url parameters to action methods.
The compromise I made was to treat the id as an integer from the URL's perspective only. So the client calls GET: /api/users/1, and I define my method as public User Get(int id).
The cool part is that Raven's session.Load(id) has overloads that take either the full string form, or the integer form, so you don't have to translate most of the time.
If you DO find yourself needing to translate the id, you can use this extension method:
public static string GetStringIdFor<T>(this IDocumentSession session, int id)
{
var c = session.Advanced.DocumentStore.Conventions;
return c.FindFullDocumentKeyFromNonStringIdentifier(id, typeof (T), false);
}
Calling it is simple as session.GetStringIdFor<User>(id). I usually only have to translate manually if I'm doing something with the id other than immediately loading a document.
I understand that by translating the ids like this, that I'm breaking some REST purist conventions, but I think this is reasonable given the circumstances. I'd be interested in any alternative approaches anyone comes up with.
I had this problem when trying out Durandal JS with RavenDB.
My workaround was to change the URL very slightly to get it to work. So in your example:
GET /api/users/users/1
Became
GET /api/users/?id=users/1
From jQuery, this becomes:
var vm = {};
vm.users = [];
$.get("/api/users/?" + $.param( { id: "users/1" })
.done(function(data) {
vm.users = data;
});
I'm using the Dojo datagrid client side, it works well and according to documentation it generates the following GET request when clicking on the column header:
GET http://localhost:8080/books/rest/books?sort(+isbn)
Problem is that I can't interpret the query parameter "sort(+isbn)" on the server side using the Apache Wink framework, because there's no value set for it. E.g. I'd expect something like "sort=+isbn" instead.
Here's my server side code:
#Path("/books")
public class BookServiceImpl implements BookService {
...
#GET
#Produces(MediaType.APPLICATION_JSON)
public String getBook(#QueryParam("sort") String sortBy) {
System.out.println("Received Queryparam for sort is " + sortBy);
return "";
}
}
Since "sort(+isbn)" has no value assigned to it, it appears to be an invalid query parameter. Not sure why Dojo datagrid uses this convention.
Would appreciate help as to how to work around this on the Java side, ideally using Wink or another mechanism to process GET requests.
Try to use #Context UriInfo to get the full uri info, call to UriInfo.getQueryParameters to get all query params. I believe sort(+isbn) will be there.
I have an ASP.NET MVC 2 application which in part allows a user to filter data and view that data in a JQGrid.
Currently this consists of a controller which initialises my filter model and configures how I wish my grid to be displayed. This information is used by a view and a partial view to display the filter and the grid shell. I use an editor template to display my filter. The JQGrid makes use of a JsonResult controller action (GET) to retrieve the results of the filter (with the addition of the paging offered by the grid - only a single page of data is returned by the GET request. The Uri used by the grid to request data contains the filter model as a RouteValue - and currently contains a string representation of the current state of the filter. A custom IModelBinder is used to convert this representation back into an instance of the filter model class.
The user can change the filter and press a submit button to get different results - this is then picked up by an (HttpPost) ViewResult action which takes the filter model - reconstituted by a further model binder and causes the grid shell to be updated.
So I have:
FilterModel
Represents the user's desired filtering characteristics
FilterModelEditorTemplateSubmissionBinder : DefaultModelBinder - used to convert the request information supplied from a user changing their filtering characteristics into the appropriate FilterModel instance.
FilterModelStringRepresentationBinder : IModelBinder - used to convert the encoded filter from the JQGrid GET request for data so the correct request is made of the service which is ultimately performing the query and returning the relevant data.
ViewResult Index() - constructs a default filter, configures the grid specification and returns the view to render the filter's editor template, and the grid shell.
[HttpPost]ViewResult Filter(FilterModel filter) - takes the new filter characteristics and returns the same view as Index(). Uses FilterModelEditorTemplateSubmissionBinder to bind the filter model.
JsonResult GetData(FilterModel filter, string sidx, string sord, int page, int rows) - called from the JQGrid in order to retrieve the data. Uses FilterModelStringRepresentationBinder to bind the filter model.
As a complication, my filter model contains a option to select a single value from a collection of items. This collection is retrieved from a service request and I don't want to keep querying for this data everytime I show the filter, currently I get it if the property is null, and then include the options hidden in the editor template and encoding in the string representation. These options are then reconstituted by the relevant model binder.
Although this approach works I can't help but feel that I am having to basically reinvent viewstate in order to maintain my filter and the included options. As I am new to ASP.NET MVC but am very happy with classic ASP and ASP.NET Web Forms I thought I'd throw this out there for comment and guidance as to find a way which more closely fits with the MVC pattern.
It seems to me that the best way in to divide some actions which provide pure data for the jqGrid from other controller action. Such jqGrid-oriented actions can have prototype like:
JsonResult GetData(string filter, string sidx, string sord, int page, int rows)
I personally prefer to implement this part as WCF service and to have this WCF service as a part of the same ASP.NET site. In general it's much more the matter of taste and depends on your other project requirements.
This part of you ASP.NET site could implement users authentication which you need and can be tested with unit tests exactly like other actions of your controllers.
The views of the ASP.NET MVC site can have empty data for jqGrids, and have only correct URLs and probably generate the HTML code depends on the users permission in the site. Every page will fill the data of jqGrids with respect of the corresponds requests to the server (request to the corresponding GetData action).
You can use HTTP GET for the data for the best data caching. The caching of data is the subject of a separate discussion. If you do this, you should use prmNames: { nd:null } in the definition of jqGrid to remove unique nd parameter with the timestamp added per default to every GET request. To have full control of the data caching on the server side you can for example add in HTTP headers of the server responses both "Cache-Control" set to "max-age=0" and "ETag" header with the value calculated based of the data returned in the response. You should test whether the request from the client has "If-None-Match" HTTP header with the value of "ETag" coresponds the data cached on the client. Then you should verify whether the current data on the server (in the database) are changed and, if there are not changed, generate a response with an empty body (set SuppressEntityBody to true) and return "304 Not Modified" status code (HttpStatusCode.NotModified) instead of default "200 OK". A more detail explanation is much more longer.
If you don't want optimize you site for caching of HTTP GET data for jqGrids you can either use HTTP POST or don't use prmNames: { nd:null } parameter.
The code inside of JsonResult GetData(string filter, string sidx, string sord, int page, int rows) is not very short of cause. You should deserialise JSON data from the filter string and then construct the request to the data model depends on the method of the data access which you use (LINQ to SQL, Entity Model or SqlCommand with SqlDataReader). Because you have this part already implemented it has no sense to discuss this part.
Probably the main part of my suggestion is the usage of clear separation of controller actions which provide the data for all your jqGrids and the usage of MVC views with empty data (having only <table id="list"></table><div id="pager"></div>). You should also has no doubt with having a relative long code for analyzing of filters which come from the Advance Searching feature of the jqGrid and generating or the corresponding requests to your data model. Just implement it one time. In my implementation the code in also relatively complex, but it is already written one time, it works and it can be used for all new jqGrids.
I made this once, very simple.
pseudo code:
Controller
[HttpGet]
public ActionResult getList(int? id){
return PartialView("Index", new ListViewModel(id??0))
}
ViewModel
public class ListViewModel{
//ObjectAmountPerPage is the amount of object you want per page, you can modify this as //parameter so the user
//can choose the amount
public int ObjectAmountPerPage = 20 //you can make this into a variable of any sort, db/configfile/parameter
public List<YourObjectName> ObjectList;
public int CurrentPage;
public ListViewModel(id){
Currentpage = id;
using (MyDataContext db = new MyDataContext()){
ObjectList = db.YourObjectName.OrderBy(object=>object.somefield).getListFromStartIndexToEndIndex(id*ObjectAmountPerPage ,(id*ObjectAmountPerPage) +20).toList();
}
}
}
Now Create A RenderPartial:
PartialView
<#page inherit="IEnumerable<ListViewMode>">
<%foreach(YourObjectName object in Model.ObjectList){%>
Create a table with your fields
<%}%>
And create a view that implements your Jquery, other components+your partialView
View
<javascript>
$(function(){
$("#nextpage").click(function(){
(/controller/getlist/$("#nextpage").val(),function(data){$("#yourlist").html = data});
});
});
</javascript>
<div id="yourlist">
<%=Html.RenderPartial("YourPartialView", new ListViewModel())%>
</div>
<something id="nextpage" value"<%=Model.CurentPage+1%>">next page</something>
I hope this helps, this is according to the MVC- mv-mv-c principle ;)
Model-View -(modelview) - control