I have the following Model
public class Customer
{
public string Name
{
get;set;
}
public string Address
{
get;
set;
}
}
The following WebAPI Method
[Route("PostCustomer")]
[HttpPost]
public Customer PostCustomer([FromBody]Customer c)
{
return c;
}
The following client code
Customer cust = new Customer() { Name = "abcd", Address = "test" };
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:11581");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.PostAsJsonAsync<Customer>("api/values/PostCustomer", cust);
The WebAPI Method never gets called and the response I get from the Web API says no content. When I try the same for a get it works fine.
Related
I'm building WebAPI & WebApp, both of them using ASP.NET Core 2.1
My Web App is trying to send post request to the Web API using ViewModel that contains IFormFile and other properties. I know I have to use MultipartFormDataContent to post IFormFile, but I don't know how to implement it with my ViewModel because my ViewModel has List of other model.
I already try to google some solutions, but I only found solutions with simple ViewModel without List like these :
https://stackoverflow.com/a/41511354/7906006
https://stackoverflow.com/a/55424886/7906006.
Is there any solution like
var multiContent = new MultipartFormDataContent();
var viewModelHttpContent= new StreamContent(viewModel);
MultiContent.Add(viewModelHttpContent, "viewModel");
var response = await client.PostAsJsonAsync("/some/url", multiContent);
so i don't have to add my property to MultipartFormDataContent one by one and post it as json.
Here's my Web App ViewModel
public class CreateDataViewModel
{
public string PrimaryKeyNumber{ get; set; }
public List<Currency> ListOfCurrency { get; set; }
public IList<DataDetail> dataDetails { get; set; }
[DataType(DataType.Upload)]
public IFormFile Attachment { get; set; }
//And other properties like Boolean, Datetime?, string
}
Here's my Web App controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(CreateDataViewModel viewModel)
{
//How to implement MultipartFormDataContent with my ViewModel in here ?
//My code below returns Could not create an instance of type Microsoft.AspNetCore.Http.IHeaderDictionary. Type is an interface or abstract class and cannot be instantiated. Path 'Attachment.Headers.Content-Disposition', line 1, position 723.
//It works fine if I don't upload a file
HttpResponseMessage res = await _client.PostAsJsonAsync<CreateDataViewModel>("api/data/create", viewModel);
var result = res.Content.ReadAsStringAsync().Result;
if (res.IsSuccessStatusCode)
{
TempData["FlashMessageSuccess"] = "Data have been submitted";
return RedirectToAction("Index", "Home"); ;
}
//Code for error checking
}
Here's my Web API controller that catches the post response using CreateDataViewModel as parameter.
[HttpPost]
[Route("[action]")]
public async Task<IActionResult> Create(CreateDataViewModel viewModel)
{
//Code to validate then save the data
}
don't know how to implement it with my ViewModel because my ViewModel has List of other model
You can refer to following code snippet and implement a custom model binder to achieve your requirement.
var multipartContent = new MultipartFormDataContent();
multipartContent.Add(new StringContent(viewModel.PrimaryKeyNumber), "PrimaryKeyNumber");
multipartContent.Add(new StringContent(JsonConvert.SerializeObject(viewModel.ListOfCurrency)), "ListOfCurrency");
multipartContent.Add(new StringContent(JsonConvert.SerializeObject(viewModel.dataDetails)), "dataDetails");
multipartContent.Add(new StreamContent(viewModel.Attachment.OpenReadStream()), "Attachment", viewModel.Attachment.FileName);
var response = await client.PostAsync("url_here", multipartContent);
Implement a custom model binder to convert incoming request data
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
// code logic here
// ...
// ...
// fetch the value of the argument by name
// and populate corresponding properties of your view model
var model = new CreateDataViewModel()
{
PrimaryKeyNumber = bindingContext.ValueProvider.GetValue("PrimaryKeyNumber").FirstOrDefault(),
ListOfCurrency = JsonConvert.DeserializeObject<List<Currency>>(bindingContext.ValueProvider.GetValue("ListOfCurrency").FirstOrDefault()),
dataDetails = JsonConvert.DeserializeObject<List<DataDetail>>(bindingContext.ValueProvider.GetValue("dataDetails").FirstOrDefault()),
Attachment = bindingContext.ActionContext.HttpContext.Request.Form.Files.FirstOrDefault()
};
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
Apply it on API action method
public async Task<IActionResult> Create([ModelBinder(BinderType = typeof(CustomModelBinder))]CreateDataViewModel viewModel)
Test Result
Below soltuion worked for me:-
var multiContent = new MultipartFormDataContent();
var viewModelHttpContent= new StreamContent(viewModel);
multiContent.Add(viewModelHttpContent, "viewModel");
multiContent.Add(new StreamContent(file.OpenReadStream()), "Attachment", file.FileName);
var request = new HttpRequestMessage(HttpMethod.Post, "/some/url") { Content = multiContent};
var response = await client.SendAsync(request);
At Api end:-
public async Task Upload([FromForm] CreateDataViewModel postRequest)
I am trying to implement Oauth 2.0 security integration with Asp.net web api 2 but only with external logins like google.I am pretty new for Oauth thing.
Can any one suggest any good tutorial/ example with only external login.
I do not want local user to register etc.
Google describes all the options pretty well on their website Using OAuth 2.0 to Access Google APIs
The most important steps are:
Register your application at Google Developer Console
Enable all your required scopes
Specify the allowed callback url to your website
Basically it works like this:
You redirect the user to your /api/Account/Login page
This Action method will redirect the user to the Google login page
User logs in and gets redirected to the callback url you specified (and approved in the Developer Console)
This callback url Action method will ask Google for a token based on the authorization code.
Following code snipped should get you started:
[RoutePrefix("api/Account")]
public class AccountController : ApiController
{
private readonly string _clientId = "YourCliendId";
[Route("Login")]
public HttpResponseMessage GetLogin()
{
string scope = HttpUtility.UrlEncode("Space Seperated list of scopes");
string redirectUri = HttpUtility.UrlEncode("http://YourWebsiteURL/api/Account/OAuthCallback");
string accessType = "Either online or offline";
string requestUri = string.Format("https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={0}&redirect_uri={1}&scope={2}&access_type={3}&approval_prompt=auto&include_granted_scopes=true", _clientId, redirectUri, scope, accessType);
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.MovedPermanently);
response.Headers.Location = new Uri(requestUri);
return response;
}
[Route("Logout")]
public HttpResponseMessage GetLogout()
{
//Optionally if you need to be able to logout...
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Moved);
response.Headers.Location = new Uri("https://accounts.google.com/logout");
return response;
}
[Route("OAuthCallback")]
public HttpResponseMessage GetOAuthCallback(string error)
{
//This would be a nice place to include some logging...
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Moved);
response.Headers.Location = new Uri("http://YourWebsiteURL");
return response;
}
[Route("OAuthCallback")]
public HttpResponseMessage GetOAuthCallback(string code, string scope)
{
string redirectUri = HttpUtility.UrlEncode("http://YourWebsiteURL/api/Account/OAuthCallback");
string postMessage = string.Format("code={0}&client_id={1}&client_secret={2}&redirect_uri={3}&grant_type=authorization_code", code, _clientId, "YourGoogleSecretCode", redirectUri);
string jsonMessage;
using (WebClient client = new WebClient())
{
//Convert the authorization code to a token
client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded; charset=utf-8";
jsonMessage = client.UploadString("https://accounts.google.com/o/oauth2/token", "POST", postMessage);
}
Token token = JsonConvert.DeserializeObject<Token>(jsonMessage);
//Do something with the token. E.g. put it in a cookie / header ... and pass it to the client.
}
}
public class Token
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
}
public class TokenInfo
{
[JsonProperty("issued_to")]
public string IssuedTo { get; set; }
[JsonProperty("audience")]
public string Audience { get; set; }
[JsonProperty("user_id")]
public string UserId { get; set; }
[JsonProperty("scope")]
public string Scope { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
[JsonProperty("verified_email")]
public bool VerifiedEmail { get; set; }
[JsonProperty("access_type")]
public string AccessType { get; set; }
}
BTW. If you need to later on validate the token you can use the following code:
string requestUri = string.Format("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={0}", token.AccessToken);
string tokenInfoMessage;
using (WebClient client = new WebClient())
{
tokenInfoMessage = client.DownloadString(requestUri);
}
TokenInfo tokenInfo = JsonConvert.DeserializeObject<TokenInfo>(tokenInfoMessage);
//Don't forget to validate the Audience and Scope, because the token might be a valid token (but not meant for your website).
if (tokenInfo.Audience == _clientId && tokenInfo.Scope.Contains("All the scopes that you provided") && tokenInfo.UserId == "TheGoogleUserIdYouWantToCheck")
{
//Looks valid, so continue with whatever you want to do...
}
I need to use EntityQuery in BreezeSharp to get Access Token from my Breeze WebAPI
I have a class called TokenResponseModel for deserializing my json from the server as follows:
using Newtonsoft.Json;
namespace CMIS.Integration.Common
{
class TokenResponseModel
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
[JsonProperty("userName")]
public string Username { get; set; }
[JsonProperty(".issued")]
public string IssuedAt { get; set; }
[JsonProperty(".expires")]
public string ExpiresAt { get; set; }
}
}
I have the following code to run:
EntityQuery query=EntityQuery.From("Token",new TokenResponseModel()).
WithParameters(new Dictionary<string,object>{{"grant_type","password"},{"username","my_username"},{"password","my_password"}});
EntityManager mng = new EntityManager(baseUrl);
var tokenobject = await query.Execute(mng);
When I run it, I Get an error. It requires metadata which is not there for the "/Token" method on the server.
How Can I call it with BreezeSharp.
With RestSharp I can do it as follows:
RestRequest request = new RestRequest("/Token", Method.POST);
request.AddParameter("grant_type", "password");
request.AddParameter("username", "my_username");
request.AddParameter("password", "my_password");
RestClient client = new RestClient(baseUrl);
var response = client.Execute<AccessToken>(request);
And this works fine.
Thanks
More Explanation:
What I want to say is that sometimes I just need to get the result from the breeze server just i a JSON format. I don't want it mapped to any objects on the client. A good example is my case for authenticating a user using the Token method. I know how to parse the JSON myself. I just want breeze to bring the result from the call below:
string baseUrl = "http://myserver_url/NHIFService/";
EntityQuery query = EntityQuery.From<string>("Token").WithParameters(new new Dictionary<string, object> { { "grant_type", "password" }, { "username", "my_username" }, { "password", "my_password" } });
EntityManager mng = new EntityManager(baseUrl);
var tokenobject = await query.Execute(mng);
I want to be able to do this because sometimes I return anonymous objects from the server that do not have a match on the client or server.
Can breese sharp allow me to do this without caring about the metadata. Or how can I supress metadata fetching.
Thank you.
After going through the BreezeSharp Source Code I came with the solution for doing what I wanted. The guys at IdeaBlade have created this DataService class that enables returning RAW JSON from the server without even caring about the Metadata. Here is how I did it:
string token = await AuthenticationHelper.GetAccessToken();
string baseUrl = "http://my_server_url/appname/breeze/my_controller/";
DataService ds = new DataService(baseUrl);
string resourcePath = string.Format("GetCardDetails?CardNo={0}", cardNoTextEdit.EditValue);
ds.HttpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
string result=await ds.GetAsync(resourcePath);
Congratulation guys Breeze Sharp is awesome.
I'm a little confused with the session documentation, so let's say i'm already send the authentication data from the client side and retrieve the ss-id and ss-pid like this:
var client = new JsonServiceClient("http://somewhere/theAPI/");
var response = client.Post(new Auth() {UserName = "myuser", Password = "password123"});
var myCookie= client.CookieContainer.GetCookies(new Uri("http://somewhere/theAPI"));
how I can retrieve the AuthSession information just like the surname, email, etc from the servicestack? do i need it store somewhere else like in memcache server, and retrieve from that?
or I need to build my authentication in the client side? and just use the API to retrieve the data?
Assuming you've already created a custom AuthUserSession, for example:
/// <summary>
/// Create your own strong-typed Custom AuthUserSession where you can add additional AuthUserSession
/// fields required for your application. The base class is automatically populated with
/// User Data as and when they authenticate with your application.
/// </summary>
public class CustomUserSession : AuthUserSession {
public string CustomId { get; set; }
}
And you've registered your custom AuthUserSession when configuring the AuthFeature plugin, like so:
public override void Configure(Container container)
{
//Register all Authentication methods you want to enable for this web app.
Plugins.Add(new AuthFeature(
() => new CustomUserSession(), //Use your own typed Custom UserSession type
new IAuthProvider[] {
new CredentialsAuthProvider(), //HTML Form post of UserName/Password credentials
// and any other auth providers you need
}));
}
Then you can expose this data to the client in a service you create. SocialBotstrapApi provides access to the current session information on the server like this: Use it as a model to create a UserAuth service that returns the information for just the current user.
public abstract class AppServiceBase : Service {
private CustomUserSession userSession;
protected CustomUserSession UserSession {
get {
return base.SessionAs<CustomUserSession>();
}
}
}
[Route("/userauths")]
public class UserAuths
{
public int[] Ids { get; set; }
}
public class UserAuthsResponse
{
public UserAuthsResponse()
{
this.Users = new List<User>();
this.UserAuths = new List<UserAuth>();
this.OAuthProviders = new List<UserOAuthProvider>();
}
public CustomUserSession UserSession { get; set; }
public List<User> Users { get; set; }
public List<UserAuth> UserAuths { get; set; }
public List<UserOAuthProvider> OAuthProviders { get; set; }
}
//Implementation. Can be called via any endpoint or format, see: http://servicestack.net/ServiceStack.Hello/
public class UserAuthsService : AppServiceBase
{
public object Any(UserAuths request)
{
var response = new UserAuthsResponse {
UserSession = base.UserSession,
Users = Db.Select<User>(),
UserAuths = Db.Select<UserAuth>(),
OAuthProviders = Db.Select<UserOAuthProvider>(),
};
response.UserAuths.ForEach(x => x.PasswordHash = "[Redacted]");
response.OAuthProviders.ForEach(x =>
x.AccessToken = x.AccessTokenSecret = x.RequestTokenSecret = "[Redacted]");
if (response.UserSession != null)
response.UserSession.ProviderOAuthAccess.ForEach(x =>
x.AccessToken = x.AccessTokenSecret = x.RequestTokenSecret = "[Redacted]");
return response;
}
}
I am calling an MVC3 controller action (JsonResult) from a console application. To make the call I am using a System.Net.Http.HttpClient and the PostAsJsonAsync method.
In the PostAsJsonAsync method I am passing a small poco instance.
It all works great apart for one thing.
My problem is that I cannot pick up the poco instance in the call to the the MVC3 controller action. Can anyone tell me how to do this?
Here's the code:
Calling code in console application:
var client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:50285/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var user = new HashedUser { UserName = "userName", PasswordHash = "abcdef".GetHashCode() };
var response = client.PostAsJsonAsync("app/test", user).Result;
var txt = response.Content.ReadAsAsync<string>().Result;
Receiving Code in MVC3 project
public class AppController : Controller
{
public JsonResult Test(HashedUser json)
{
// Problem: json is always a new instance
// of HashedUser and not the one passed in
// from the call to this controller action.
return Json("qqq ppp 777 888" + json.UserName);
}
}
public class HashedUser
{
public string UserName { get; set; }
public int PasswordHash { get; set; }
}
You should serialize and send your POCO as JSon object. Use Newtonsoft.Json to do that. Here's some samples:
http://james.newtonking.com/pages/json-net.aspx
http://www.west-wind.com/weblog/posts/2012/Aug/30/Using-JSONNET-for-dynamic-JSON-parsing