forwarding from register to authenticate not work - spring-boot

I have two postmapping, one is, e.g. /api/register, another is /api/authenticate, after /api/register given username and password, I want to forward the request to /api/authenticate, make it unnecessary for users input username and password again, my /api/register method like:
#PostMapping("/api/register")
public String register(#RequestBody xxxx) {
...
return "forward:/api/authenticate";
}
and /api/authenticate is like:
#PostMapping("/api/authenticate")
public ResponseEntity<JWTToken> authorize(#RequestBody LoginVM loginVM) {
....
}
but it does not work, I emulate with postman and get a string like: "Expected 'a' instead of 'o'", don't know why?

Forward / redirect only works with endpoints which support Get requests so PostMapping("/api/authenticate") would not work.
The best approach you have is to call authorize method directly from your code. Something like -
#PostMapping("/api/register")
public ResponseEntity<JWTToken> register(#RequestBody xxxx) {
...
return authorize(...);
}

Related

How to use Spring method DELETE

I have some customers in ArrayList and when I want through Postman delete (with id) it is not working : 500 - Internal Error. Please Could someone help me ?
Delete customer
DELETE http://localhost:8080/api/customers/{customerId}
#DeleteMapping("/api/customers{customerId}")
public void deleteCustomer(#PathVariable Long customerId) {
customers.remove(this.customers.get(Math.toIntExact(customerId)));
}
try like this you did not add "/" in delete mapping.
#DeleteMapping("/api/customers/{customerId}")
public void deleteCustomer(#PathVariable Long customerId) {
customers.remove(this.customers.get(Math.toIntExact(customerId)));
}
Is this how you send the request?
http://localhost:8080/api/customers/{customerId}
If you are sending like this, it won't work because you gave path like this:
/api/customers{customerId}
In my opinion, either change the path like this:
/api/customers/{customerId}
Or send the request like this:
http://localhost:8080/api/customers{customerId}

Unexpected return code while testing spring controller

I have simple RestController in my application, with post mapping
#PostMapping("/bank/api/balance")
fun changeBalance(#RequestParam amount: String, #RequestParam action: String)
And I have test function for this controller.
private fun postBalanceAddRequest(amount: String): ResultActions {
return mockMvc.perform(MockMvcRequestBuilders.post(baseUrl).apply {
param("action", "add")
param("amount", amount)
})
}
Which I am using in this test
#Test
fun `add post request must return ok if request contains correct data`() {
Mockito.`when`(authenticationService.getCurrentUserUsername()).thenReturn("user")
Mockito.`when`(userService.userExist("user")).thenReturn(true)
postBalanceAddRequest(amount = "10")
.andExpect(MockMvcResultMatchers.status().isOk)
}
When I start this test I got 404 code, but it seems to me that there cannot be such a return code here.
when using MockMvc you don't need to provide the full url (including domain and port), the mockMvc will take care of it.
instead, you need just the uri. set your baseUrl to /bank/api/balance
Thanks for help, I just forgot to add a necessary controller. As for mockMvc, you can use short url, without domain and port
(I think it is the best way), but if you want you can use full url.

Spring Controller: Handling Custom return types

I'd like my Spring Controllers to return an Either<Redirect, X> which I can then unwrap using a Filter/AOP to avoid repetitive "is this a redirect, or is it a 2xx response" code.
If the return type is an Either.Left(Redirect("host", "port")) then my Filter/AOP will return a 302.
If the return type is an Either.Right(Vehicle(...)) then my Filter/AOP will return a 200 and serialize the payload to JSON.
e.g.
#GetMapping("vehicles/{id}")
public Either<Redirect, Vehicle> getById(#PathVariable("id") String id) {
return someService.get(id);
}
Is it possible to do this using Spring? Or do I have to handle this redirection in my Controller layer e.g.
#GetMapping("vehicles/{id}")
public ResponseEntity getById(#PathVariable("id") String id) {
return someService.get(id)
.fold(
redirect -> ResponseEntity.status(302).location(...).build(),
payload -> ResponseEntity.ok(payload).build()
);
}
A bit of extra context if it is useful:
This app is a member of a cluster and has a "local state". The total state is spread across the cluster.
As a result, when a request comes in for a particular id/key it could be coming into the wrong instance of the app, and will need to be redirected to the correct instance.

aspnet identity; how to login using a legacy password?

i've a site i'm updating to web api with aspnet identity 2.0.
It's a legacy site for which we need to allow the users to use their old passwords; at least during a reasonable migration period
following this article, i've derived a new UserManager from the base UserManager, and set up the PasswordHasher to hash with an old SHA1 algorithm.
My passwordHasher looks like this:
public class SQLPasswordHasher : PasswordHasher
{
public override string HashPassword(string password)
{
string cipherText = EncryptPassword(password);
return cipherText;
}
public override PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
string cipherText = EncryptPassword(providedPassword);
if (cipherText == hashedPassword)
{
return PasswordVerificationResult.SuccessRehashNeeded;
}
else
{
return PasswordVerificationResult.Failed;
}
}
private string EncryptPassword(string plainText)
{
return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(plainText, "sha1");
}
}
When i register users with this code, I can see the passwords are being hashed and persisted in the database correctly... for the password 'foobar', the hashed value is fixed and recognizable, since this algorithm did not use a salt.
However, I cannot log in as these users. If i set a breakpoint in the new hasher, it never gets it. Neither can i seem to hit a breakpoint anywhere in the account controller when trying to log in.
thanks in advance
I'm answering my own question, in the hopes that someone else may benefit.
The problem was, i couldn't find what in the web api service was being called when logging in. I finally realized that something called /Token was being set up as the url to be called in the app.js javascript.
Searching through the project server side sources and googling led me to this article, which pointed me to the ApplicationOAuthProvider.cs file, in the 'Providers' folder of the template application.
The specific line of interest is where the method GrantResourceOwnerCredentials instantiates it's own user manager, thus:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
From there, all i had to do was add this line:
userManager.PasswordHasher = new SQLPasswordHasher();
and i could finally log in.

