Getting deserialized body without binding - model-binding

I'm starting with Nancy, and I've run into a frustrating issue.
I have a model that has an ID (amongst other properties).
public class MyModel
{
public string Id { get; set; }
// other properties
}
In my module, I defined a PUT method
Put["/{id}", true] = async (parameters, token) =>
{
var model = this.Bind<MyModel>();
string id = parameters["id"];
if (model.Id != id)
return new Response
{
ReasonPhrase = $"[error message about IDs not matching]",
StatusCode = HttpStatusCode.BadRequest
};
await _myModelService.Update(model);
return Nancy.Response.NoBody;
};
The issue I'm experiencing is that at the this.Bind<MyModel>() call, Nancy overwrites the ID in the body with the ID in the route, meaning I can't test my "unmatched" scenario. For example:
PUT /orders/someObjectId
{
"Id" : "aDifferentObjectId"
}
binds to a MyModel with Id as "someObjectId".
I've also tried blacklisting the property:
this.Bind<MyModel>(m => m.Id)
this.Bind<MyModel>("id")
this.Bind<MyModel>("Id")
this.Bind<MyModel>("id", "Id")
However, this results in the bind operation clearing the ID property in the model.
How can I get exactly what's in the body?

Nevermind. I was doing something wrong, and now it's working without the blacklist. I have no explanation.

Related

return object after null checking through if in blazor

public async Task<ServiceResponse<Product>> GetProductAsync(int productId)
{
var response = new ServiceResponse<Product>()
{
Data = await _context.Products.AsNoTracking().FirstOrDefaultAsync(p => p.Id == productId)
} ;
if ( response.Data == null )
{
response.Success = false ;
response.Message = "Sorry, but this product does not exist." ;
}
else
return response ;
return response ;
}
I am a newbie c# and blazor.
I made productservice.cs file. The above code is the part of it which will return one product class in Gerelic T.
First, I find matched productid in db.
Then, null check. if it is not null it will return response, ok.
I thought at first I will return response as one product to Task T, all code might be good.
But I faced an error in the last line. So I put again 'return response'. Then the error sign was disappeared.
But I think this might be wrong and weird, even though there is no error anymore.
I want to improve this code correctly or efficiently.
May I get your opinion?
The error you faced probably was something like not all code paths return a value. Since your method returns a typed value, and you are using if-else statement -- you have to provide returning value both in if and else clauses.
Extend your ServiceResponse<T> with a constructor that receives some arguments.
Refactor your method in order to stress your product variable for better readability.
Return ServiceResponse using newly-created constructor.
Unfortunately, I can't test it right now to be sure 100%, but you can give it a try.
class ServiceResponse<T>
{
public T Data { get; set; }
public bool IsSuccess { get; set; }
public string Message { get; set; }
public ServiceResponse( T data, string message = "success" )
{
Data = data;
IsSuccess = data != null;
Message = data == null ? "Sorry, but this product does not exist." : message;
}
}
public async Task<ServiceResponse<Product>> GetProductAsync( int productId )
{
var product = await _context.Products.Where( p => p.Id == productId).AsNoTracking().FirstOrDefault();
return new ServiceResponse<Product>( product );
}
Remarks
We don’t have to provide 2nd argument into the constructor while returning the response since it already has the default value defined for the message in the constructor. But you still can do that!
I also set default value for the message there if data == null. Actually, it can be removed if you wish!
IsSuccess is also being regulated by data in the constructor of ServiceResponse<T>
UPD: Tested
if ProductId == 1
{
"data": {
"id": 1,
"name": "Test1"
},
"isSuccess": true,
"message": "success"
}
if ProductId == 2 (which is out of list)
{
"data": null,
"isSuccess": false,
"message": "Sorry, but this product does not exist."
}

Testing a method in MVC which makes calls to Repositories

