For educational purposes, I am building a clone social bookmarking service (similar to reddit). Under each 'bookmark' in the list, I'd like to show the base domain of the source URL (as opposed to the full URL.
I've already found a few threads on SO on how to do this, so I've gone ahead and made a class to abstract the functionality, but I'm unsure of where I should actually be calling the method.
At the moment I have my BookmarkList controller method passing a list of Bookmark objects to the view, where I'm iterating over the list. Since the Bookmark object doesn't have a property for storing the base URL (as I'm computing it on the fly) I can't put it inside the Bookmark objects before passing them to the view (and it seems wrong anyway). So should I be calling the GetDomainFromUrl method I've made from the view itself? For some reason that doesn't feel appropriate either.
I am unsure of how to fit in this functionality without breaking MVC convention.
Thanks.
I would add it to the Bookmark class. Properties can be computed, here is an example from a tutorial on asp.net mvc from msdn (MSDN source):
public string LastName { get; set; }
public string FirstMidName { get; set; }
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
Instead of simply concatinating strings, you would call your GetDomainFromUrl method here.
IE,
public string BaseUrl
{
get
{
return GetDomainFromUrl(this.Url);
}
}
Notice that there is not a set method defined, since you could/would be setting the Url property.
Also, is your Url stored as a string?
If you are using the URI class, you could just use this.FullUrl.Host, assuming your article's url is defined in a property called FullUrl.(also assuming that this would not defeat the point of this assignment, since you said this was for school.)
For very basic scenarios, or where you have full control over your models, MVC (Model, View, Controller) is a good pattern.
In my experience, you typically need additional information that is important to your views but not to your actual model. For example, a list of dropdown items to be displayed for a model property, or in your case, putting the base URL for a site for your users to see.
In this case, I like to adapt MVC to be VM-V-C (ViewModel, View, Controller).
Essentially, you would want to create a Bookmark ViewModel and use that when rendering your views:
BookmarkViewModel.cs:
public class BookmarkViewModel
{
public string BaseUrl {get;set;}
// + all existing bookmark properties
}
You can either add your base URL function right into your view model and have the view model perform the function itself, or you can do it in your controller when creating the view model.
There are a few different options on how you could do this. I recommend storing the GetDomainFromUrl() method in the Bookmark Class. Also I recommend creating a property for the BaseUrl as well.
You can then either pass the full Url to the Bookmark object in the constructor, perform your function, and set it to the BaseUrl property.
class Bookmark
{
public string BaseUrl { get; }
public Bookmark(string url)
{
BaseUrl = GetDomainFromUrl(url);
}
private string GetDomainFromUrl(string url)
{
//your logic to generate BaseUrl
}
}
Another alternative is to do something like the following:
class Bookmark
{
private string baseUrl;
public string BaseUrl
{
get
{
return baseUrl;
}
set
{
baseUrl = GetDomainFromUrl(value));
}
}
private string GetDomainFromUrl(string url)
{
//your logic to generate BaseUrl
}
}
and then set the BaseUrl property somplace in your code to the value of the full url, and when you do so it will perform your function and store it in your property.
Related
I am receiving an error message, "Sequence contains no elements" while trying to update a table in SQL from Angular 7 to an AspNet Core controller by passing two model parameters using an "http.post".
I am passing the data from the form to the class models with no problem because I can see the payload data in the browser console. However, when trying to pass the models as parameters in my api service to the controller, all of the parameters in the model are null. I usually don't have an issue when passing one model parm thru, but passing two of them to get to my controller with a [FromBody] doesn't seem to want to work for me.
I tried to wrap the models in curly brackets to pass them, to no avail:
UpdateService(serviceAddress: ServiceAddressModel, contact: ContactModel) {
let reqHeader = new HttpHeaders();
let body = { svc: serviceAddress, cnt: contact };
reqHeader.append('Content-Type', 'application/json');
return this.http.post(this.baseurl + 'api/customermanagement/update-service-address-info', body, { headers: reqHeader });
When I view the request / response in the browser console, I can see the data within the payload, so I know that the data is ready to pass.
My controller is set up as follows:
[Route("update-service-address-info")]
public bool UpdateServiceAddressAccount([FromBody] ServiceAddressEntity svc_id, [FromBody] ContactEntity cnt_id)
{
return serviceAddressService.UpdateServiceAddressAccount(svc_id, cnt_id);
}
Using breakpoints in this call shows null for all values.
If I can properly pass the parameters to my interface, I should be good-to-go. I am sensing that I am not structuring the parameters properly in the http.post body.
Your request body, { svc: serviceAddress, cnt: contact } is received as a json string, e.g. {"svc":{"serviceAddressProperty1":"value",...},"cnt":{"contactProperty1":"value",...}}. The parameters to your action method are bound via the default model binding mechanism (unless you provide your own custom model binding implementation). The default mechanism attempts to create instances by binding from the top level of the json object received with the request. enter code here
In simpler terms, lets assume you class ServiceAddressModel is defined like this:
public class ServiceAddressModel
{
public string Name { get; set; }
public string Property2 { get; set; }
}
the model binder looks for properties with the names "name" and "property2" at the top level of the json tree. If found, these are bound to the Name and Property2 properties of the created instance.
In your case, wrapping your models in a class that can make svc_id and cnt_id the top level properties would work fine. Like this example:
public class MyRequest
{
public ServiceAddressModel svc_id { get; set; }
public ContactEntity cnt_id { get; set; }
}
Then you can declare your action like
[Route("update-service-address-info")]
public bool UpdateServiceAddressAccount([FromBody] MyRequest request)
{
return serviceAddressService.UpdateServiceAddressAccount(request.svc_id, request.cnt_id);
}
Snake casing, camel casing should be allowed by default (you will have to try it, I havent tested that part). That is, if you declare your properties as SvcId and CntId (if you prefer more natural C# naming conventions) it should be able to bind correctly from JSONs with "svc_id" or "cnt_id".
Another option would be to implement custom model binders, but that might be a longer and more complex route.
Hope this helps.
Just try to pass the value like this and see
let body = { svc_id: serviceAddress, cnt_id: contact };
I have a method in Web API that I used the object as input, but when I try to run the API using URI the fields inside the object are Null.
this is my method:
[HttpGet]
[Route("AddUser/{user}")]
public async Task<string> CreateUser([FromUri]AddUser user)
{
//LoansApiTrace.Trace.Verbose(EventId.Start, () => string.Format("{0}: {1}", "AddUser", user.));
string Exception = await Repository.AddUserAsync(user);
return Exception;
}
This is AddUser object:
public class AddUser
{
public string UserEmailAddress { get; set; }
public string PasswordHash { get; set; }
public string Salt { get; set; }
public string RemoteRefNumber { get; set; }
}
and this is the URI:
http://localhost:59509/Adduser/user=test#yahoo.com,pass,salt,remref/
it goes to the method but UserEmailAddress , PasswordHash ,..all 4 are empty.
This is a really a bad practice to pass secret data through URI like you're doing. Then I will not attempt to give a solution for that to work.
The best practice is to pass that kind of data through your request body and use Http POST method :
[HttpPost]
[Route("AddUser/{userId}")]
public async Task<string> CreateUser(string userId, [FromBody]AddUser user)
{
// Find a user by userId
// Then update the user data.
}
you use an URI like this => http://localhost:59509/Adduser/12345 where 12345 is the user id.
you need to make sure that the selected HTTP method is POST
you need to write the data of AddUser into the request body
It also recommanded to use HTTPS when user need to send that type of data.
Consider Using POST If Applicable
While it may not be do-able, you may want to consider adding these fields within a <form> and simply posting them to the server in the body as opposed to using the actual URL itself. Passwords, salts and hashes generally aren't something that you want to get passed around like that.
If You Must Use A GET
Have you tried passing the values in as proper query-string parameters instead?
http://localhost:59509/Adduser/user?UserEmailAddress=test#yahoo.com&PasswordHash=abc&Salt=123&RemoteRefNumber=foo
This should set the following properties based on your current routes:
user = "user"
UserEmailAddress = "test#yahoo.com"
PasswordHash = "abc"
Salt = "123"
RemoteRefNumber = "foo"
MVC has to have some idea of how to bind these properties to those on your class, so unless the names match as expected, it will not know how to map something like "user" to "UserEmailAddress". As mentioned earlier, this isn't ideal and can present all sorts of security issues (so only use something like this on prototype / non-production environments).
I'm trying to setup Facebook Notification API.
I have an APi Controller with RealtimeUpdate() - Get, will be used just for verification of endpoint.
As is written in Fb Docs:
Firstly, Facebook servers will make a single HTTP GET to your callback
URL when you try to add or modify a subscription. A query string will
be appended to your callback URL with the following parameters:
hub.mode - The string "subscribe" is passed in this parameter
hub.challenge - A random string
hub.verify_token - The verify_token value you specified when you created the subscription
From here I have a problem - I have no idea how to handle this dots in query params names. I google a lot, and did not find the solution.
Can somebody please say to me how to get data from this hub.* values?
Thank you!
Update your method signature using the FromUri attributes, like this:
public string Get(
[FromUri(Name="hub.mode")]string mode,
[FromUri(Name="hub.challenge")]string challenge,
[FromUri(Name="hub.verify_token")]string verifyToken
)
{
/* method body */
}
The parameters will be bound from the query string using the specified names.
Slightly different form Steve's answer.
In case you need to have a normal controller instead of an Api one (if you are inheriting from Controller rather tha ApiController), the follow worked for me:
namespace Name
{
public class Hub
{
public string Mode { get; set; }
public string Challenge { get; set; }
// ReSharper disable once InconsistentNaming
public string Verify_Token { get; set; }
}
public class FacebookWebHooksController : Controller
{
[System.Web.Http.HttpGet, ActionName("Callback")]
[AllowAnonymous]
public ContentResult CallbackGet(Hub hub)
{
if (hub.Mode == "subscribe" && hub.Verify_Token == "YOUR_TOKEN")
return Content(hub.Challenge, "text/plain", Encoding.UTF8);
return Content(string.Empty, "text/plain", Encoding.UTF8);
}
}
[HttpPost]
[AllowAnonymous]
public ActionResult Callback()
{
Request.InputStream.Seek(0, SeekOrigin.Begin);
var jsonData = new StreamReader(Request.InputStream).ReadToEnd();
}
}
The Model Binder has some illegal characters, of which I believe '.' is a special character, used primarily to bind complex objects. When all else fails, you can look at Request.QueryString and Request.Form directly, just like in ASP.NET WebForms.
You can also try using a complex object that has a Property named hub with subproperties mode, challenge, and verify_token. This might just do the trick.
I'm currently evaluation whether AutoMapper can be of benefit to our project. I'm working on a RESTful Web API using ASP.NET Web API, and one of the things I must return is a resource that contains links. Consider this simplified example, using the following domain object:
public class Customer
{
public string Name { get; set; }
}
I need to map this into a resource object, sort of like a DTO but with added properties to facilitate REST. This is what my resource object may look like:
public class CustomerResource
{
public string Name { get; set; }
public Dictionary<string, string> Links { get; set; }
}
The Links property will need to contain links to related resources. Right now, I could construct them using the following approach:
public IEnumerable<CustomerResource> Get()
{
Func<Customer, CustomerResource> map = customer =>
new CustomerResource
{
Name = customer.Name,
Links = new Dictionary<string, string>()
{
{"self", Url.Link("DefaultApi", new { controller = "Customers", name = customer.Name })}
}
}
var customers = Repository.GetAll();
return customers.Select(map);
}
...but this is pretty tedious and I have a lot of nested resources and such. The problem that I see is that I can't use AutoMapper because it doesn't let me provide certain things needed during projection that are scoped to the point where the mapping operation is performed. In this case, the Url property of the ApiController provides the UrlHelper instance that I need to create the links for me, but there may be other cases.
How would you solve this conundrum?
P.S. I typed up this code specifically for this question, and it compiled in your head but may fail in your favorite IDE.
This is not a pretty solution, but after reading through the docs it appears that there isn't one... We're currently throwing in contextual stuff by mapping Tuple<TDomainType, TContextStuff> to TDataTransfer. So in your case you'd Mapper.CreateMap<Tuple<Customer, Controller>, CustomerResource>.
Not pretty, but it works.
I would look in to using a Custom Type Converter. The type converter could have contextual information injected via an IOC container. Or, since the converter is instantiated at configuration time, it could have a reference to a factory which would return contextual information each time the type converter is run.
Simple Example
You could define an interface for getting your current "context" (what that means depends on what you're doing and how you implement things so for this example I'll just the current HttpContext which gets you access to Session, Server, Items, etc...):
public interface IContextFactory
{
HttpContext GetContext();
}
And the implementation is simply:
public class WebContextFactory : IContextFactory
{
public HttpContext GetContext()
{
return HttpContext.Current;
}
}
Your custom type converter could take an instance of IContextFactory from your IOC container and each time the mapping is run, you can call GetContext() to get the context for the current request.
Accessing the Url Property
The UrlHelper comes from the Request object attached to the current controller's context. Unfortunately, that is not available in the HttpContext. However, you could override the Initialize method on your ApiController and store the controllerContext in the HttpContext.Items collection:
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
HttpContext.Current.Items["controllerContext"] = controllerContext;
base.Initialize(controllerContext);
}
You can then access that from the current HttpContext:
var helper = ((HttpControllerContext) HttpContext.Current.Items["controllerContext"]).Request.GetUrlHelper();
I'm not sure it's the best solution, but it can get you the UrlHelper instance inside your custom type mapper.
I have a .net mvc 4 webapi project that I'm trying to pass an array of an object to a method on my controller.
I've found some examples here on SO that talk about needing to set my object's properties with: param1=whatever¶m2=bling¶m3=blah.
But I don't see how I can pass in a collection using that.
Here is my method signature. Notice I've decorated the argument with the [FromUri] attribute.
public List<PhoneResult> GetPhoneNumbersByNumbers([FromUri] PhoneRequest[] id)
{
List<PhoneResult> prs = new List<PhoneResult>();
foreach (PhoneRequest pr in id)
{
prs.Add(PhoneNumberBL.GetSinglePhoneResult(pr.PhoneNumber, pr.RfiDate, pr.FinDate, pr.State));
}
return prs;
}
here is my simple PhoneRequest object:
public class PhoneRequest
{
public string PhoneNumber { get; set; }
public string RfiDate { get; set; }
public string FinDate { get; set; }
public string State { get; set; }
}
and here's a sample of what I'm using to pass in:
http://localhost:3610/api/phonenumber/getphonenumbersbynumbers/
[{"PhoneNumber":"8016667777","RfiDate":"","FinDate":"2012-02-11","State":"UT"},
{"PhoneNumber":"8018889999","RfiDate":"2012-12-01","FinDate":"","State":"UT"}]
using this comes back with "bad request"
I also tried this
http://localhost:3610/api/phonenumber/getphonenumbersbynumbers?
id=[{"PhoneNumber":"8016667777","RfiDate":"","FinDate":"2012-02-11","State":"UT"},
{"PhoneNumber":"8018889999","RfiDate":"2012-12-01","FinDate":"","State":"UT"}]
which does reach the method, but the array is null.
how can I pass in an array of my PhoneRequest object to my Web API method?
Try passing the PhoneRequest[] from the uri in this format:
http://localhost:3610/api/phonenumber/getphonenumbersbynumbers?
id[0][PhoneNumber]=8016667777&id[0][FinDate]=2012-02-11&id[0][State]=UT&
id[1][PhoneNumber]=8018889999&id[1][RfiDate]=2012-12-01&id[1][State]=UT
I suggest you use POST for this.
As you query string grows, you will run into problems with the maximum length of the URL, which is browser dependent.
If you have a lot of parameters to pass, a POST is perfectly acceptable even if you are really only GETting data. What you will lose, however, is the ability for the user to bookmark a particular page with the query string.
I created a custom model binder, the FieldValueModelBinder class, which can effectively pass any object containing nested array or generic list types of data with query strings having field-name pairs without imbedding any JSON and XML structures. The model binder can resolve all issues discussed above. Since this question was extended by the question ID 19302078, you can see details of my answer in that thread.