WebMethod return values in JSON format - asp.net

How to return values from Webmethod to the client in JSON format?
There are two static int values that i want to return.
Do I need to create new object with those 2 properties and return it?
The GetStatus() method is called frequently and i don't like the idea of creating a special object each time just for json formatting...
[WebMethod]
public static int GetStatus()
{
int statusProcess,statusProcessTotal;
Status.Lock.EnterReadLock();
statusProcess=Status.Process; //Static field
statusProcessTotal=Status.ProcessTotal; //Static field
Status.Lock.ExitReadLock();
return ...
}
On client side I catch the return value in :
function OnSucceeded(result, userContext, methodName)
(PageMethods.GetStatus(OnSucceeded, OnFailed);)

I would just go with an object. It fits with what you need to do. If you have two return values you have to put them together in a structured way.
public class StatusResult
{
public int StatusProcess { get; set; }
public int StatusProcessTotal { get; set; }
}
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public StatusResult GetStatus()
{
int statusProcess,statusProcessTotal;
//Status.Lock.EnterReadLock();
statusProcess = 5;
statusProcessTotal = 1; //Static field
var result = new StatusResult();
result.StatusProcess = statusProcess;
result.StatusProcessTotal = statusProcessTotal;
return result;
}

Related

ASP.NET GET request always returns blank

I'm trying to make an endpoint to return a JSON response. I've tried narrowing it down to just the object and all I receive is {} or [{}] as a response. After debugging I confirmed that the object was being created correctly but when returning the response it was always blank. Below is simplified code but still has the same issue. Any ideas what I'm doing wrong?
[Route("{application}")]
[HttpGet]
public IActionResult Get(string application)
{
List<RequestedSetting> requestedSettings = new List<RequestedSetting>();
RequestedSetting rs = new RequestedSetting("foo", "bar");
requestedSettings.Add(rs);
return Json(requestedSettings);
}
public class RequestedSetting
{
public string Name;
public string Value;
public RequestedSetting(string name, string value)
{
Name = name;
Value = value;
}
}
I've also tried this:
[Route("{application}")]
[HttpGet]
public List<RequestedSetting> Get(string application)
{
List<RequestedSetting> requestedSettings = new List<RequestedSetting>();
RequestedSetting rs = new RequestedSetting("foo", "bar");
requestedSettings.Add(rs);
return requestedSettings;
}
Fields are not supported for serialisation in System.Text.Json. see the docs for more info.
Instead, change your class to use properties:
public class RequestedSetting
{
public string Name { get; set; }
public string Value { get; set; }
public RequestedSetting(string name, string value)
{
Name = name;
Value = value;
}
}

Data Annotations to sanitize request and response before logging