I have a MVC method like so:
public ActionResult ChangeStatus(string productId, string statusToChange)
{
var productToChangeStatus = _updateProductRepository.GetUpdateProduct(productId);
if (statusToChange.ToLower() == ChangeStatusTo.Disable)
{
productToChangeStatus.Active = "false";
}
else
{
productToChangeStatus.Active = "true";
}
_updateProductsManager.UpsertProduct(productToChangeStatus);
return Json(new { success = true });
}
This method gets an existing product based on the 'productId', changes the 'Active' property on it based on the 'statusToChange' value, saves it back and returns a Json with success.
The test setup is like so:
private ProductController _controller;
private Mock<IUpdateProductRepository> _iProductRepository;
[TestInitialize]
public void TestSetup()
{
_iProductRepository = new Mock<IUpdateProductRepository>();
_controller = new ProductController(_iProductRepository.Object);
}
Wrote a test method like so:
[TestMethod]
public void Disable_A_Product_Which_Is_Currently_Enabled()
{
const string productId = "123";
var productBeforeStatusChange = new Product()
{
Active = "true",
Id = new Guid().ToString(),
Name = "TestProduct",
ProductId = "123"
};
var productAfterStatusChange = new Product()
{
Active = "false",
Id = new Guid().ToString(),
Name = "TestProduct",
ProductId = "123"
};
_iProductRepository.Setup(r => r.GetUpdateProduct(productId)).Returns(productBeforeStatusChange);
_iProductRepository.Setup(r => r.UpsertProduct(productBeforeStatusChange)).Returns(productAfterStatusChange);
var res = _controller.ChangeStatus("123", "disable") as JsonResult;
Assert.AreEqual("{ success = true }", res.Data.ToString());
}
The test fails with this error:
Object reference not set to an instant of the object.
On debugging I found that it fails inside the
if(...)
condition where the actual setting of the Active property is happening.
Since the productId that's being passed is not real a product object can't be retrieved for the code to work on.
I tried to use Mock but I think my usage is not correct.
So what I want to know is, how to test a method like this where a method that returns an ActionResult is in turn calling the repository to work with object(s).
Thanks in advance.
You seems to be missing setup for
_updateProductsManager.UpsertProduct()
The way you setup GetUpdateProduct() method you ought to setup UpsertProduct() on the mock instance.

Designing data in $.ajax to match server side model definition

