How to consume ItemService of Sitecore Services Client - asp.net

There are few tabs and a content panel. End user (EU) will click on a tab and the panel's content changes asynchronously.
eg:
<ul class="products">
<li class="p1">product1</li>
<l1>product2</li>
</ul>
<div class="product-data">
NAME : <span> <product name here> </span>
COLOR : <span> <product color here> </span>
</div>
<script>
(function ($) {
$(".p1").click(function(){
HelloWorld();
});
function HelloWorld() {
$.ajax({
type: "POST",
url: 'http://mysite/Services/myService.asmx/HelloWorld',
data: "pid:" + someId,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
alert(response.d);
},
failure: function (response) {
alert("ERR-" + response.d);
}
});
}
})(jQuery);
</script>
Each product is an item in the content tree. When EU clicks on a tab, an ajax call is made, which will get that product's data and bind to the panel.
I'm trying to achieve this functionality using a web service and return JSON to front end:
[WebMethod]
public string HelloWorld(string pid)
{
//get a certain product details and return it as JSON
Sitecore.Data.Items.Item newItem = Sitecore.Context.Item;
if (newItem != null)
return newItem.Name;
else
return "it was null";
}
Of course, the result is "it was null" as I understand this has to be done with Item Service of SSC, but unable to find any suitable/beginner example.
Using sitecore 8 with ASP.NET

You will want to use Entity Service instead of Item Service of Sitecore.Services.Client. It will let you serve custom models that are specific to the type of data you want to display for each product.
First, you will need to create a class to represent your Product. It needs to implement Sitecore.Services.Core.Model.EntityIdentity.
Just an FYI if you are using Sitecore SPEAK ensure you define a property named itemId with that casing, SPEAK requires it.
public class ProductModel : Sitecore.Services.Core.Model.EntityIdentity
{
public string itemId { get; set; }
public string ProductName { get; set; }
...
}
When Developing with Sitecore.Services.Client you'll want to follow Sitecore best practices. A simple Controller passing off all the computation to a Repository for the type of Model. In this case a ProductRepository.
The controller needs to implement EntityService with the type of Model.
[ValidateAntiForgeryToken]
[ServicesController]
public class ProductController : EntityService<ProductModel>
{
public ProductController(IRepository<ProductModel> repository)
: base(repository)
{
}
public ProductController()
: this(new SitecoreItemRepository())
{
}
}
This controller exposes the methods of the Repository Get, GetById, Add, etc.
public class ProductRepository : Sitecore.Services.Core.IRepository<ProductModel>
{
public ProductModel FindById(string id)
{
// code to find by id
}
}
See here for a full example of Entity Service and here

Related

Posting to MVC Controller, ViewModel stays null

I have this ASP.NET MVC controller action and viewmodel:
public JsonResult Upload(UploadModel MyModel)
{
//do stuff with MyModel
}
public class UploadModel
{
public string Name;
}
And in Angular, a method for submitting a form to this action:
function onSubmit() {
console.log(vm.model);
$http.post('/Home/Upload', vm.model).
then(function (response)
{
// do stuff with response
});
};
When I log vm.model, it looks like this:
{ Name : "Some cool name" }
It looks exactly the same in the request payload. But when I post this object to the Upload method, Name appears to be null. However, when I change the Upload method to accept just a string instead of a viewmodel:
public JsonResult Upload(String Name)
{
//do stuff with Name
}
And post the exact same object to it, it does get recognized.
What is going on? Why isn't MVC recognizing my JS object as a viewmodel?
Name is a field, not a property (no getter/setter) so the DefaultModelBinder cannot set the value. Change it to
public class UploadModel
{
public string Name { get; set; }
}
You should serialize your form data and then post
var cpformdata = $("#Myform form").serializeArray();
$.ajax({
url: url,
type: 'POST',
data: cpformdata,
success: function (data) {
}

ASP.Net Web API 404 Error on 2nd API

So I already have 1 Web API set up and working great, but now that I am trying to set up my own admin panel ( which I did ), I need to use the DeleteUser() function from the Web API named AdminApi but I can't seem to get it working. I keep getting 404 error while giving the path that the API should be at.
Web Api Config:
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}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Global :
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//Create the custom role and user
RoleActions roleActions = new RoleActions();
roleActions.AddUserAndRole();
}
AdminApi :
[Authorize(Roles = "admin")]
public class AdminApiController : ApiController
{
public string test()
{
return "test";
}
[HttpPost]
public string DeleteUser(string id)
{
ApplicationDbContext db = new ApplicationDbContext();
var user = db.Users.Find(id);
if (user != null)
{
string email = user.Email;
db.Users.Remove(user);
return "Succesfully deleted user : " + email;
}
else
return "Failed to delete user.";
}
}
Ajax :
function deleteUser (id)
{
$.ajax({
url: '../api/AdminApi/DeleteUser',
type: 'POST',
contentType: "application/json",
dataType: 'json',
data: JSON.stringify(id),
success: function (data) {
alert(data);
},
error: function (x, y, z) {
alert(x + '\n' + y + '\n' + z);
}
});
}
The ajax function is called on the page /Admin/AdminPage
so to get to the web api -> ../api/AdminApi
and the function to delete users is DeleteUser
-> ../api/AdminApi/DeleterUser
I don't get why I get a 404 error. I can understand if my function DeleteUser is not working since I haven't tested it, but I can't test it if I can't get in the function.
The issue is related to how you use the attribute:
[Route("DeleteUser")]
If you use the Attribute Route. at Method level what it does is to define new route or more routes therefore the way you should use it is like [Route('Url/path1/route1')]:
As an example of how it works:
//GET api/customer/GetMetaData
[Route('/api/customer/GetMetaData')]
public string Get2(){
//your code goes here
}
If you will be declaring several Routes in your class then you can use RoutePrefix attribute like [RoutePrefix('url')] at class level. This will set a new base URL for all methods your in Controller class.
For example:
[RoutePrefix("api2/some")]
public class SomeController : ApiController
{
// GET api2/some
[Route("")]
public IEnumerable<Some> Get() { ... }
// POST api2/some/DeleteUser/5
[HttpPost]
[Route("DeleteUser/{id:int}")]
public Some DeleteUser(int id) { ... }
}
Update
By default Web API looks at the routing URL first, what is in your [Route] I mean and it tries to match it against your post. However if your method has a complex object as parameter WebApi can't get the values from the request URI because the parameter is a complex type Web API uses a media-type formatter to read the value from the request body.
Since your string id is not a complex object and it is part of your Route WebApi expects it as part of your URL not the body. Try this instead:
[HttpPost]
public string DeleteUser([FromBody]string anotherName)