I'm looking for a reliable solution to log details of requests and responses made to and from our controllers. However, some of the data passing through contains sensitive information that should not be written to a log.
In the controller, the inbound request is bound to a single model from the request body, and as the request is answered, a single model is passed to the Ok() result like this (very simplified):
[HttpGet]
[Route("Some/Route")]
public IHttpActionResult SomeController([FromBody] RequestType requestObj)
{
ResponseType responseObj = GetResponse(requestObj)
return this.Ok(responseObj);
}
Now my goal is to somehow log the contents of the request and response object at the beginning and end of the controller, respectively. What I would like to do is bind the models first, then log out their attributes. An example of the RequestType is something like:
public class RequestType
{
public string SomeAttribute { get; set; }
public string AnotherAttribute { get; set; }
public string Password{ get; set; }
}
And the log would look something like:
[date-time] Request to SomeController:
SomeAttribute: "value_from_request"
AnotherAttribute: "another_value"
Password: "supersecret123"
Now clearly we don't want the password to be logged. So I would like to create a custom data annotation that would not log certain fields. Its use would look like this (updated RequestType):
public class RequestType
{
public string SomeAttribute { get; set; }
public string AnotherAttribute { get; set; }
[SensitiveData]
public string Password{ get; set; }
}
Where would I start with this? I'm not incredibly familliar with .NET, but know that there are many sort of magic classes that can be subclassed to override some of their functionality. Is there any such class that can help here? Even better, is there any way to do this during the model binding? So we could catch errors that occur during model binding as well?
We should be able to achieve what you're looking for with an ActionFilterAttribute.
Capture Requests Attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public sealed class CaptureRequestsAttribute : ActionFilterAttribute // *IMPORTANT* This is in the System.Web.Http.Filters namespace, not System.Web.Mvc
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var messages = actionContext.ActionArguments.Select(arg => GetLogMessage(arg.Value));
var logMessage = $"[{DateTime.Now}] Request to " +
$"{actionContext.ControllerContext.Controller}]:\n{string.Join("\n", messages)}";
WriteToLog(logMessage);
base.OnActionExecuting(actionContext);
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var result = actionExecutedContext.Response.Content as ObjectContent;
var message = GetLogMessage(result?.Value);
var logMessage = $"[{DateTime.Now}] Response from " +
$"{actionExecutedContext.ActionContext.ControllerContext.Controller}:\n{message}";
WriteToLog(logMessage);
base.OnActionExecuted(actionExecutedContext);
}
private static void WriteToLog(string message)
{
// todo: write you logging stuff here
}
private static string GetLogMessage(object objectToLog)
{
if (objectToLog == null)
{
return string.Empty;
}
var type = objectToLog.GetType();
var properties = type.GetProperties();
if (properties.Length == 0)
{
return $"{type}: {objectToLog}";
}
else
{
var nonSensitiveProperties = type
.GetProperties()
.Where(IsNotSensitiveData)
.Select(property => $"{property.Name}: {property.GetValue(objectToLog)}");
return string.Join("\n", nonSensitiveProperties);
}
}
private static bool IsNotSensitiveData(PropertyInfo property) =>
property.GetCustomAttributes<SensitiveDataAttribute>().Count() == 0;
}
Sensitive Data Attribute
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class SensitiveDataAttribute : Attribute
{
}
Then, you can just add it to your WebApi controller (or a specific method in it):
[CaptureRequests]
public class ValuesController : ApiController
{
// .. methods
}
And finally your models can just add the SensitiveDataAttribute:
public class TestModel
{
public string Username { get; set; }
[SensitiveData]
public string Password { get; set; }
}
This does not make use of DataAnnotations,however, One way that comes to mind would be to use the serialization. If your payload is within a reasonable size you could serialize and deserialize your RequestType class when reading and writing to/from a log. This would require a custom serialization format or making use of the default, xml.
[Seriliazeble()]
public class RequestType
{
public string SomeAttribute { get; set; }
public string AnotherAttribute { get; set; }
[NonSerialized()]
public string Password{ get; set; }
}
Using the above attribute will omit Password from serialization. Then you copuld proceed to Logger.Log(MySerializer.Serialize(MyRequest)); and your sensitive data will be omitted.
This link describes the approach in detail.
For xml serialization, simply use the XmlSerializer class.
public class MySerializationService
{
public string SerializeObject(object item)
{
XmlSerializer serializer = new XmlSerializer(item.GetType());
System.IO.MemoryStream aMemStr = new System.IO.MemoryStream();
System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(aMemStr, null);
serializer.Serialize(writer, item);
string strXml = System.Text.Encoding.UTF8.GetString(aMemStr.ToArray());
return strXml;
}
public object DeSerializeObject(Type objectType, string objectString)
{
object obj = null;
XmlSerializer xs = new XmlSerializer(objectType);
obj = xs.Deserialize(new StringReader(objectString));
return obj;
}
}
Then using the above or similar methods you can read and write in a custom format.
Write :
string logData=new MySerializationService().SerializeObject(myRequest);
Read :
RequestType loggedRequest= (RequestType)new MySerializationService().DeSerializeObject(new RequestType().GetType(), logData);

Web method return JSON result in two level (In kendoUI Datasource)

This the server side Code
[System.Web.Services.WebMethod]
public static object GetDevelopers()
{
return new DqListViewModel(DQContext.Service._IDqs_IssueRepository.SelectList().ToArray(), 10);
}
View Model
public class DqListViewModel
{
public Array Data { get; set; }
public int Count { get; set; }
public DqListViewModel(Array data, int count)
{
this.Data = data;
this.Count = count;
}
}
This is the JSON return Value
why the JSON result has tow level object. I am not supposed to have "d" level?
Please check the below link. http://encosia.com/a-breaking-change-between-versions-of-aspnet-ajax/
This is not an issue from Knedo-ui but it is the functionality of the Asp.net
Please try with the below link, may be it will help you.
How to bind JSON child array to Kendo grid