I'm running the following AJAX call.
var submission = {};
submission.input = [];
submission.input.push({ Id: "{ab684cb0-a5a4-4158-ac07-adff49c0c30f}" });
submission.input.push({ Id: "{bb684cb0-a5a4-4158-ac07-adff49c0c30f}" });
$.ajax({
url: "http://" + "localhost:49642/Controller/Action",
data: submission
});
It works as supposed to and in my controller I can see two elements. However, the Id fields is all-zeros. I'm certain I failed to match the definition of the object on the server-side but I'm to annoyed and frustrated right now to generate more suggestions how to shove the data to the service.
The data model is like this.
public class Thingy
{
public Guid Id { get; set; }
public IEnumerable<Guid> Blobb { get; set; }
}
I've tried to use different bracket types, apostrophes and such enclosing the guids on client-side. To no avail. What can I have forgotten?!
Edit
I need to clarify the structural definition of my information object. The controller is set up to receive the following.
public ActionResult SelectionStorage(IEnumerable<Stuff> stuff)
{
Session["Stuff"] = stuff;
return null;
}
The definition of the Stuff class is more complex but the following will suffice as a POC.
public class Stuff
{
public Guid Id { get; set; }
public IEnumerable<Guid> Ids { get; set; }
public Dictionary<String, decimal> Amounts { get; set; }
}
So, on the client, I'm performing the following set up of the submitted data object.
var submission = {};
var subIds = [];
subIds.push("{ab684cb0-a5a4-4158-ac07-adff49c0c30f}");
subIds.push("{bb684cb0-a5a4-4158-ac07-adff49c0c30f}");
submission.input = [];
submission.input.push({
Id: "{cb684cb0-a5a4-4158-ac07-adff49c0c30f}",
Ids: subIds,
Amounts: null
});
Note that the Amounts will differ from null but that headache I haven't even got to, yet.
Edit 2
New try - a simpler approach. In JS I send the following.
var stuff = {};
stuff.input = [];
stuff.input.push("{ab684cb0-a5a4-4158-ac07-adff49c0c30f}");
stuff.input.push("{bb684cb0-a5a4-4158-ac07-adff49c0c30f}");
$.ajax({
url: ...,
data: stuff,
type: "POST",
success: ...,
error: ...
});
On recieving end in C# I have this.
public ActionResult MyAction(List<String> input) { ... }
This gives null. I can't see why.
You should be able to simplify the jquery. With what you have here you don't need the submission. If you are sending a complex list back to the controller you need to name your variables but since you are just sending a string back you don't need to do that. Try changing your data to
var input = [];
input.push("{ab684cb0-a5a4-4158-ac07-adff49c0c30f}");
input.push("{bb684cb0-a5a4-4158-ac07-adff49c0c30f}");
then in the ajax call
data: input,
or
data: Json.stringify(input);
then on your controller
public ActionResult Action(List<String> input){...
Edit:
try changing your jquery to this:
var stuff= {};
stuff.Id = "{cb684cb0-a5a4-4158-ac07-adff49c0c30f}";
stuff.Ids= [];
stuff.Ids.push("{ab684cb0-a5a4-4158-ac07-adff49c0c30f}");
stuff.Ids.push("{bb684cb0-a5a4-4158-ac07-adff49c0c30f}");
then in your ajax have data: stuff, or data: Json.stringify(stuff),

How to access parameter value in controller method from RedirectToAction

After saving some data to the db, I pull the newly created userId from the db and I need to send it to the next View. I am using ReditectToAction to direct it and send the value. I have verified the value is making it to the redirect but I can't figure out how to grab it, because the model that comes in to AccountSetup is null. Where am i goig wrong?
method sending the id:
var id = user.UserId;
var accountModel = new AccountViewModel
{
UserId = id
};
return RedirectToAction("AccountSetup", "Home", accountModel.UserId);
}
I have verified in debug, that accountModel.userId ha a value.
Method supposed to receive the id:
public ActionResult AccountSetup(AccountViewModel model)
{
int id = model.UserId;
model is null.
return RedirectToAction("AccountSetup", "Home", accountModel.UserId);
You are not returning the model the action expects but just an id value. Model binder can't bind this value to AccountViewModel.UserId, it probably binds it to the id parameter in the default route. You are actually expecting a model of type AccountViewModel so you should send a AccountViewModel instance.
return RedirectToAction("AccountSetup", "Home",
new AccountViewModel {UserId = accountModel.UserId});
You need to use TempData to store anything during the redirect.
TempData["MyModel"] = accountModel;
return RedirectToAction("AccountSetup", "Home");
And then in the other action:
var accountModel = (AccountViewModel)TempData["MyModel"];
int id = accountModel.UserId;

AJAX Get request returns success or error depending on the object received from controller

Here is my problem step by step,
1- I do ajax get request from my external javascript file as following,
$.ajax({
type: "GET",
dataType: 'json',
url: '/Company/GetCompanies',
error: function (xhr, status, error) {
alert('error');
return false;
},
success: function (response) {
alert('success');
return false;
}
});
2 - This is the action method that receives the request
public ActionResult GetCompanies()
{
var model = new CompanyIndex();
model.FillCompanies();
return Json(model ,JsonRequestBehavior.AllowGet);
}
3 - CompanyIndex is a ViewModel that is created in my action method above.
public class CompanyIndex
{
public IList<Company> Companies { get; set; }
public void FillCompanies()
{
/* Part that is not working */
UnitOfWork unitOfWork = new UnitOfWork();
Companies = unitOfWork.CompanyRepository
.Get(orderBy: q => q.OrderBy(u => u.CompanyName)).ToList();
unitOfWork.Dispose();
/****************************/
/* This works fine */
var companyList = unitOfWork.CompanyRepository
.Get(orderBy: q => q.OrderBy(u => u.CompanyName)).ToList();
Companies = companyList.Select(x => new { name = x.CompanyName }).ToList()
.Select(x => new Company
{
CompanyName = x.name
}).ToArray<Company>();
/********************/
}
}
As you can see I fetch company entities from the database using my repository and Get method that I also share below. The problem is when I fetch them from database, the type of the model that is retrieved is System.Data.Entity.DynamicProxies.Company_someRandomStringHere. In this case Ajax get request does not succeed and alerts error message.
However, if I first fetch from db and create Company objects in ViewModel and assign them to my List, ajax works fine, and ajax get request returns success. And type of Company entities when I create objects at ViewModel is CompanyManagement.Models.Company as it should be.
Just in case you need it this is the Get method I am using to fetch data from db.
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
query = query.Where(filter);
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
query = query.Include(includeProperty);
if (orderBy != null)
return orderBy(query).ToList();
else
return query.ToList();
}
I don't want to manually create new Company objects. Any ideas are really appreciated. Thanks in advance.
Found the problem. It seems the respond of Ajax request needs to be serializable. To do that, I canceled dynamic proxies with,
context.ContextOptions.ProxyCreationEnabled = false;
As a result my Get function now returns CompanyManagement.Models.Company entities instead of System.Data.Entity.DynamicProxies.Company.
Edit:
Use viewmodels to populate your views, do not return entities keeping your business logic to client side. This is bad practice and dangerous in some cases.
I had a similar problem once and i have to do it manually like this
/* Part that is not working */
UnitOfWork unitOfWork = new UnitOfWork();
Companies = unitOfWork.CompanyRepository
.Get(orderBy: q => q.OrderBy(u => u.CompanyName))
.Select(x => new Company()
{
fooAttribute = x.fooAtribute
}).ToList();

Resources