ActionResult not calling to partialView when calls from Ajax hit in ASP.Net MVC

I want to call partial view after calling ActionResult with ajax hit and render partial view data into main view. But when calling to ActionResult, its calling perfectly but not calling to Partial view and returning error message in ajax.
Ajax method is as:
function postComment() {
var comment = $("#addComment").val();
var pathname = window.location.pathname.split("/");
var title = pathname[pathname.length - 1];
alert(comment);
jQuery.ajax({
type: 'POST',
dataType: "html",
url: "/Blogger/PostComment",
data: JSON.stringify({ 'comment': comment, "title": title ,"type":"Blogs"}),
contentType: "application/json; charset=utf-8",
success: function (result) {
$("#divAnswerSchemeContainer1").html(result);
},
error: function (e) {
alert("error");
}
});
}
Action Result is:
public ActionResult PostComment(string comment, string title, string type) {
return PartialView("~/Views/Shared/_partialShowComments", db.SocialMediaComments.ToList());
}
And the partial view is:
#model IEnumerable<DataModel.SocialMediaComment>
<div id="divAnswerSchemeContainer1">
#foreach (var item in Model)
{
#item.Comment
#item.CreatedDate
#item.BlogeName
}
</div>
Action result calling fine with ajax but partial view not hitting well and returning error message from ajax.
Browser console showing error message:
"NetworkError: 500 Internal Server Error - http://localhost:6767/Blogger/PostComment"
At first you have to create viewmodel.
public class Post{
public string comment {get;set;}
public string title {get;set;}
public string type {get;set;}
}
you action controller be like.
public ActionResult PostComment(Post post) {
return PartialView("~/Views/Shared/_partialShowComments", db.SocialMediaComments.ToList());
}

Unobtrusive validation not working for custom validation attribute

