WebAPI OData v4 custom action without parameters can't be routed with error "No routing convention was found..." - asp.net-web-api

I have very simple OData controller that successfully process standard actions (at least GET, POST, PUT and DELETE methods are working). I have followed this tutorial and added simple bound action. The method has parameters argument, but actually it does not required the parameters:
[HttpPost]
public IHttpActionResult Close([FromODataUri] int key, ODataActionParameters parameters) {
return Ok();
}
I have defined this action in OData EDM configuration as following:
builder.EntitySet<Ticket>("tickets");
builder.EntityType<Ticket>().Action("Close");
I am trying to call action from Postman:
POST /odata/tickets(2)/Default.Close HTTP/1.1
Host: localhost:50477
Accept: application/json
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: eef4c1f6-8c7f-f5eb-c22d-4397f3bda170
But receives the error message:
{
"error": {
"code": "",
"message": "No HTTP resource was found that matches the request URI 'http://localhost:50477/odata/tickets(2)/default.close'.",
"innererror": {
"message": "No routing convention was found to select an action for the OData path with template '~/entityset/key/unresolved'.",
"type": "",
"stacktrace": ""
}
}
}
I have read the whole internet and all related articles on SO but can't fix this issue. Please help me because I have no any fresh idea how to fight this.

My controller:
public class TicketsController : ODataController
{
[HttpPost]
public IHttpActionResult Close([FromODataUri] int key, ODataActionParameters parameters)
{
return Ok();
}
}
My request:
string requestUri = "http://localhost/odata/tickets(2)/Default.Close";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri);
request.Content = new StringContent("",
Encoding.UTF8,
"application/json");
HttpResponseMessage response = _client.SendAsync(request).Result;
Or remove the ODataActionParameters parameters in the close method and call with:
string requestUri = "http://localhost/odata/tickets(2)/Default.Close";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri);
HttpResponseMessage response = _client.SendAsync(request).Result;
My EdmModel is use your model.

Related

application/json not supported when passing multipart files and json together

