How do I get Odata working with a List variable?
The following API is not working and giving error.
HttpGet
Error: {"type":"https://tools.ietf.org/html/rfc7231#section-6.5.13","title":"Unsupported Media Type","status":415,"traceId":"00-b0b48a41f445ac4bbcebba902f8cca95-a714db4f4fd0c146-00"}
All my other APIs work,
"http://localhost:4547/api/properties/GetIdentifierPaged"
Controller:
[HttpGet[("[Action]")]
public ActionResult GetIdentifierPaged(List<string> propertyListRequest, ODataQueryOptions<PropertyDto> queryOptions)
{
propertyListRequest.Add("1110200100"); // fake data
var model = _propertyService.GetByPropertyIdentifierPaged(propertyListRequest).ToODataPageResult(queryOptions);
return Ok(model);
}
Service:
public IQueryable<PropertyDto> GetByPropertyIdentifierPaged(List<string> identifiers)
{
var identifiersEnumerable = identifiers.AsEnumerable();
var properties = _dataContext.Property
.Include(x => x.PropertyStatus)
Where(x => identifiersEnumerable.Contains(x.PropertyIdentifier))
return properties;
}
Page Result:
public static PageResult<T> ToODataPageResult<T>(this IQueryable<T> query, ODataQueryOptions<T> queryOptions)
{
var settings = new ODataQuerySettings();
settings.EnsureStableOrdering = false;
var countQuery = (IQueryable<T>)queryOptions.ApplyTo(query, settings, AllowedQueryOptions.Skip | AllowedQueryOptions.Top | AllowedQueryOptions.Select | AllowedQueryOptions.OrderBy);
long count = countQuery.Count();
var results = (IQueryable<T>)queryOptions.ApplyTo(query, settings, AllowedQueryOptions.None);
return new PageResult<T>(results, null, count);
}
This problem indicates that there is a problem with your request, but even if you came to respond, there are errors you will receive. You cannot give a value that session is open in response
return new PageResult<T>(**results.ToList()**, null, count);
Related
I am creating an API for my Xamarin Android application and I have created this method to update password in the database:
[HttpPut]
[ActionName("updatepassword")]
public HttpResponseMessage updatepassword(string password,string email)
{
user_table user = dbe.user_table
.Where(x => x.email_address == email)
.FirstOrDefault();
user.password_hash = password.GetHashCode();
var response = dbe.SaveChanges();
string finalresponse = "password updated successfully" + response;
return Request.CreateResponse(HttpStatusCode.OK, finalresponse );
}
But when I test it using postman it shows that 0 entities have been affected.
I also tried using
dbe.user_table.AddorUpdate()
but it doesn't work either, please help me.
This is my postman query
http://192.168.10.9:8044/api/account/updatepassword/?password=asad1234&email=asadregards#gmail.com
I have published my API on IIS Server that is why I am using IP Address instead of localhost.
Change your code to this:
var user = dbe.user_table
.Where(x => x.email_address == email)
.FirstOrDefault();
var response=0;
if (user!=null)
{
user.password_hash = password.GetHashCode();
dbe.Entry(user).State = EntityState.Modified;
// Or you can try
//dbe.Entry(user).Property(i => i.password_hash).IsModified = true;
response = dbe.SaveChanges();
}
........
........
And maybe it's a good idea to change you API header too:
[Route("updatepassword/{password}/{email}")]
public IHttpActionResult updatepassword(string password,string email)
{
.........
.......
return response>0 ? Ok():BadRequest();
}
I was trying to call the Put method through Postman and always getting error: "405 Method Not Allow" and "Message": "The requested resource does not support http method 'PUT'."
I'm using DocumentDB and C#. Here is my code:
[Route("multilanguage/Resources/{id}/{Language}")]
[HttpPut]
public async Task<IHttpActionResult> UpdateResource(string Id, string Language, string text)
{
client = new DocumentClient(new Uri(EndPoint), AuthKey);
var collectionLink = UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId);
var query = new SqlQuerySpec("SELECT * FROM MultiLanguage as m where m.id = #pmId",
new SqlParameterCollection(new SqlParameter[] { new SqlParameter { Name = "#pmId", Value = Id } }));
Document doc = client.CreateDocumentQuery<Document>(
collectionLink, query).AsEnumerable().FirstOrDefault();
List<Models.Translations> d = doc.GetPropertyValue<List<Models.Translations>>("Translations");
Models.Translations temp = d.Find(p => p.Language == Language);
temp.Content = text;
temp.LastModified = DateTimeOffset.Now;
temp.ModifiedBy = "admin";
doc.SetPropertyValue("Translations", d);
Document updated = await client.ReplaceDocumentAsync(doc);
return Ok();
}
When I call the Put method throught Postman, I call "http://localhost:XXXX/multilanguage/resources/2/En". "2" and "En" are the first two parameters in my code. And I also specify the "text" parameter value in the Postman request Body with x-www-form-urlencoded type: key = text, value = Test! This put method suppose to update the temp.Content value to "Test!". However, it always failed with the error I mentioned above.
Did I miss anything here?
The 405 error when performing a PUT request to web api is a well known topic. You can find many solutions in this or this SO question.
And for the design of you controller:
PUT are designed to have a body, just like POST and in your case
you should send all parameters in the body instead.
You should create a class which contains the objects you want to send to the server:
public class resourceClass
{
public string Id { get; set; }
public string Language { get; set; }
public string text { get; set; }
}
Then specify the route without the attribute routing and get the object from the request body
[Route("multilanguage/Resources/PutResource")]
[HttpPut]
public async Task<IHttpActionResult> UpdateResource([FromBody] resourceClass obj)
{
client = new DocumentClient(new Uri(EndPoint), AuthKey);
var collectionLink = UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId);
var query = new SqlQuerySpec("SELECT * FROM MultiLanguage as m where m.id = #pmId",
new SqlParameterCollection(new SqlParameter[] { new SqlParameter { Name = "#pmId", Value = Id } }));
Document doc = client.CreateDocumentQuery<Document>(
collectionLink, query).AsEnumerable().FirstOrDefault();
List<Models.Translations> d = doc.GetPropertyValue<List<Models.Translations>>("Translations");
Models.Translations temp = d.Find(p => p.Language == Language);
temp.Content = text;
temp.LastModified = DateTimeOffset.Now;
temp.ModifiedBy = "admin";
doc.SetPropertyValue("Translations", d);
Document updated = await client.ReplaceDocumentAsync(doc);
return Ok();
}
From the client you could add an object to the PUT request of Content-Type application/json like this
var data = {
Id: clientId,
Language: clientLanguage,
text: clientText
};
Don't forget to stringify the json when adding it to the http request
data: JSON.stringify(data),
The PUT controller will then be reached at "http://localhost:XXXX/multilanguage/resources/putresource".
Check the URL for which you are posting the data, in my case the URL was incorrect because of which I got these errors, also verify that in Body you should select raw and change the Text to JSON if you are passing a JSON as a data.
We have scenario where external API returns either User XML or Error XML based on whether request succeed or failed.
At the moment I'm passing User POCO to the restsharp and works fine. But if it fails, this object is NULL. And we won't know why it failed unless we parse the Error XML manually.
Is there a way to workaround this?
e.g.
var restClient = new RestClient(baseURL);
var request = new RestRequest(uri);
request.Method = Method.POST;
var response = restClient.Execute<User>(request);
On execution of above method the API can return Error xml object. How do I get Error object on fail and User on success?
This is possible, although the code is a little ugly. RestSharp allows you to specify your own XML deserializer, so we'll need to do that in order to make this work.
First, though, you need a data type that can store either an Error or a User (I made it generic so it works for more than just Users):
public class Result<T>
{
public T Data { get; set; }
public Error Error { get; set; }
}
So the idea is, now when you execute the request, you ask RestSharp for a Result<User> instead of just a User, i.e.:
var result = client.Execute<Result<User>>(request);
Now here's the magic required to deserialize as either an Error or a User. It's a custom deserializer that inherits from RestSharp's XmlDeserializer. Warning: this code is not tested at all, but it can hopefully point you in the right direction.
public class XmlResultDeserializer : XmlDeserializer
{
public override T Deserialize<T>(IRestResponse response)
{
if (!typeof(T).IsGenericType || typeof(T).GetGenericTypeDefinition() != typeof(Result<>))
return base.Deserialize<T>(response);
// Determine whether the response contains an error or normal data.
var doc = XDocument.Parse(response.Content);
var result = Activator.CreateInstance<T>();
if (doc.Root != null && doc.Root.Name == "Error")
{
// It's an error
var error = base.Deserialize<Error>(response);
var errorProperty = result.GetType().GetProperty("Error");
errorProperty.SetValue(result, error);
}
else
{
// It's just normal data
var innerType = typeof(T).GetGenericArguments()[0];
var deserializeMethod = typeof(XmlDeserializer)
.GetMethod("Deserialize", new[] { typeof(IRestResponse) })
.MakeGenericMethod(innerType);
var data = deserializeMethod.Invoke(this, new object[] { response });
var dataProperty = result.GetType().GetProperty("Data");
dataProperty.SetValue(result, data);
}
return result;
}
}
Then you would wire it all up like this:
var restClient = new RestClient(baseURL);
client.AddHandler("application/xml", new XmlResultDeserializer());
var request = new RestRequest(uri);
request.Method = Method.POST;
var result = restClient.Execute<Result<User>>(request);
if (response.Data.Data != null)
{
var user = response.Data.Data;
// Do something with the user...
}
else if (response.Data.Error != null)
{
var error = response.Data.Error;
// Handle error...
}
We have many services in our system. ( integrating with a mobile company)
So, (for example) we have :
Action1 in Controller1
Action2 in Controller1
...
Action4 in Controller4
Action5 in Controller4
...
Currently, the mobile company calls each action with a single request.
But recently they told us , "can we send you a list of Actions to invoke ? instead of running single action manually each time... ?"
So I tried reflection:
ServicesController :
[HttpGet]
[AllowAnonymous]
public HttpResponseMessage AAA( )
{
Type type = typeof(UsersController);
var instance = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod("Test2", BindingFlags.Instance | BindingFlags.Public);
var t= method.Invoke(instance, new object[] { "royi" });
return Request.CreateResponse(HttpStatusCode.OK, t);
}
And :
UseresController :
[HttpGet]
[AllowAnonymous]
public HttpResponseMessage Test2( string ggg)
{
return Request.CreateResponse(HttpStatusCode.OK, "hello"+ggg);
}
When I run via fiddler :
http://es.com/api/services/aaa ( GET)
It does work , but (obviously) the Request on the other side is null :
Question
How can I make Test2 run as expected ? am I on the right direction of solving this ? or does webApi has built in mechanism for this sort of thing ?
You better use the ActionInvoker to do that:
public HttpResponseMessage AAA()
{
var ctrlDesc = new HttpControllerDescriptor(this.Configuration, "UsersController", typeof(UsersController));
var actionDesc = new ReflectedHttpActionDescriptor(ctrlDesc, typeof(UsersController).GetMethod("Test2"));
var ctrlCtx = new HttpControllerContext(this.Configuration, this.Request.GetRouteData(), this.Request);
var apiCtrl = ctrlDesc.CreateController(this.Request) as ApiController;
apiCtrl.Request = this.Request;
apiCtrl.Configuration = this.Configuration;
apiCtrl.ControllerContext = ctrlCtx;
ctrlCtx.Controller = apiCtrl;
ctrlCtx.ControllerDescriptor = ctrlDesc;
ctrlCtx.Request = this.Request;
ctrlCtx.RouteData = this.Request.GetRouteData();
var actionContext = new HttpActionContext(ctrlCtx, actionDesc);
actionContext.ActionArguments.Add("ggg", "royi");
var invoker = this.Configuration.Services.GetActionInvoker();
return invoker.InvokeActionAsync(actionContext, CancellationToken.None).Result;
}
How can i call a Post method with multiple parameters using HttpClient?
I am using the following code with a single parameter:
var paymentServicePostClient = new HttpClient();
paymentServicePostClient.BaseAddress =
new Uri(ConfigurationManager.AppSettings["PaymentServiceUri"]);
PaymentReceipt payData = SetPostParameter(card);
var paymentServiceResponse =
paymentServicePostClient.PostAsJsonAsync("api/billpayment/", payData).Result;
I need to add another parameter userid. How can i send the parameter along with the 'postData'?
WebApi POST method prototype:
public int Post(PaymentReceipt paymentReceipt,string userid)
Simply use a view model on your Web Api controller that contains both properties. So instead of:
public HttpresponseMessage Post(PaymentReceipt model, int userid)
{
...
}
use:
public HttpresponseMessage Post(PaymentReceiptViewModel model)
{
...
}
where the PaymentReceiptViewModel will obviously contain the userid property. Then you will be able to call the method normally:
var model = new PaymentReceiptViewModel()
model.PayData = ...
model.UserId = ...
var paymentServiceResponse = paymentServicePostClient
.PostAsJsonAsync("api/billpayment/", model)
.Result;
UserId should be in query string:
var paymentServiceResponse = paymentServicePostClient
.PostAsJsonAsync("api/billpayment?userId=" + userId.ToString(), payData)
.Result;
In my case my existing ViewModels don't line up very nicely with the data I want to post to my WebAPI. So, instead of creating an entire new set of model classes, I posted an anonymous type, and had my Controller accept a dynamic.
var paymentServiceResponse = paymentServicePostClient.PostAsJsonAsync("api/billpayment/", new { payData, userid }).Result;
public int Post([FromBody]dynamic model)
{
PaymentReceipt paymentReceipt = (PaymentReceipt)model.paymentReceipt;
string userid = (string)model.userid;
...
}
(I'd be curious to hear some feedback on this approach. It's definitely a lot less code.)