Restricting auto Help Page contents when using Attribute Routing in Web API 2

I'm currently implementing a Web API using Web API 2's attribute routing (http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2). I am also using the Help Pages module in order to automatically generate documentation from XML comments (http://www.asp.net/web-api/overview/creating-web-apis/creating-api-help-pages).
For this API I am providing support for optional return format extensions, so that every API method has a pair of routes defined on it like so:
[HttpGet]
[Route("Path/Foo")]
[Route("Path/Foo.{ext}")]
public HttpResponseMessage DoFoo()
{
// Some API function.
}
This allows a user to hit any of these and get a result:
www.example.com/api/Controller/Path/Foo
www.example.com/api/Controller/Path/Foo.json
www.example.com/api/Controller/Path/Foo.xml
My issue is that when Help Pages uses MapHttpAttributeRoutes() to generate documentation, it is picking up both routes for each method. So right now I see help for:
api/Controller/Foo
api/Controller/Foo.{ext}
But I want to only see:
api/Controller/Foo.{ext}
I would prefer to hide the non-extension route on each method, so that every method only shows a single Help Page entry.
Has anyone else tried something similar? Is there a work around that I am missing?
My question would be is that, would consumers of your api figure out easily that the {ext} is optional?...personally, I would prefer the default behavior...but anyways following are some workarounds that I can think of:
A quick and dirty workaround. Split the DoFoo into 2 actions like DoFoo() and DoFooWithExt maybe. Notice that I am using an attribute called ApiExplorerSettings, which is for HelpPage purposes. Example below:
[HttpGet]
[Route("Path/Foo")]
[ApiExplorerSettings(IgnoreApi=true)]
public HttpResponseMessage DoFoo()
{
return DoFooHelper();
}
[HttpGet]
[Route("Path/Foo.{ext}")]
public HttpResponseMessage DoFooWithExt()
{
return DoFooHelper();
}
private HttpResponseMessage DoFooHelper()
{
//do something
}
Create a custom ApiExplorer (which HelpPage feature uses internally) and check for specific routes like the following and can decide whether to show the action or not for that particular route.
// update the config with this custom implementation
config.Services.Replace(typeof(IApiExplorer), new CustomApiExplorer(config));
public class CustomApiExplorer : ApiExplorer
{
public CustomApiExplorer(HttpConfiguration config) : base(config)
{
}
public override bool ShouldExploreAction(string actionVariableValue, HttpActionDescriptor actionDescriptor, IHttpRoute route)
{
if (route.RouteTemplate.EndsWith("Path/Foo", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return base.ShouldExploreAction(actionVariableValue, actionDescriptor, route);
}
}
Get list of all ApiDescription from the default ApiExplorer and then filter out the descriptions which you do not like. Example:
Configuration.Services.GetApiExplorer().ApiDescriptions.Where((apiDesc) => !apiDesc.RelativePath.EndsWith("Path/Foo", StringComparison.OrdinalIgnoreCase))

Resources