I have a REST controller method which will take multipart files and JSON object to save as a product with images.
Here is my controller method.
#PostMapping(value = "/{username}/saveProduct", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_OCTET_STREAM_VALUE})
public void saveProduct(#PathVariable("username") String username,
#RequestPart("multipartFiles") List<MultipartFile> multipartFiles,
#RequestPart("product") Product product)
{
Users user = userService.findUserByUsername(username);
List<Images> listOfImages = productService.getBLOBfromFile(multipartFiles, product);
product.setImages(listOfImages);
product.setUser(user);
user.setProducts(product);
userService.saveUser(user);
}
For some reason I am getting this error:
"timestamp": "2021-01-18T20:05:32.409+00:00",
"status": 415,
"error": "Unsupported Media Type",
"trace": "org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json' not supported\r\n\tat org.
From postman I am sending
I tried using #RequestParam and #ModelAttribute as well. Did not work for me.
Also, this method was working when I was writing MVC app.

Spring post method "Required request body is missing"

#PostMapping(path="/login")
public ResponseEntity<User> loginUser(#RequestBody Map<String, String> userData) throws Exception {
return ResponseEntity.ok(userService.login(userData));
}
I have this method for the login in the UserController. The problem is when i try to make the post request for the login i get this error:
{
"timestamp": "2018-10-24T16:47:04.691+0000",
"status": 400,
"error": "Bad Request",
"message": "Required request body is missing: public org.springframework.http.ResponseEntity<org.scd.model.User> org.scd.controller.UserController.loginUser(java.util.Map<java.lang.String, java.lang.String>) throws java.lang.Exception",
"path": "/users/login"
}
You have to pass that as JSON in your body, if it's a POST request.
I had a similar issue, was getting this error in my Spring Boot service
HttpMessageNotReadableException: Required request body is missing:...
My issue was that, when I was making requests from Postman, the "Content-Length" header was unchecked, so service was not considering the request body.
This is happening because you are not passing a body to you server.
As can I see in your screenshot you are passing email and password as a ResquestParam.
To handle this values, you can do the following:
#PostMapping(path="/login")
public ResponseEntity<User> loginUser(#RequestParam("email") String email, #RequestParam("password") String password) {
//your imp
}
In order to accept an empty body you can use the required param in the RequestBody annotation:
#RequestBody(required = false)
But this will not solve your problem. Receiving as RequestParam will.
If you want to use RequestBody you should pass the email and password in the body.
You need to send data in Body as JSON
{ "email":"email#email.com", "password":"tuffCookie"}
If it's still not working, try adding additional information UTF-8 in Headers.
key : Content-Type
value : application/json; charset=utf-8
For my case, I must adding UTF-8 in Headers.
In my case it was poorly defined JSON that I sent to my REST service.
Attribute that was suppose to be an object, was in my case just string:
Changed from:
"client" = "",
to:
"client" = { ... },
In my case String did not add additional information about value in different format.

Angular POST request's header is null

I am developing an Ionic 3 Mobile Application, I have problem with Angular's POST method.
In login page, I created a form and tried send data to server with Angular HTTP POST method. But in server (.NET WEB API) I see request's header is null.
Here is the Angular side codes;
login(username, password):Observable<Object>{
let url : string = this.apiUrl+"/login";
let headers = new HttpHeaders();
headers.append('Authorization', btoa(username+":"+password).toString());
return this.http.post(url,JSON.stringify({username,password}), {headers: headers});
}
Here is the .NET side codes for controller;
[EnableCors(origins: "http://localhost:8100", headers: "*", methods: "*")]
public Response Post()
{
return _mobileUserService.Login();
}
Here is the part of .NET side codes for catch request;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
var token = request.Headers.GetValues("Authorization").FirstOrDefault();
}
catch (Exception ex)
{
}
return base.SendAsync(request, cancellationToken);
}
When request catched by .NET (in running), I see these values for "request" variable;
request = {Method: POST, RequestUri: 'http://localhost:41582/api/login', Version: 1.1, Content: System.Web.Http.WebHost.HttpControllerHandler+LazyStreamContent, Headers:
{
Connection: keep-alive
Accept: application/json
Accept: text/plain
Accept: */*
...
In normally, request's url is localhost:8100, so I think server accepted CORS
How can I solve that?
In Web api you have to tell which method is post or get based on how you have setup your route.
[EnableCors(origins: "http://localhost:8100", headers: "*", methods: "*")]
[HttpPost] // Decorate post this attribute in your controller
public Response Post()
{
return _mobileUserService.Login();
}

Asp.Net WebApi OData with DataJs throws an error

I just created a really simple ASP.Net WebApi project. I used NuGet to download the latest OData in WebAPI – RC release. I also download DataJs and Knockout via NuGet. All my dependencies are up to date. I created a simple "Books" class and wired everything together using HttpConfiguration.EnableOData(IEdmModel). I also added the [Queryable] attribute to my Get action in the controller. There is not database involved, I hard-coded the data I want returned. Basically, I did the minimum amount of changes to run my project with WebApi and OData.
When I try to query the OData service using DataJs, I get a 500 Internal Server Error in the response, but if I browse to the URL directly I can see the XML data. I've included the request, response, my C# class, the Javascript code, and the Global.asax code. What am I missing to get this to work?
REQUEST
Response Headers
Cache-Control private
Content-Length 966
Content-Type application/json; odata=fullmetadata; charset=utf-8
DataServiceVersion 3.0;
Date Fri, 21 Dec 2012 22:13:27 GMT
Server Microsoft-IIS/8.0
X-AspNet-Version 4.0.30319
X-Powered-By ASP.NET
X-SourceFiles =?UTF-8?B?YzpcdXNlcnNcanVzdGluXGRvY3VtZW50c1x2aXN1YWwgc3R1ZGlvIDIwMTJcUHJvamVjdHNcRGF0YUpzU3Bpa2VcRGF0YUpzU3Bpa2VcYXBpXEJvb2tz?=
Request Headers
Accept application/atomsvc+xml;q=0.8, application/json;odata=fullmetadata;q=0.7, application/json;q=0.5, */*;q=0.1
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Connection keep-alive
Cookie glimpseState=null; glimpseLatestVersion=0.87; glimpseOptions=null; glimpseClientName=null
Host localhost:31652
MaxDataServiceVersion 3.0
Referer http://{localhost}/
User-Agent Mozilla/5.0 (Windows NT 6.2; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0
RESPONSE
{
"odata.error":{
"code":"","message":{
"lang":"en-US","value":"An error has occurred."
},"innererror":{
"message":"The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; odata=fullmetadata; charset=utf-8'.","type":"System.InvalidOperationException","stacktrace":"","internalexception":{
"message":"The related entity set could not be found. The related entity set is required to serialize the payload.","type":"System.Runtime.Serialization.SerializationException","stacktrace":" at System.Web.Http.OData.Formatter.Serialization.ODataFeedSerializer.WriteObject(Object graph, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)\r\n at System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.<>c__DisplayClass8.<WriteToStreamAsync>b__7()\r\n at System.Threading.Tasks.TaskHelpers.RunSynchronously(Action action, CancellationToken token)"
}
}
}
}
C# Class
namespace DataJsSpike.Models
{
public class Book
{
public string ISBN { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Publisher { get; set; }
}
}
Javascript Code
// the URL of the first page to retrieve
var startPage = "api/Books";
var viewModel = new Object();
viewModel.books = ko.observable();
// On initialization, make a request for the first page
$(document).ready(function () {
LoadDataJs();
function LoadDataJs() {
OData.read(startPage, function (data) {
viewModel.books(data.results);
ko.applyBindings(viewModel);
});
}
});
Global.asax
public class WebApiApplication : HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
var modelBuilder = new ODataConventionModelBuilder();
EntityTypeConfiguration<Book> bookConfiguration = modelBuilder.Entity<Book>();
bookConfiguration.HasKey(x => x.ISBN);
modelBuilder.EntitySet<Book>("Books");
IEdmModel model = modelBuilder.GetEdmModel();
GlobalConfiguration.Configuration.EnableOData(model, "api");
}
}
EnableOData actually registers a route for you, but since you registered routes before it ran, those routes take precedence. If you remove this line:
RouteConfig.RegisterRoutes(RouteTable.Routes);
I think it should work out. The request needs to come in on an OData route for the OData formatting to work because the route parses the OData path and gives the formatter information about things like the Entity Set that's being accessed.

Self-hosted Web API - Post Param null

I'm trying to set up a simple test to make a POST request to my self-hosted Web-API server.
var config = new HttpSelfHostConfiguration(new Uri(baseAddress));
// add a route
config.Routes.MapHttpRoute(name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
server = new HttpSelfHostServer(config);
server.OpenAsync().Wait();
I added a Controller:
public class PayloadController : ApiController
{
public string Get()
{
return "Hello from GET";
}
public HttpResponseMessage PostItem(Payload payload)
{
// payload
//...
}
}
And I do have the corresponding model (making sure it contains properties):
public class Payload
{
public string Date { get; set; }
public string Command { get; set; }
}
I'm using Rest Console on Chrome to test the server, getting correct results for the GET in my controller.
Issueing the following request as POST:
Request Url: http://localhost:8080/api/payload
Request Method: POST
Params: {
"Date": "2012.09.26",
"Command": "Hello"
}
EDIT: Content-Type is set to application/json and encoding to UTF-8
Setting a break-point at the controller shows that the POST params are not correctly deserialized into an Paylod object, the param is null.
What am I possibly missing?
You need to specify the request Content-Type as application/json
Edit
Complete response with info from the comments below:
If you are using REST console's "request parameters" instead of "request payload raw body" it will not generate JSON out of them even when you specify application/json.
You need to paste raw JSON into the raw body field like this: http://screencast.com/t/iRWZqv91
{
"Date": "2012.09.26",
"Command": "Hello"
}
If you are receiving a null post parameter to your controller you need to create a constructor with 0 parameters in your model.
Greetings

Resources