I have this code in C# mvc Controller:
[HttpPost]
public ActionResult Delete(string runId)
{
if (runId == "" || runId == null)
{
return this.Json(new { error = "Null or empty params" });
}
try
{
int userId = (int)Session["UserId"];
int run = Convert.ToInt32(runId);
CloudMgr cloud = new CloudMgr(Session);
cloud.DeleteRun(userId, run);
return this.Json(new { success = true });
}
catch (Exception ex)
{
return this.Json(new { error = ex.ToString() });
}
}
How I can access my Json "error" field in a ControllerTest to check if it is null or not?
[TestMethod]
public void DeleteWrongParam()
{
WhatIfController controller = new WhatIfController();
controller.ControllerContext =
TestUtils.CreateMockSessionControllerContext().Object as ControllerContext;
JsonResult result = controller.DeleteWhatIf(null) as JsonResult;
Assert.IsNotNull(result.Data.error); is what I would like to do. Any Ideas? Thanks.
JavaScriptSerializer is good for string and static type. Here you created anonymous type as Json(new { success = true }). This case, you had better used dynamic type.
JsonResult result = controller.DeleteWhatIf(null) as JsonResult;
dynamic dresult = result.Data;
Assert.IsTrue(dresult.succes);
You need to import Microsoft.CSharp dll to test project.
If test and your controller are in different assemblies, you need to make the test assembly a "friend" assembly of the controller assembly, like this:
[assembly: InternalsVisibleTo("testproject assembly name")]
You can use like this - the result will be the expected object definition. So in case of success, your success flag will be TRUE otherwise false and if false then you should expect that the error property will be updated with the error message.
JsonResult jsonResult = oemController.List() as JsonResult;
JavaScriptSerializer serializer = new JavaScriptSerializer();
Result result = serializer.Deserialize<Result>(serializer.Serialize(jsonResult.Data));
public class Result
{
public bool success ;
public string error;
}
If you returned an actually non-anonymous class, you could have just done:
var strongTypedResult = result as <YourStrongTypeHere>;
Related
I am trying to just return a very simple Anonymous object as Json like so:
[HttpGet]
public JsonResult GetJson()
{
return Json(new {
id = 1,
child = new
{
id = 12,
name = "woop woop"
}
});
}
Then I have a test case that verify that id = 1
[TestMethod]
public void TestMethod4()
{
var controller = new ValuesController();
dynamic result = controller.GetJson();
Assert.AreEqual(1, result.Value.id);
}
This result in:
Message: Test method UnitTestProject1.UnitTest1.TestMethod4 threw
exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
'object' does not contain a definition for 'id'
Your method returns a JsonResult which contains a property Data which is your object, so your code should be
[TestMethod]
public void TestMethod4()
{
var controller = new ValuesController();
dynamic result = controller.GetJson().Data; // returns { id = 1, child = new { id = 12, name = "woop woop" } }
Assert.AreEqual(1, result.id);
}
However anonymous objects are internal so you also need to make your object visible to your test project. In the AssemblyInfo.cs file of the project containing the ValuesController class, add
[assembly: InternalsVisibleTo("YourProjectName.UnitTestProject1")]
Ideally I would like to have an URL in following format:
/api/categories/1,2,3...N/products
And this would return all products for the specified categories. Having one API call with multiple category IDs saves me several database calls, thus improves performance.
I can easily implement this in a following way.
public HttpResponseMessage GetProducts(string categoryIdsCsv)
{
// <1> Split and parse categoryIdsCsv
// <2> Get products
}
However, this doesn't look like a clean clean solution, and possibly breaking SRP principle. I also tried using ModelBinder, however it adds parameters to query string.
Questions:
Is there a clean way to implement such URL structure?
Or is there a different/better approach to retrieve all products for multiple categories?
Please let me know if you need any further clarification.
I've just found an answer to my question. Route attribute had missing parameter when using ModelBinder.
[Route("api/categories/{categoryIds}/products")]
public HttpResponseMessage GetProducts([ModelBinder(typeof(CategoryIdsModelBinder))] CategoryIds categoryIds)
{
// <2> Get products using categoryIds.Ids
}
And CategoryIds would be
public class CategoryIds
{
public List<int> Ids{ get; set; }
}
And CategoryIdsModelBinder would be
public class CategoryIdsModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof(CategoryIds))
{
return false;
}
var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (val == null)
{
return false;
}
var key = val.RawValue as string;
if (key == null)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Wrong value type");
return false;
}
var values = val.AttemptedValue.Split(',');
var ids = new List<int>();
foreach (var value in values)
{
int intValue;
int.TryParse(value.Trim(), out intValue);
if (intValue > 0)
{
ids.Add(intValue);
}
}
if (ids.Count > 0)
{
var result = new CategoryIds
{
Ids= ids
};
bindingContext.Model = result;
return true;
}
bindingContext.ModelState.AddModelError(
bindingContext.ModelName, "Cannot convert value to Location");
return false;
}
We can use Post methods
[RoutePrefix ( "api/categories" )]
public class TestController
{
[HttpPost]
[Route ( "getProducts" )]
public HttpResponseMessage GetProducts ( HttpRequestMessage request )
{
HttpResponseMessage message = null;
string input = string.Empty;
input = request.Content.ReadAsStringAsync ().Result;
var ids = Newtonsoft.Json.JsonConvert.DeserializeObject<List<string>> ( input );
}
}
Unfortunately Web API can not parse your data as array or as some kind of your custom object out of the box.
If you want to parse your url param as array you can try to do:
Write your own route constraint which will read and convert your param from string to array of ints/strings/whatever;
Write your custom type converter and use it with your data model;
write your value provider and also use it with your data model
Use parameter binding
Moreover you can always use query params which is never will break principles of REST :)
Please see more details about here and here
Hope that helps
I've set up this test method on a controller to strip out any complication to it. Based off of all the results I've found from searching this should work. I'm not sure what I'm missing here.
public JsonResult test()
{
return Json(new { id = 1 });
}
This is the error I get.
Cannot implicitly convert type 'System.Web.Http.Results.JsonResult' to 'System.Web.Mvc.JsonResult'
you should return a JsonResult instead of just Json
public JsonResult test()
{
var result = new JsonResult();
result.Data = new
{
id = 1
};
result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return result;
}
Try the following:
public System.Web.Http.Results.JsonResult test()
{
return Json(new { id = 1 });
}
It seems that Json does not generate a System.Web.Mvc.JsonResult which is expected as you are probably using System.Web.Mvc; but a System.Web.Http.Results.JsonResult.
The more generic one should also work:
public ActionResult test()
{
return Json(new { id = 1 });
}
NOTE:
In my MVC controllers the Json method does return a System.Web.Mvc.JsonResult. Are you inheriting from the default System.Web.Mvc.Controller?
Try
return Json(new { id = 1 }, JsonRequestBehavior.AllowGet);
You need to return the data through a model class rather than an anonymous class. Like:
public System.Web.Http.Results.JsonResult<modelClass> test(){
return Json(new modelClass(){ id=1 });
}
In MVC JsonResult is inherited from ActionResult which is in namespace System.Web.Mvc
thats why you should make the Reference to System.Web.Mvc.JsonResult as::
public System.Web.Mvc.JsonResult test()
{
return Json(new { id = 1 });
}
Put this in your Using:
using System.Web.Http.Results;
Then Your Action:
public JsonResult<YourClass> Get(string Search)
{
var Search = Search
return Json(Search);
}
I'd like to search all the places like following where the Anonymous types in Controllers is being used as follows.
if(success) {
returnData = JsonConvert.SerializeObject(new { Success = true, Message = "Operation completed successfully" });
}
else {
returnData = JsonConvert.SerializeObject(new { Success = false, Message = "Operation failed" });
}
In above case the returnData is a JsonResult and its used in our Razor views to parse the status of the AJAX requests.
I want to minimize the usage of Anonymous types in such case as this could be maintenance issue as compiler would not raise any warning/errors if any of the line is written as new { Succes = true, Message = "Operation completed successfully"} and it would result into run-time error in the client-side scripts.
Any insights on restricting such situation or detecting such instances would be appreciated.
Why not search in solution/project for this with option "Use Regular Expressions" on?
\bnew\s*{
Just don't use an anonymous type. Create a new concrete type with the data you plan to use:
public class JSONMessage
{
public string Message { get; set; }
public bool Success { get; set; }
}
Then those lines can be changed to:
if(success) {
returnData = JsonConvert.SerializeObject(new JSONMessage(){ Success = true, Message = "Operation completed successfully" });
}
else {
returnData = JsonConvert.SerializeObject(new JSONMessage(){ Success = false, Message = "Operation failed" });
}
How about wrapping up the json call so you can have run time error/assert:
First an extension to detect anonymous from here: Determining whether a Type is an Anonymous Type
public static class TypeExtension {
public static Boolean IsAnonymousType(this Type type) {
var hasCompilerGeneratedAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Count() > 0;
var nameContainsAnonymousType = type.FullName.Contains("AnonymousType");
var isAnonymousType = hasCompilerGeneratedAttribute && nameContainsAnonymousType;
return isAnonymousType;
}
}
Then use that it your new method.
public static object JsonSeralize(object obj)
{
Debug.Assert(!obj.getType().IsAnonymousType());
return JsonConvert.SerializeObject(obj);
}
Now you can easily search for places that illegally call JsonConvert.SerializeObject directly.
When using MVC, returning adhoc Json was easy.
return Json(new { Message = "Hello"});
I'm looking for this functionality with the new Web API.
public HttpResponseMessage<object> Test()
{
return new HttpResponseMessage<object>(new { Message = "Hello" }, HttpStatusCode.OK);
}
This throws an exception as the DataContractJsonSerializer can't handle anonymous types.
I have replaced this with this JsonNetFormatter based on Json.Net.
This works if I use
public object Test()
{
return new { Message = "Hello" };
}
but I don't see the point of using Web API if I'm not returning HttpResponseMessage, I would be better off sticking with vanilla MVC. If I try and use:
public HttpResponseMessage<object> Test()
{
return new HttpResponseMessage<object>(new { Message = "Hello" }, HttpStatusCode.OK);
}
It serializes the whole HttpResponseMessage.
Can anyone guide me to a solution where I can return anonymous types within a HttpResponseMessage?
This doesn't work in the Beta release, but it does in the latest bits (built from http://aspnetwebstack.codeplex.com), so it will likely be the way for RC. You can do
public HttpResponseMessage Get()
{
return this.Request.CreateResponse(
HttpStatusCode.OK,
new { Message = "Hello", Value = 123 });
}
This answer may come bit late but as of today WebApi 2 is already out and now it is easier to do what you want, you would just have to do:
public object Message()
{
return new { Message = "hello" };
}
and along the pipeline, it will be serialized to xml or json according to client's preferences (the Accept header). Hope this helps anyone stumbling upon this question
In web API 2 you can use the new IHttpActionResult which is a replacement for HttpResponseMessage and then return a simple Json object: (Similiar to MVC)
public IHttpActionResult GetJson()
{
return Json(new { Message = "Hello"});
}
you can use JsonObject for this:
dynamic json = new JsonObject();
json.Message = "Hello";
json.Value = 123;
return new HttpResponseMessage<JsonObject>(json);
You could use an ExpandoObject. (add using System.Dynamic;)
[Route("api/message")]
[HttpGet]
public object Message()
{
dynamic expando = new ExpandoObject();
expando.message = "Hello";
expando.message2 = "World";
return expando;
}
You may also try:
var request = new HttpRequestMessage(HttpMethod.Post, "http://leojh.com");
var requestModel = new {User = "User", Password = "Password"};
request.Content = new ObjectContent(typeof(object), requestModel, new JsonMediaTypeFormatter());
In ASP.NET Web API 2.1 you can do it in a simpler way:
public dynamic Get(int id)
{
return new
{
Id = id,
Name = "X"
};
}
You can read more about this on https://www.strathweb.com/2014/02/dynamic-action-return-web-api-2-1/
public IEnumerable<object> GetList()
{
using (var context = new DBContext())
{
return context.SPersonal.Select(m =>
new
{
FirstName= m.FirstName ,
LastName = m.LastName
}).Take(5).ToList();
}
}
}
You should be able to get this to work if you use generics, as it will give you a "type" for your anonymous type. You can then bind the serializer to that.
public HttpResponseMessage<T> MakeResponse(T object, HttpStatusCode code)
{
return new HttpResponseMessage<T>(object, code);
}
If there are no DataContract or DataMebmer attributes on your class, it will fall back on serializing all public properties, which should do exactly what you're looking for.
(I won't have a chance to test this until later today, let me know if something doesn't work.)
You can encapsulate dynamic object in returning object like
public class GenericResponse : BaseResponse
{
public dynamic Data { get; set; }
}
and then in WebAPI; do something like:
[Route("api/MethodReturingDynamicData")]
[HttpPost]
public HttpResponseMessage MethodReturingDynamicData(RequestDTO request)
{
HttpResponseMessage response;
try
{
GenericResponse result = new GenericResponse();
dynamic data = new ExpandoObject();
data.Name = "Subodh";
result.Data = data;// OR assign any dynamic data here;//
response = Request.CreateResponse<dynamic>(HttpStatusCode.OK, result);
}
catch (Exception ex)
{
ApplicationLogger.LogCompleteException(ex, "GetAllListMetadataForApp", "Post");
HttpError myCustomError = new HttpError(ex.Message) { { "IsSuccess", false } };
return Request.CreateErrorResponse(HttpStatusCode.OK, myCustomError);
}
return response;
}