When I have a return type of 'string' in my WebAPI controller, the SuccessStatusCode returns 'OK' in my MVC Controller, but when the return type is of a model named 'USER', I get this Internal Server Error. Here's my code:
WebAPI:
public class UserController : ApiController
{
OnlineCenterEntities db = new OnlineCenterEntities();
public USER GetUserInfo(string userName, string domain)
{
USER userInfo = (from u in db.USERs
where u.USER_NAME.ToUpper() == userName.ToUpper() && u.LDAP_NAME.ToUpper() == domain.ToUpper()
select u).FirstOrDefault();
return userInfo;
}
}
MVC Controller that calls the WebAPI:
public class HomeController : Controller
{
HttpClient client;
string url = "http://localhost:61566/api/user/";
public HomeController()
{
client = new HttpClient();
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task<ActionResult> Index(string userName, string domain)
{
string GetUserInfoURL = String.Format("GetUserInfo?userName={0}&domain={1}", userName, domain);
HttpResponseMessage responseMessage = await client.GetAsync(url+GetUserInfoURL);
if (responseMessage.IsSuccessStatusCode)
{
var responseData = responseMessage.Content.ReadAsStringAsync().Result;
var userInfor = JsonConvert.DeserializeObject<USER>(responseData);
}
return View();
}
USER model:
public partial class USER
{
public int USER_ID { get; set; }
public string USER_NAME { get; set; }
public string FIRST_NAME { get; set; }
public string LAST_NAME { get; set; }
public string LDAP_NAME { get; set; }
public string EMAIL { get; set; }
}
In my WebAPI, if I change the return type from USER to string (and of course, change the return variable type to some string (userInfo.FIRST_NAME)), I get the SuccessStatusCode as 'OK', but as of this code, I get Internal Server Error with StatusCode: 500 (whatever that means). I have tried inserting breakpoint at every possible points, and I know that the api is returning the result fine. I simply don't understand why the following line
HttpResponseMessage responseMessage = await client.GetAsync(url+GetUserInfoURL);
gives InternalServerError error when I have the return type of USER, and return the whole USER model instead of just one string.
Please don't worry about the userName and domain parameters that I'm passing to the controllers, they are working fine!
Typically when this happens, it means it is failing to serialize the response. Once your controller returns a USER instance, somewhere WebAPI has to serialize that into the format requested by the client.
In this case the client requested "application/json". The default JsonMediaTypeFormatter uses JSON.Net to turn your C# object into json for the client. Apparently that serialization step is failing, but the response code doesn't tell you exactly why.
The easiest way to see exactly what is happening is to use a custom MessageHandler which forces the body to buffer earlier so you can see the actual exception. Take a look at this blog post for an example to force it to show you the real failure.
Related
I'm working on a simple notes api, I'm trying to create a Put method to update a note in my notes list, but when I try to update any note through the SwaggerUI I get a the 404 status code. I think that I'm missing something in the structure.
This is my [HttpPut] request:
[HttpPut("{id}")]
public IActionResult Put([FromBody] Note requestParam)
{
if (!ModelState.IsValid)
{
return BadRequest("Not a valid model");
}
using (_datacontext)
{
var ExistingNote = _datacontext.Note.Where(n => n.Id == requestParam.Id)
.FirstOrDefault<Note>();
if (ExistingNote != null)
{
ExistingNote.Title = requestParam.Title;
ExistingNote.Description = requestParam.Description;
ExistingNote.Completed = requestParam.Completed;
_datacontext.SaveChanges();
} else
{
return NotFound();
}
}
return Ok();
}
My DataContext:
public class DataContext : DbContext
{
public DataContext(DbContextOptions<DataContext> option) : base(option)
{
}
public DbSet<Note> Note { get; set; }
}
And lastly my Note Model:
public class Note
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public bool Completed { get; set; }
}
After looking for different examples I haven't found a standard approach so I'm not sure what to do about it
I've researched about Http bodies since it seemed like it needed to be part of the request but still get the error code. What could be wrong with it? (Both post and get methods work!).
Also, the error code:
When using the [HttpPut("{id}")] attribute on your controller, you need to add a parameter to the controller method's signature:
IActionResult Put([FromRoute] int Id, [FromBody] Note requestParam)
You can then call the API like this when Id=123
PUT http://{base-url}/123
Then you need to query the data context using the id from the route (which means you can remove it from the body)
On the other hand, if you don't want the Id as part of the request URL and keep it in the body, you need to remove the Id from the route template:
[HttpPut] without {id}.
Needless to say, make sure the Id actually exists in the data context. Otherwise your code will return, yes, a 404.
I am trying to retrieve the current logged on user details in a Web API controller to create a user profile page.
I have got the method created but the user keeps returning null in the code was shown below. This works fine in my MVC controller but in Web API controller it throws an error.
[HttpGet]
public IActionResult userProfile()
{
if (ModelState.IsValid)
{
var user = _userManager.Users.First(x => x.Email == User.Identity.Name);
return Ok(new UserViewModel
{
Id = user.Id,
UserName = user.Email,
FirstName = user.FirstName,
LastName = user.LastName
});
}
else
{
return BadRequest(ModelState);
}
}
UPDATE
[HttpGet]
[Authorize(Roles = "Client, Administrator")]
public IActionResult userProfile()
{
string baseUrl = "https://localhost:5001";
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(baseUrl);
var contentType = new MediaTypeWithQualityHeaderValue("application/json");
client.DefaultRequestHeaders.Accept.Add(contentType);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", HttpContext.Session.GetString("token"));
UserViewModel userModel = new UserViewModel();
HttpResponseMessage response = client.GetAsync("https://localhost:5001/api/UserAPI").Result;
string stringData = response.Content.ReadAsStringAsync().Result;
userModel = JsonConvert.DeserializeObject<UserViewModel>(stringData);
return View(userModel);
}
Error Message:
System.InvalidOperationException: 'Sequence contains no elements
An API is stateless meaning that there is no concept of logged in users or sessions. This is because each request is unique, separate and holds all the information required to provide a response.
An API has no way of knowing who is sending a request, there can be 10k people all sending requests at the same time, so who exactly is "logged in"?
So, if you want to load a user profile then send the userID as a parameter, something like:
[HttpGet]
public IActionResult userProfile(string userEmail)
{
if ( !string.IsNullOrEmpty (userEmail) ) ..... etc etc
{
var user = _userManager.Users.First(x => x.Email == userEmail);
return Ok(new UserViewModel
{
Id = user.Id,
UserName = user.Email,
FirstName = user.FirstName,
LastName = user.LastName
});
}
}
As a side note, if you don't have any input parameters or the input is a primitive type such as string or int, then ModelState.IsValid won't do anything. Only use ModelState.IsValid if you have a model class populated with the right rules.
in my case I could actually replace the string with a class
public class UserProfileRetrieveModel
{
[Required]
public string UserEmail { get;set; }
}
then I can do :
[HttpGet]
public IActionResult userProfile(UserProfileRetrieveModel model)
{
if (ModelState.IsValid)
{
var user = _userManager.Users.First(x => x.Email == model.UserEmail);
//etc
}
else
{
return BadRequest(ModelState);
}
}
--- after question updated
so it looks like you have a client application and from that you call the API.
everything I said above still applies, simply populate the data you have before calling the API.
Example:
public IActionResult userProfile()
{
//removed code we don't care about
string userEmail = "";// get your user email here in the MVC controller
//populate it in the api url.
//you might need to URL encode it since it will contain dots and #
string apiUrl = "https://localhost:5001/api/UserAPI/{userEmail}";
HttpResponseMessage response = client.GetAsync(apiUrl).Result;
}
you don't need to work out anything in the API, just pass everything you need to the API, from the MVC controller.
Now, all this aside, you have massive async issues. that part needs work although it's not related to the question.
Im presuming you're on WebAPI 2.
Try the following:
var user = _userManager.GetUser(HttpContext.User);
or
var user = _userManager.FindById(User.Identity.GetUserId());
Instead of .First() change it to .FirstOrDefault()
From the error message It seems you are trying to retrieve an element from an empty sequence. So check whether you have the the data or not.And also try to use .FirstOrDefault().
How would I create one controller with one API link in Web API 2 ASP.NET to respond on received data by an action that is in that data?
For example I receive this data:
{"t":"868efd5a8917350b63dfe1bd64","action":"getExternalServicePar","args":
{"id":"4247f835bb59b80"}}
and now I need to respond based on this "action" value. If there is some other action value like "incrementVallet" I need to respond with different data, and all that from one API link, etc.
The obvious question to ask is "Why would you want to do that?". Why not multiple methods or even multiple controllers? Having said that, you could do the following if you really want to:
public class ActionDetails
{
public string t { get; set; }
public string action { get; set; }
public ArgsContainer args { get; set; }
}
public ArgsContainer
{
public string id { get; set; }
}
Controller and method:
public class MyController : ApiController
{
// POST is not really the right choice for operations that only GET something
// but if you want to pass an object as parameter you really don't have much of a choice
[HttpPost]
public HttpResponseMessage DoSomeAction(ActionDetails details)
{
// prepare the result content
string jsonResult = "{}";
switch (details.action)
{
case "getExternalServicePar":
var action1Result = GetFromSomewhere(details.args.id); // do something
jsonResult = Newtonsoft.Json.JsonConvert.SerializeObject(action1Result);
break;
case "incrementVallet":
var action2Result = ...; // do something else
jsonResult = Newtonsoft.Json.JsonConvert.SerializeObject(action2Result);
break;
}
// put the serialized object into the response (and hope the client knows what to do with it)
var response = this.Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(jsonResult, Encoding.UTF8, "application/json");
return response;
}
}
My controller cannot accept string via POST method. What could be wrong? When I create HttpClient and send content like this :
var content = new FormUrlEncodedContent(new []
{
new KeyValuePair<string, string>("signature", "someexamplecontent"),
});
var response = await _client.PostAsync(path, content);
I'm getting an error: 415, Unsupported media type and it not stepping into controller. Instead, when I use PostAsJsonAsync - stepping into but parameter signature is null.
var response = await _client.PostAsJsonAsync(path, content);
That's a method in a controller:
[HttpPost("generatecert")]
public byte[] PostGenerateCertificate([FromBody] string signature)
{
}
The endpoint is most likely configured for JSON content. If using PostAsJsonAsync then just pass the string to be posted.
var signature = "someexamplecontent";
var response = await _client.PostAsJsonAsync(path, signature);
the method will serialize and set the necessary content type headers for the request.
if posting a more complex object, like
public class Model {
public string signature { get; set; }
public int id { get; set; }
}
The same applies, but the action would need to be updated to expect the complex object
[HttpPost("generatecert")]
public byte[] PostGenerateCertificate([FromBody] Model signature) {
//...
}
and the client would send the object
var model = new Model {
signature = "someexamplecontent",
id = 5
};
var response = await _client.PostAsJsonAsync(path, model);
Reference Parameter Binding in ASP.NET Web API
How can we support ajax post?
This the server code:
[RoutePrefix("api/Dashboard")]
public class PatientDashboardController : ApiController
{
[Route("UpdatePatientById")]
[HttpPost]
public IHttpActionResult UpdatePatientById(int? pk, string name, object value )
{
return Ok(name);
}
}
This is what I post to the server
Request URL:http://localhost/mydomain/api/Dashboard/UpdatePatientById
Request Method:POST
name:sex
value:1
pk:1093
I'm using x-editable plugin on the front end, it does the ajax post automatically. I don't think there is anything wrong with the post url.
This the error it gives me:
"No HTTP resource was found that matches the request URI 'http://example.com/mydomain/api/Dashboard/UpdatePatientById'."
MessageDetail: "No action was found on the controller 'Dashboard' that matches the request."
Web API can only receive one parameter from the body so you'll have to specify it as a type that aggregates those fields.
class PatientParameters
{
public int? Pk { get; set; }
public string Name { get; set; }
public object Value { get; set; }
}
and pass that:
public IHttpActionResult UpdatePatientById([FromBody] PatientParameters parameters) { }