I am trying to write a custom validation attribute in MVC and I can't make it work as an unobstructive validation. It works fine with postback (kinda) but as the form is on a dialog, I must have an Ajax style call or it's unusable. Maybe what i am trying to do is unachieveable. The problem is i need to connect to a database to do the check.
I made a simple demo for my problem.
My model
public class Customer
{
public int Id { get; set; }
[IsNameUnique]
[Required]
public string Name { get; set; }
}
The view:
#model WebApplication1.Models.Customer
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { #id = "NewForm" }))
{
#Html.ValidationSummary(true)
#Html.EditorFor(m => m.Name)
#Html.ValidationMessageFor(m => m.Name)
<br />
<input type="submit" value="Submit" />
}
Custom validation class
public class IsNameUnique : ValidationAttribute
{
private CustomerRepository _repository;
public IsNameUnique()
{
_repository = new CustomerRepository();
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if(value != null)
{
var isValid = _repository.IsNameUnique(value);
if(!isValid)
{
return new ValidationResult("Name must be unique");
}
}
return ValidationResult.Success;
}
}
Post method
[HttpPost]
public ActionResult Index(Customer customer)
{
if(ModelState.IsValid)
{
//add customer
}
return View();
}
database call
class CustomerRepository
{
internal bool IsNameUnique(object value)
{
//call to database
return false;
}
}
There is a form with a name field. I need to check if name is already in the database.
My question is how can I do unobtrusive style validation in my case? I found other posts on SO about IClientValidatable but none of them really show what I need to do. i.e. none of them do check against a database. Thanks.
Basically "unobtrusive validation" means "Client-Side validation, defined in an unobtrusive way". Key point here is "Client-Side", that is, validation which can be done via JavaScript in client browser.
Checking for name uniqueness involves server-side resources, and while it can be done in JavaScript using AJAX requests to server, usually people decide not to do so.
You can follow this guide for details of implementing unobtrusive validation: http://thewayofcode.wordpress.com/tag/custom-unobtrusive-validation/
In general you will need to do the following:
Enable unobtrusive validation in web.config
Include jQuery, jQuery Validate and unobtrusive scripts into your page
Implement IClientValidatable for your custom validation attribute
Implement and register client-side rules for your custom attribute
You may want to look into the [Remote] validation attribute. Just make a controller method that returns a JsonResult and map it to the remote attribute. This is probably the easiest way to accomplish what you're looking to do.
[Remote( "IsNameUnique", "Customers", HttpMethod = "post" )]
public override string Name { get; set; }
[HttpPost]
public JsonResult IsNameUnique( string name )
{
// Code
}
If you want to implement this as a custom validation, you need to do the following:
In your attribute, implement IClientValidatable. This requires you to implement GetClientValidationRules() method. Return a new client validation rule with your type and parameters.
Here's an example:
https://github.com/DustinEwers/dot-net-mvc-ui-demos/blob/master/ASPNET4/UIDemos/UIDemos/Attributes/PastDateOnlyAttribute.cs
Then you need to implement a jQuery validation rule. This is where you'd make your ajax call:
jQuery.validator.addMethod("pastdateonly", function (val, element, params) {
var value = $.trim($(element).val());
if (value === "") return true;
var maxDate = params.maxdate,
dteVal = new Date(value),
mxDte = new Date(maxDate);
return dteVal < mxDte;
});
Then add an unobtrusive adapter method.
jQuery.validator.unobtrusive.adapters.add("pastdateonly", ["maxdate"],
function (options) {
options.rules["pastdateonly"] = {
maxdate: options.params.maxdate
};
options.messages["pastdateonly"] = options.message;
}
);
Example:
https://github.com/DustinEwers/dot-net-mvc-ui-demos/blob/master/ASPNET4/UIDemos/UIDemos/Scripts/site.js

Web API Model properties are null

My Controller is able to create the model object but all the properties related to model and assigned to null values
Environment : VS 2010, ASP.NET MVC RC latest, jQuery 1.7.1
Following is the Web API Controller code
public class Customer
{
public string Name { get; set; }
public string City { get; set; }
}
public class UserController : ApiController
{
public Customer Post(Customer user)
{
return user;
}
}
Following is the ajax calling code
$.ajax('/api/user',
{
ContentType: "application/x-www-form-urlencoded; charset=UTF-8",
dataType: 'json',
type: 'POST',
data: JSON.stringify({ "Name": "Scott", "City": "SC" })
});
Controller does create the model "Customer" object but both "Name" and "City" properties are null.
What's wrong here?
I have read many similar issues on this site but could not find the solution.
This blog here gives a good idea about how Model Binding differs in ASP.NET Web project and a ASP.NET Web API project.
I was facing a similar issue in the project I was working on and adding a explicit ModelBinding attribute made the property values stick
Requested Data :
var customer = { Name : "customer Name", City : "custome City" }
$.ajax({
url : ...
type: ...
data : customer
});
Request Class:
public class Customer
{
public string Name { get; set; }
public string City { get; set; }
}
Controller :
public class UserController : ApiController
{
[HttpGet]
public Customer Get([ModelBinder] Customer user)
{
// fetch from whereever
}
}
I'm going through the same issue right now. I'm not 100% sure of the answer, but below is my javascript and I've added to the class [DataModel] and to the Properties [DataMember]. That is:
[DataModel]
public class Customer
{
[DataMember] public string Name { get; set; }
[DataMember] public string City { get; set; }
}
And My JavaScript
$(document).ready(function () {
// Send an AJAX request
$.getJSON("api/session/GetAll",
function (data) {
// On success, 'data' contains a list of products.
$.each(data, function (key, val) {
//debugger;
// Format the text to display.
//var str = val.Name + ': $' + val.Price;
var str = 'abcd';
// Add a list item for the product.
$('<li/>', { text: str })
.appendTo($('#products'));
});
});
});
Faced a similar issue and my problem turned out to be invalid JSON in the body of the request. There was a comma missing after one of the fields. So it seems like the default model binder just binds null if there are any syntax errors in the JSON.
Next time this happens, ensure that there is no "internal" keyword on the property setter. That is:
instead of public string Comment {get; internal set;}
use public string Comment {get; set;}
This fixed it for me.

Resources