.NET Core OData Action Parameter Null - .net-core

My Odata Action Parameters are not resolving / deserializing.
I am using dotnet core 2.2 to surface an OData controller.
I need to implement an unbounded action. The action parameter (UserDto userDto) is not being deserialized by the OData routing engine:
[AllowAnonymous]
[HttpPost]
[ODataRoute(Routing.Endpoints.UserRoutes.AUTHENTICATE)]
public async Task<IActionResult> Authenticate(UserDto userDto)
{
var user = await _userService.Authenticate(userDto?.Username, userDto?.Password);
if (user == null)
return BadRequest("Username or password is incorrect");
var dto = Mapper.Map<UserDto>(user);
return Ok(dto);
}
Here is my configuration:
app.UseMvc(routeBuilder =>
{
var odataBuilder = new ODataConventionModelBuilder(app.ApplicationServices);
odataBuilder.EnableLowerCamelCase();
odataBuilder.EntitySet<BookDto>(nameof(Book));
odataBuilder.EntitySet<UserDto>(nameof(User));
var authenticate = odataBuilder.Action(Routing.Endpoints.UserRoutes.AUTHENTICATE);
authenticate.Parameter<UserDto>("userDto");
routeBuilder.Select().Expand().Filter().OrderBy().Count().MaxTop(int.MaxValue);
routeBuilder.MapODataServiceRoute("odata", string.Empty, odataBuilder.GetEdmModel());
});
Here is the UserDto:
public class UserDto
{
[Key] public Guid Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Token { get; set; }
}
When I post:
The action is resolved by the routing engine - but the parameter does not have the "Username" and "Password" values:
If I use the [FromBody] attribute on the parameter - the "userDto" parameter is null:
The schema seems correct:
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="Default">
<Action Name="authenticate">
<Parameter Name="userDto" Type="ExampleApi.Dto.UserDto"/>
</Action>
<EntityContainer Name="Container">
<EntitySet Name="Book" EntityType="ExampleApi.Dto.BookDto"/>
<EntitySet Name="User" EntityType="ExampleApi.Dto.UserDto"/>
<ActionImport Name="authenticate" Action="Default.authenticate"/>
</EntityContainer>
</Schema>
I have tried following this: Action Parameter Support
And even Microsofts version (albeit dated): Actions and Functions in OData
Been banging my head on this all day...

You could use simple WebApi attributes only to achieve authentication
public class UserController : ODataController
{
[AllowAnonymous]
[HttpPost("user/auth")]
public async Task<IActionResult> Authenticate([FromBody] UserDto userDto)
{
return Ok(userDto);
}
}

Related

Routes having conflict in URL rewriting or configuring route in ASP.NET Web API

