I'm still a beginner for .NET, C#, RESTful API. Now, I'm learning the POST method in RESTful API. Here is the sample coding of POST method, but I still can't get the meaning of the coding. Can anyone provide me an explanation for each line of the coding in a clearly understand way? Or if you don't mind, can you explain it to me by using comment style? For example:
public string message; //to declare message for what use...`
public class MessageController : ApiController
{
// GET api/values
public class MessageToShow
{
public string message;
public string from;
}
public List<MessageToShow> Get()
{
var x = new cheeAdDCClf3rfFREntities();
var y=x.messages.Take(100);
List<MessageToShow> messageToShow = new List<MessageToShow>();
foreach (var xx in y)
{
MessageToShow m = new MessageToShow();
member me = x.members.FirstOrDefault(j => j.ID == xx.from);
if (me != null)
{
m.from = me.username;
m.message = xx.message1;
messageToShow.Add(m);
}
}
return messageToShow;
}
// POST api/values
public void Post(int memberid, dynamic value)
{
var x = new cheeAdDCClf3rfFREntities();
message m = new message();
m.ID = x.messages.Max(record => record.ID) + 1;
m.from = memberid;
m.message1 = value.value;
x.messages.Add(m);
x.SaveChanges();
}
}
}
I would be very appreciate if anyone would like to share me your knowledge on programming. Thank you so much!!! ^_^
It looks like you posted code for both a GET and POST method.
When I make a GET to your API:
public List<MessageToShow> Get()
{
// Looks like EntityFramework? Represents the items already in database
var x = new cheeAdDCClf3rfFREntities();
// Take to top 100 item already in the database
var y=x.messages.Take(100);
// Create a new list to hold the messages we will return
List<MessageToShow> messageToShow = new List<MessageToShow>();
// For each message in the 100 we just took
foreach (var xx in y)
{
MessageToShow m = new MessageToShow();
// Get the details of the member that send this message
member me = x.members.FirstOrDefault(j => j.ID == xx.from);
// If we found the member, create a message to show
// (populating the message and the username of the member
// who sent it)
if (me != null)
{
m.from = me.username;
m.message = xx.message1;
messageToShow.Add(m);
}
}
// Return the list of messages we just created to the caller of the API
return messageToShow;
}
When I POST to your API this is what happens:
public void Post(int memberid, dynamic value)
{
// Gets the items already in the database
var x = new cheeAdDCClf3rfFREntities();
// Create a new message object
message m = new message();
// Find the highest ID already in the database, then add 1. This is the
// ID for our new item
m.ID = x.messages.Max(record => record.ID) + 1;
// The 'from' property is set to the memberId that the user passed in the POST
m.from = memberid;
// The 'message' property is set to whatever dynamic value is passed in the POST
m.message1 = value.value;
// Add the message to the database
x.messages.Add(m);
x.SaveChanges();
}
To understand more about REST, you can read this:
A beginners Guide To HTTP and REST
The code you posted actually looks more like EntityFramework, which is a way of interacting with a database. It's not specific to APIs.
You can find our more about EF here:
EntityFramework
Related
i tried this method that I created but it prompts me an error:
Realms.RealmInvalidObjectException:This object is detached. Was it deleted from the realm?'
public void deleteFromDatabase(List<CashDenomination> denom_list)
{
using (var transaction = Realm.GetInstance(config).BeginWrite())
{
Realm.GetInstance(config).Remove(denom_list[0]);
transaction.Commit();
}
}
what is the proper coding for deleting records from database in realm in C# type of coding?
You are doing it the right way. The error message you are getting indicates that the object was removed already. Are you sure it still exists in the realm?
UPDATE:
I decided to update this answer because my comment on the other answer was a bit hard to read.
Your original code should work fine. However, if you want deleteFromDatabase to accept lists with CashDenomination instances that either have been removed already or perhaps were never added to the realm, you would need to add a check. Furthermore, note that you should hold on to your Realm instance and use it in the transaction you created. In most cases, you want to keep it around even longer, though there is little overhead to obtaining it via GetInstance.
public void deleteFromDatabase(List<CashDenomination> denom_list)
{
if (!denom_list[0].IsValid) // If this object is not in the realm, do nothing.
return;
var realm = Realm.GetInstance(config);
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom_list[0]);
transaction.Commit();
}
}
Now, if you want to use identifiers, you could look it up like you do, but still just use Remove:
public void deleteFromDatabase(int denom_id)
{
var realm = Realm.GetInstance(config);
var denom = realm.All<CashDenomination>().FirstOrDefault(c => c.denom_id == denom_id);
if (denom == null) // If no entry with this id exists, do nothing.
return;
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom);
transaction.Commit();
}
}
Finally, if your CashDenomination has denom_id marked as PrimaryKey, you could look it up like this:
public void deleteFromDatabase(int denom_id)
{
var realm = Realm.GetInstance(config);
var denom = realm.ObjectForPrimaryKey<CashDenomination>(denom_id);
if (denom == null) // If no entry with this id exists, do nothing.
return;
using (var transaction = realm.BeginWrite())
{
realm.Remove(denom);
transaction.Commit();
}
}
public void deleteFromDatabase(Realm realm, long cashDenominatorId)
{
realm.Write(() =>
{
var cashDenominator = realm.All<Person>().Where(c => c.Id == cashDenominatorId);
Realm.RemoveRange<CashDenomination>(((RealmResults<CashDenomination>)cashDenominator));
});
}
Which you would call as
Realm realm = Realm.GetInstance(config);
var denom_list = ...
// ...
deleteFromDatabase(realm, denom_list[0].id);
I already made it having this code :) thanks to #EpicPandaForce 's answer.
public void deleteFromDatabase(int denom_ID, int form_ID)
{
//Realm realm;
//and
//RealmConfiguration config = new RealmConfiguration(dbPath, true);
//was initialized at the top of my class
realm = Realm.GetInstance(config);
realm.Write(() =>
{
var cashflow_denom = realm.All<CashDenomination>().Where(c => c.denom_id == denom_ID);
var cashflow_form = realm.All<CashForm>().Where(c => c.form_id == form_ID);
realm.RemoveRange(((RealmResults<CashDenomination>)cashflow_denom));
realm.RemoveRange(((RealmResults<CashForm>)cashflow_form));
});
}
it is now deleting my data without exception :)
The old code I've inherited for Twilio retrieves messages using the absolute PageNumber property of the MessageListRequest but according to the documentation this is obsolete and I should be using GetNextPage and GetPrevPage.
The API metadata shows this as obsolete with the message "Use GetNextPage and GetPreviousPage for paging. Page parameter is scheduled for end of life https://www.twilio.com/engineering/2015/04/16/replacing-absolute-paging-with-relative-paging".
Are there any examples of this usage? I couldn't find any in the documentation except in one of the API test methods and I'm not sure how well I can get to processing multiple pages with this example as a guide.
public class Foo : TwilioBase
{
public string Bar { get; set; }
}
public class FooResult : TwilioListBase
{
public List<Foo> Foos { get; set; }
}
[Test]
public void ShouldGetNextPage()
{
IRestRequest savedRequest = null;
FooResult firstPage = new FooResult();
firstPage.NextPageUri = new Uri("/Foos?PageToken=abc123", UriKind.Relative);
mockClient.Setup(trc => trc.Execute<FooResult>(It.IsAny<IRestRequest>()))
.Callback<IRestRequest>((request) => savedRequest = request)
.Returns(new FooResult());
var client = mockClient.Object;
var response = client.GetNextPage<FooResult>(firstPage);
mockClient.Verify(trc => trc.Execute<FooResult>(It.IsAny<IRestRequest>()), Times.Once);
Assert.IsNotNull(savedRequest);
Assert.AreEqual("/Foos?PageToken=abc123", savedRequest.Resource);
Assert.AreEqual(Method.GET, savedRequest.Method);
Assert.IsNotNull(response);
}
The old usage might look something like so:
var twilio = new TwilioRestClient(config.AccountSid, config.AuthToken);
var result = new List<Message>();
MessageResult tempResult;
int page = 0;
do
{
var request = new MessageListRequest();
request = new MessageListRequest { Count = 1000, DateSent = newestDate, DateSentComparison = ComparisonType.GreaterThanOrEqualTo, PageNumber = page++, To = config.FromNumber };
tempResult = twilio.ListMessages(request);
result.AddRange(tempResult.Messages);
} while (tempResult.NextPageUri != null);
Finally, I built the Twilio API 3.4.1.0 from the twilio-csharp GitHub project instead of NuGet since I need to update it to use the MessagingServiceSid which isn't included in the API yet.
Thanks for any pointers. I'll post a solution if I can figure it out on my own.
Actually, I got it to work now!
MessageResult messages = twilio.ListMessages(request);
do
{
if (messages.Messages != null)
{
foreach (var message in messages.Messages)
{
... process results
}
if (messages.NextPageUri != null)
{
messages = twilio.GetNextPage<MessageResult>(messages);
}
}
} while (messages.NextPageUri != null);
Did you try the example from the API Explorer?
https://www.twilio.com/console/dev-tools/api-explorer/sms/sms-mms-list
var twilio = new TwilioRestClient(AccountSid, AuthToken);
// Build the parameters
var options = new MessageListRequest();
var messages = twilio.ListMessages(options);
foreach (var message in messages.Messages)
{
Console.WriteLine(message.Body);
}
The helper library will automatically fetch from the API as you loop over the list until all records matching your criteria are processed.
You can limit the results with MessageListRequest.
Please give that a try and let me know how it goes.
I want to test if the Update or Insert function is called with a unit test. What would the unit test look like for this?
public void LogicForUpdatingAndInsertingCountriesFromMPtoClientApp()
{
var allCountriesAlreadyInsertedIntoClientDatabase = _countryBLL.GetAllCountries();
var countiresFromMP = GetAllCountriesWithTranslations();
List<Country> countiresFromMPmapped = new List<Country>();
foreach (var country in countiresFromMP)
{
Country newCountry = new Country();
newCountry.CountryCode = country.Code;
newCountry.Name = country.TranslatedText;
countiresFromMPmapped.Add(newCountry);
}
foreach (var country in countiresFromMPmapped)
{
//check if the country is already inserted into the Client Database,
//if it is update, else insert it
Country testedCountry = allCountriesAlreadyInsertedIntoClientDatabase
.Where(x => x.CountryCode == country.CountryCode)
.FirstOrDefault();
//here fallback function for tested country
if (testedCountry != null)
{
var countryToUpdate = _countryBLL.GetCountryByCode(testedCountry.CountryCode);
//return _countryBLL.UpdateCountry(countryToUpdate);
_countryBLL.UpdateCountry(countryToUpdate);
}
else
{
country.CountryId = Guid.NewGuid();
// return _countryBLL.InsertCountryFromMP(country);
_countryBLL.InsertCountryFromMP(country);
}
}
return null;
}
The method is wrapped in an interface which I can mock.
Are you trying to test for a specific call, or are you happy with just testing either call was received?
For the latter, you can use the ReceivedCalls() extension method to get a list of all the calls a substitute has received:
var allCalls = _countryBLL.ReceivedCalls();
// Assert “allCalls” contains “UpdateCountry” and “InsertCountry”
NSubstitute wasn’t really designed to support this, so it is quite messy.
To test a specific call was made, we can use Received():
_countryBLL.Received().UpdateCountry(Arg.Any<Country>());
// or require a specific country:
_countryBLL.Received().UpdateCountry(Arg.Is<Country>(x => x.CountryCode == expectedCountry));
This requires that the required dependencies have been substituted in for the test, which often results in tests like this:
[Test]
public void TestCountryIsUpdatedWhen….() {
var countryBLL = Substitute.For<ICountryBLL>();
// setup specific countries to return:
countryBLL.GetAllCountries().Returns( someFixedListOfCountries );
var subject = new MyClassBeingTested(countryBLL);
subject.LogicForUpdatingAndInsertingCountries…();
countryBLL.Received().UpdateCountry(…);
}
With ASP.NET WebApi, when I send GET api/question?page=0&name=qwerty&other=params and API should give result within pagination envelop.
For that, I'd like to put result and change given page querystring to other values.
I tried as below code but I got a bad feeling about this.
protected HttpResponseMessage CreateResponse(HttpStatusCode httpStatusCode, IEnumerable<Question> entityToEmbed)
// get QueryString and modify page property
var dic = new HttpRouteValueDictionary(Request.GetQueryNameValuePairs());
if (dic.ContainsKey("page"))
dic["page"] = (page + 1).ToString();
else
dic.Add("page", (page + 1).ToString());
var urlHelper = new UrlHelper(Request);
var nextLink= page > 0 ? urlHelper.Link("DefaultApi", dic) : null;
// put it in the envelope
var pageEnvelope = new PageEnvelope<Question>
{
NextPageLink = nextLink,
Results = entityToEmbed
};
HttpResponseMessage response = Request.CreateResponse<PageEnvelope<Question>>(httpStatusCode, pageEnvelope, this.Configuration.Formatters.JsonFormatter);
return response;
}
The NextPageLink gives a lot complex result.:
http://localhost/api/Question?Length=1&LongLength=1&Rank=1&SyncRoot=System.Collections.Generic.KeyValuePair%602%5BSystem.String%2CSystem.String%5D%5B%5D&IsReadOnly=False&IsFixedSize=True&IsSynchronized=False&page=1
My question is,
My page handling with Dictionary approach seems dirty and wrong. Is there better way to address my problem?
I don't know why urlHelper.Link(routeName, dic) gives such a verbose ToString result. How to get rid of unusable Dictionary-related properties?
The key issue probably in your code is the conversion to the HttpRouteValueDictionary. New it up instead and add in a loop all key value pairs.
The approach can be simplified quite a lot, and you should also probably want to consider using an HttpActionResult (so that you can more easily test your actions.
You should also avoid using the httproutevaluedictionary and instead write your UrlHelper like
urlHelper.Link("DefaultApi", new { page = pageNo }) // assuming you just want the page no, or go with the copying approach otherwise.
Where just pre calculate your page no (and avoid ToString);
Write it all in an IHttpActionResult that exposes an int property with the page No. so you can easily test the action result independently of how you figure out the pagination.
So something like:
public class QuestionsResult : IHttpActionResult
{
public QuestionResult(IEnumerable<Question> questions>, int? nextPage)
{
/// set the properties here
}
public IEnumerable<Question> Questions { get; private set; }
public int? NextPage { get; private set; }
/// ... execution goes here
}
To just get the page no, do something like:
Source - http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-21
string page = request.Uri.ParseQueryString()["page"];
or
you can use this extension method below (from Rick Strahl)
public static string GetQueryString(this HttpRequestMessage request, string key)
{
// IEnumerable<KeyValuePair<string,string>> - right!
var queryStrings = request.GetQueryNameValuePairs();
if (queryStrings == null)
return null;
var match = queryStrings.FirstOrDefault(kv => string.Compare(kv.Key, key, true) == 0);
if (string.IsNullOrEmpty(match.Value))
return null;
return match.Value;
}
I want to change some code in an action of the OpcSaveBilling action from the CheckoutController. I don't want to alter the core code of NopCommerce so i need to try to overide the code with my own custom code.
I've read this article to get me started http://www.pronopcommerce.com/overriding-intercepting-nopcommerce-controllers-and-actions. From what I've read you can execute your own code before an action is executed and after an action is executed. But what I am not getting is the part that the article is leaving open (the actual code that needs to be executed).
What I basicly want is the same function of the original code but with some custom tweaks. I've added a checkbox in the OnePageCheckout view and based on that checkbox it needs to skip the enter shipping addresss part in the checkout or not. (Use the billing address for the shipping address)
I already have that code added to in the core code and this work and skips the step (NOTE: I know I still need to manually add the billing address as shipping address) but like i said i don't want to alter the code in the core of NopCommerce but override of it.
If my question is not understandable and you need more code or explanation I'm happy to provide more. If the way I am doing this is not suitable for what I want, i would appreciate if you tell me!
My code:
The Action Filter class:
using Nop.Web.Controllers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;
namespace Nop.Plugin.Misc.MyProject.ActionFilters
{
class ShippingAddressOverideActionFilter : ActionFilterAttribute, IFilterProvider
{
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
if (controllerContext.Controller is CheckoutController && actionDescriptor.ActionName.Equals("OpcSaveBilling", StringComparison.InvariantCultureIgnoreCase))
{
return new List<Filter>() { new Filter(this, FilterScope.Action, 0) };
}
return new List<Filter>();
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// What do I put in here? So that I have the code of the core action but with my custom tweaks in it
}
}
}
Registered the class in DependencyRegistar in the same Nop plugin
builder.RegisterType<ShippingAddressOverideActionFilter>().As<System.Web.Mvc.IFilterProvider>();
A working example with custom code in it. But this is in the core action.
public ActionResult OpcSaveBilling(FormCollection form)
{
try
{
//validation
var cart = _workContext.CurrentCustomer.ShoppingCartItems
.Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart)
.Where(sci => sci.StoreId == _storeContext.CurrentStore.Id)
.ToList();
if (cart.Count == 0)
throw new Exception("Your cart is empty");
if (!UseOnePageCheckout())
throw new Exception("One page checkout is disabled");
if ((_workContext.CurrentCustomer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed))
throw new Exception("Anonymous checkout is not allowed");
int billingAddressId = 0;
int.TryParse(form["billing_address_id"], out billingAddressId);
if (billingAddressId > 0)
{
//existing address
var address = _workContext.CurrentCustomer.Addresses.FirstOrDefault(a => a.Id == billingAddressId);
if (address == null)
throw new Exception("Address can't be loaded");
_workContext.CurrentCustomer.BillingAddress = address;
_customerService.UpdateCustomer(_workContext.CurrentCustomer);
}
else
{
//new address
var model = new CheckoutBillingAddressModel();
TryUpdateModel(model.NewAddress, "BillingNewAddress");
//validate model
TryValidateModel(model.NewAddress);
if (!ModelState.IsValid)
{
//model is not valid. redisplay the form with errors
var billingAddressModel = PrepareBillingAddressModel(selectedCountryId: model.NewAddress.CountryId);
billingAddressModel.NewAddressPreselected = true;
return Json(new
{
update_section = new UpdateSectionJsonModel()
{
name = "billing",
html = this.RenderPartialViewToString("OpcBillingAddress", billingAddressModel)
},
wrong_billing_address = true,
});
}
//try to find an address with the same values (don't duplicate records)
var address = _workContext.CurrentCustomer.Addresses.ToList().FindAddress(
model.NewAddress.FirstName, model.NewAddress.LastName, model.NewAddress.PhoneNumber,
model.NewAddress.Email, model.NewAddress.FaxNumber, model.NewAddress.Company,
model.NewAddress.Address1, model.NewAddress.Address2, model.NewAddress.City,
model.NewAddress.StateProvinceId, model.NewAddress.ZipPostalCode, model.NewAddress.CountryId);
if (address == null)
{
//address is not found. let's create a new one
address = model.NewAddress.ToEntity();
address.CreatedOnUtc = DateTime.UtcNow;
//some validation
if (address.CountryId == 0)
address.CountryId = null;
if (address.StateProvinceId == 0)
address.StateProvinceId = null;
if (address.CountryId.HasValue && address.CountryId.Value > 0)
{
address.Country = _countryService.GetCountryById(address.CountryId.Value);
}
_workContext.CurrentCustomer.Addresses.Add(address);
}
_workContext.CurrentCustomer.BillingAddress = address;
_customerService.UpdateCustomer(_workContext.CurrentCustomer);
}
// Get value of checkbox from the one page checkout view
var useSameAddress = false;
Boolean.TryParse(form["billing-address-same"], out useSameAddress);
// If it is checked copy the billing address to shipping address and skip the shipping address part of the checkout
if (useSameAddress)
{
var shippingMethodModel = PrepareShippingMethodModel(cart);
return Json(new
{
update_section = new UpdateSectionJsonModel()
{
name = "shipping-method",
html = this.RenderPartialViewToString("OpcShippingMethods", shippingMethodModel)
},
goto_section = "shipping_method"
});
}
// If it isn't checked go to the enter shipping address part of the checkout
else
{
if (cart.RequiresShipping())
{
//shipping is required
var shippingAddressModel = PrepareShippingAddressModel(prePopulateNewAddressWithCustomerFields: true);
return Json(new
{
update_section = new UpdateSectionJsonModel()
{
name = "shipping",
html = this.RenderPartialViewToString("OpcShippingAddress", shippingAddressModel)
},
goto_section = "shipping"
});
}
else
{
//shipping is not required
_genericAttributeService.SaveAttribute<ShippingOption>(_workContext.CurrentCustomer, SystemCustomerAttributeNames.SelectedShippingOption, null, _storeContext.CurrentStore.Id);
//load next step
return OpcLoadStepAfterShippingMethod(cart);
}
}
}
catch (Exception exc)
{
_logger.Warning(exc.Message, exc, _workContext.CurrentCustomer);
return Json(new { error = 1, message = exc.Message });
}
}
Nobody can tell you what you need to put in OnActionExecuting, because there is so much you can do in it.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// What do I put in here? So that I have the code of the core action but with my custom tweaks in it
}
Rule of thumb? Write any code like how you'll write an Action. The only tweak is, instead of returning ActionResult, you should set filterContext.Result (you can't return anything as this is a void method).
For example, setting the following will redirect to home page before even executing the action you are overriding.
filterContext.Result = new RedirectToRouteResult("HomePage", null);
Remember this is OnActionExecuting, so this is executed before the Action you are overriding. And if you redirects it to another page, it'll not call the Action you are overriding. :)