Cache an object in a .ashx handler

I'm not sure of the best way to approach this, but here is my scenario.
Let's say I have an ashx handler that feeds out some data to an ajax request from the client. Something simple like:
public class Handler : IHttpHandler
{
private List<MyObject> data;
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
data = GetData();
string result;
switch (context.Request.QueryString["requestType"])
{
case "case":
result = Foo.Bar(data).GetJSON();
break;
// .. several more data conversitions / comparisions
}
context.Response.Write(result);
}
public class MyObject
{
public int Id { get; set; }
public string Data { get; set; }
}
}
How would I cache the list of MyObject's so it is not rebuilt every time a request is made to the service? Let's say the list of MyObject gets thousands of results and I want to keep it cached for ~1 minute. Could I make use of context.Cache?
Basically; I do not want to cache the handlers output. Just an object with data that resides in it.
Edit:
I am looking for something along the lines of:
data = (List<MyObject>) context.Cache["data"];
if (data == null || !data.Any())
{
data = GetData();
context.Cache.Insert("data", data, null, DateTime.Now.AddMinutes(1), System.Web.Caching.Cache.NoSlidingExpiration);
}
You can add result to HttpContext.Current.Cache, something like this :
var requestType = context.Request.QueryString["requestType"];
HttpContext.Current.Cache[requestType] = result;

Message 'The underlying connection was closed' when calling a WCF function

Initially, I called a function in my web service from my controller and next I paginate the result to only show 10 items in my view. I proceeded like this:
Controller:
public ActionResult Index(int? page)
{
var companies = _requestServiceClient.GetCompanies();
int currentPageIndex = page.HasValue ? page.Value - 1 : 0;
var companiesListPaged = companies.ToPagedList(currentPageIndex, defaultPageSize);
return View(companiesListPaged);
}
Service:
public IEnumerable<Company> GetCompanies()
{
using (var unitOfWork = UnitOfWorkFactory.Create())
{
var companyRepository = unitOfWork.Create<Company>();
return companyRepository.GetAll().MyInclude(x => x.City).ToList();
}
}
So the pagination was done after all data was retrieved from my service. It works but a lot of data was transmitted so not very efficient. I changed my code to do the pagination work directly in the service like this:
Controller:
[Authorize]
public ActionResult Index(int? page)
{
int currentPageIndex = page.HasValue ? page.Value - 1 : 0;
var companies = _requestServiceClient.GetCompaniesToPagedList(currentPageIndex, defaultPageSize);
return View(companies);
}
Service:
public IPagedList<Company> GetCompaniesToPagedList(int PageIndex, int PageSize)
{
using (var unitOfWork = UnitOfWorkFactory.Create())
{
var companyRepository = unitOfWork.Create<Company>();
var companies = companyRepository.GetAll().MyInclude(x => x.City).ToList();
return companies.ToPagedList(PageIndex, PageSize);
}
}
It compiles but at runtime I got the error:
The underlying connection was closed: A connection that was expected to be kept alive was closed by the server.
Any idea? Why does this change in my code gives me this error?
I didn't change anything else.
Thanks.
UPDATE
And here is the code for the IPagedList
public interface IPagedList<T> : IList<T>
{
int PageCount { get; }
int TotalItemCount { get; }
int PageIndex { get; }
int PageNumber { get; }
int PageSize { get; }
bool HasPreviousPage { get; }
bool HasNextPage { get; }
bool IsFirstPage { get; }
bool IsLastPage { get; }
}
And for ToPagedList
public static IPagedList<T> ToPagedList<T>(this IEnumerable<T> source, int pageIndex, int pageSize, int? totalCount = null)
{
return new PagedList<T>(source, pageIndex, pageSize, totalCount);
}
I would suggest checking if the amount of data returned by the call may exceed the limits defined in the ReaderQuotas element of your Binding.

Resources