I am developing an ASP.Net MVC application. In my project I am providing REST API for mobile devices. But I am having a problem with configuring route for URLs of my rest API in Web Api. I am using Web Api 2 and MVC 5. My problem is routes having conflict. Request cannot choose the correct route even I configured it clearly.
I have AccountController like this.
[RoutePrefix("v1")]
public class AccountController : ApiController
{
public UserManager<ApplicationUser> UserManager { get; private set; }
[HttpPost]
public async Task<IHttpActionResult> Register(RegisterBM model)
{
}
[HttpPost]
public IHttpActionResult Login(LoginBM model)
{
}
}
I configured routes in WebApiConfig like this
config.Routes.MapHttpRoute(
name: "",
routeTemplate: "api/v1/account/register",
defaults: new { controller = "Account", action = "Register" ,model = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "",
routeTemplate: "api/v1/account/login",
defaults: new { controller = "Account", action = "Login", model = RouteParameter.Optional }
);
These are my model classes
public class LoginBM
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
public string Password { get; set; }
}
public class RegisterBM
{
[Required]
public string Name { get; set; }
[Required]
[EmailAddress]
[MaxLength(70)]
[UniqueAccountEmail(ErrorMessage = "Email is alreay taken")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
public string Password { get; set; }
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
This is error I get when I make request to http://localhost:50489/api/account/login
What is wrong with my route configuration? How can I fix that?
The URL you are testing http://localhost:50489/api/account/login does not match the routeTemplate of the route you have provided api/v1/account/login, namely you are missing v1 from the URL. Your test is apparently reaching another route, which is causing this exception.
Since Web API and MVC are separate frameworks, each has their own controllers. URLs must be unique for the application, but route values are not shared between them. So you can use the same set of route values in both frameworks (same names for controllers and actions) provided you use unique URLs across them both. Generally, this is done by prefixing the Web Api URL with /api (which you have already done). But if you put a version in the routeTemplate, you must also use it in the actual URL or you will not reach the controller action.

ServiceStack JsonServiceClient: The requested resource does not support http method GET

So I recently remade my API and client in a new solution on a different version control scheme. Some of the code has change, but nothing related to the JsonServiceClient. Anyways, I'm getting this exception stating 405 Method Not Allowed Code: Method Not Allowed, Message: The requested resource does not support http method 'GET'.
The issue is that the request is not a GET but a POST, I'm not sure where its getting mixed up. The actual post is represented as such:
var result = (
await
webService.PostAsync(new Login
{
Token = token,
Provider = provider,
IsLinkedAccount = isLinkedAccount,
ResponseText = responseText,
Uuid = uuid,
Platform = platform
}))
The Login DTO is represented as this:
[Route("/login", "POST")]
public partial class Login
: IReturn<LoginDTO>
{
public virtual string Token { get; set; }
public virtual SocialNetworks Provider { get; set; }
public virtual string Email { get; set; }
public virtual bool IsLinkedAccount { get; set; }
public virtual string ResponseText { get; set; }
public virtual string Uuid { get; set; }
public virtual Platform Platform { get; set; }
}
And the actual API route is represented as this:
public class LoginController : ApiController
{
[AcceptVerbs("POST")]
public async Task<LoginDTO> Login(Login request)
{
...
}
}
And I've used swagger and postman to verify that the endpoint is indeed accurate and I can manually send an HTTP POST, so what messed up with the JsonServiceClient?
These is the service stack versions I'm using
<package id="ServiceStack.Client" version="4.0.52" targetFramework="net452"/>
<package id="ServiceStack.Common" version="4.0.52" targetFramework="net452"/>
<package id="ServiceStack.Interfaces" version="4.0.52" targetFramework="net452" />
<package id="ServiceStack.Text" version="4.0.52" targetFramework="net452" />

How can I extend ApplicationUser in ASP.NET Identity but with properties that can be null?

I would like to extend Application User. Here's an example:
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity>
GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
I tried this but it seems like when I login if any of these properties is set to a null in the database then when the /Token call is made it returns with an error.
Can someone tell me. Do I need to make a change to the way these properties are set here or in another place?
If i understand your question correctly, a simple solution to your problem could be to create a nullable type. Can you please share what you would like to do? Create a custom identity provider, for example?

Using optional complex type action parameter for a POST Web API

I am trying to write an API with the following prototype:
[HttpPost]
public ApplicationStatus appLogIn(string id, UserCredentials userCredentials = null)
But when I make a request to this API, with or without userCredentials, I get the following error with response code as 500
{
"Message": "An error has occurred.",
"ExceptionMessage": "Optional parameter 'userCredentials' is not supported by 'FormatterParameterBinding'.",
"ExceptionType": "System.InvalidOperationException",
"StackTrace": null
}
If I do not make the userCredentials as optional, everything works fine. The userCredential definition is as follows:
public class UserCredentials
{
public string password { get; set; }
public string username { get; set; }
}
Try changing the API definition to:
[HttpPost]
public ApplicationStatus appLogIn(string id, UserCredentials userCredentials)
As UserCredentials is a reference type, if client doesn't provide a value it will be set to null

ASP.NET MVC API model not parsing

I am having some problems parsing my model in ASP.NET MVC API
This is my API controller:
public class UserController : ApiController
{
// Hent liste af personer
public IEnumerable<UserModel> Get()
{
return new UserModel[] { new UserModel(), new UserModel() };
}
// Hente enkelt person
public UserModel Get(int id)
{
return new UserModel();
}
// Opret person
[ValidationActionFilter]
public CreateUserRespose Post([FromBody]UserModel model)
{
CreateUserRespose rs = new CreateUserRespose();
return rs;
}
// Rediger person
public UserModel Put(int id, [FromBody]UserModel model)
{
return new UserModel();
}
// Slet person
public UserModel Delete(int id)
{
return new UserModel();
}
}
}
And the UserModel:
public class UserModel
{
[Required]
[StringLength(500)]
public String FristName { get; set; }
[Required]
[StringLength(500)]
public String LastName { get; set; }
[Required]
[StringLength(250)]
public String Email { get; set; }
[Required]
public String MatrikelId { get; set; }
}
When I call though Fiddler to the Post command with the following body
FirstName=Fistname MiddleName&LastName=SomeName&Email=email#email.us&MatrikelId=1234
Will the action Post be called, but the model is null, and ModelState.IsValid is true, the same happens if I send no data with the body!
What am I doing wrong here?
Update:
I have tryed sending the data as json instead
Fiddler:
User-Agent: Fiddler
Host: localhost:51268
Content-Length: 102
Content-type: application/json
{"FristName":"Kasper asdasd","LastName":"asdasdasd","Email":"asdaasd#asdasd.us","MatrikelId":"132456asd"}
But should the model state not be invalid when the model is null?
The ASP.NET Web API is using content negotiation process in order to decide which MediaTypeFormatter to use for deserializing the body of the request. For the typical POST request it will check for Accept and Content-Type headers. If none is present it will use the first MediaTypeFormatter on the list (by default it is JsonMediaTypeFormatter).
In your case Web API was unable to determine the proper MediaTypeFormatter. Adding a Content-Type header with value of application/x-www-form-urlencoded to the request should resolve the issue.
If you want to get more detailed knowledge regarding Formatters, Model Binding and Content Negotiation in ASP.NET Web API I would suggest following reading:
Designing Evolvable Web APIs with ASP.NET -> Chapter 8. Formatters and Model Binding (you should look very close at the entire book if you are interesed in learning ASP.NET Web API)
Everything you want to know about ASP.NET Web API content negotiation

Resources