Target class [blade.compiler] does not exist. in Lumen can we use balde in lumen provider - package

In Lumen can we use Blade in the Lumen provider?
Target class [blade.compiler] does not exist.
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class RolesServiceProvider extends ServiceProvider
{
/**
* #return void
*/
public function register()
{
}
/**
* #return void
*/
public function boot()
{
Blade::directive('role', function ($role) {
return "<?php if(auth()->check() &&
auth()->user()->hasRole({$role})) :";
});
Blade::directive('endrole', function ($role) {
return "<?php endif; ?>";
});
}
}

In your scenario, it's happening because I believe that you forgot to register the Illuminate\View\ViewServiceProvider class.
Also, when registering the Provider, make sure to use $app->configure('view') in your bootstrap/app.php or $this->app->configure('view') from your Service Provider to configure your view configuration. Because the view service provider doesn't load the configuration itself.
You can check how the view component is loaded in a Lumen application.
I did like the following
if (!$this->app->bound('view')) {
// Lumen doesn't load the view config by default
$this->app->configure('view');
$this->app->register(ViewServiceProvider::class);
}
Or you can do the loadComponent thing as lumen does. And it will solve the issue you stated.

Related

Spring Boot + React all pages return 404 except index [duplicate]

I have a single page Angular app with Spring Boot. It looks like the following:
src
main
java
controller
HomeController
CustomerController
OtherController
webapp
js/angular-files.js
index.html
Spring boot correctly defaults to webapp folder and serves index.html file.
What I am looking to do is:
For every local REST request not starting with /api overwrite and redirect to default webapp/index.html. I plan to serve anything /api to the spring controllers.
Is there a way to prefix all controllers with API so that I do not have to write API every time?
e.g.
#RequestMapping("/api/home") can write shorthand in code #RequestMapping("/home")
or
#RequestMapping("/api/other-controller/:id") can write shorthand #RequestMapping("/other-controller/:id")
I'm looking for every API request, e.g. 1) http://localhost:8080/api/home keep API with API and resolve to correct controller and return JSON, however if someone enters a URL like http:///localhost/some-url or http:///localhost/some-other/123/url then it will serve the index.html page and keep the URL.
Alternative ways to do it: try adding #ErrorViewResolver:
Springboot/Angular2 - How to handle HTML5 urls?
If you're tired of trying to solve this problem by following so many conflicting solutions - look here!!
After hours upon hours trying to follow all the scattered advice from dozens of stack overflow and blog posts, I've finally found the minimum PURE spring boot + angular 6 application to always redirect to index.html after a refresh on a non-root page WHILE maintaining all your REST API endpoint paths. No #EnableWebMvc, no #ControllerAdvice, no changes to application.properties, no custom ResourceHandlerRegistry modifications, just simplicity:
Very important pre-requisite
You *must* include the output of ng build into Spring's resources/static folder. You can accomplish this via the maven-resources-plugin. Learn here: Copying multiple resource directories to independent target directories with maven
Code
#Controller
#SpringBootApplication
public class MyApp implements ErrorController {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
private static final String PATH = "/error";
#RequestMapping(value = PATH)
public String error() {
return "forward:/index.html";
}
#Override
public String getErrorPath() {
return PATH;
}
}
Reasoning
Including the output of ng-build into resources/static at build time allows spring view redirects ("forward:/index.html") to succeed. It seems spring cannot redirect to anything outside of the resources folder so if you're trying to access pages at the root of the site, it won't work.
With default functionality (i.e. no additions of #EnableWebMvc or changes to application.properties) navigating to / automatically serves the index.html (iff it was included in the resources/static folder) so no need to make changes there.
With default functionality (as stated above), any error encountered in a spring boot app routes to /error and implementing ErrorController overrides that behavior to - you guessed it - route to index.html which allows Angular to take over the routing.
Remarks
Don't settle for the HashLocationStrategy to get over this problem as it is not recommended by Angular: https://angular.io/guide/router#which-strategy-is-best
For every local REST request not starting with /api overwrite and redirect to default webapp/index.html. I plan to serve anything /api to the spring controllers.
Update 15/05/2017
Let me re-phrase your query for other readers. (Correct me, if misunderstood)
Background
Using Spring Boot and Serving static resources from classpath
Requirement
All 404 non api requests should be redirected to index.html.
NON API - means Requests in which URL doesn't start with /api.
API - 404 should throw 404 as usual.
Sample Response
/api/something - will throw 404
/index.html - will server index.html
/something - will redirect to index.html
My Solution
Let the Spring MVC throw exceptions, if any handler is not available for the given resource.
Add following to application.properties
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
Add a ControllerAdvice as follows
#ControllerAdvice
public class RedirectOnResourceNotFoundException {
#ExceptionHandler(value = NoHandlerFoundException.class)
public Object handleStaticResourceNotFound(final NoHandlerFoundException ex, HttpServletRequest req, RedirectAttributes redirectAttributes) {
if (req.getRequestURI().startsWith("/api"))
return this.getApiResourceNotFoundBody(ex, req);
else {
redirectAttributes.addFlashAttribute("errorMessage", "My Custom error message");
return "redirect:/index.html";
}
}
private ResponseEntity<String> getApiResourceNotFoundBody(NoHandlerFoundException ex, HttpServletRequest req) {
return new ResponseEntity<>("Not Found !!", HttpStatus.NOT_FOUND);
}
}
You can customize the error message as you like.
Is there a way to prefix all controllers with api so that I do not have to write api every time.
For this, you can create a BaseController and set the RequestMapping path to /api
Example
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RequestMapping("/api")
public abstract class BaseController {}
And extend this BaseController and make sure you do not annotate child class with #RequestMapping
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class FirstTestController extends BaseController {
#RequestMapping(path = "/something")
public String sayHello() {
return "Hello World !!";
}
}
Previous Answer
You can create a Filter which redirects to /index.html if request path doesn't startsWith /api.
// CODE REMOVED. Check Edit History If you want.
Try this instead
#SpringBootApplication
#Controller
class YourSpringBootApp {
// Match everything without a suffix (so not a static resource)
#RequestMapping(value = "/**/{path:[^.]*}")
public String redirect() {
// Forward to home page so that route is preserved.(i.e forward:/intex.html)
return "forward:/";
}
}
#Controller
public class RedirectController {
/*
* Redirects all routes to FrontEnd except: '/', '/index.html', '/api', '/api/**'
*/
#RequestMapping(value = "{_:^(?!index\\.html|api).*$}")
public String redirectApi() {
return "forward:/";
}
}
Too late on this thread, but thought it might help someone
Tried many solutions, but this looked pretty straight forward and great to me
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.PathResourceResolver;
import java.io.IOException;
#Configuration
public class MvcConfiguration implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/")
.resourceChain(true)
.addResolver(new PathResourceResolver() {
#Override
protected Resource getResource(String resourcePath, Resource location) throws IOException {
Resource requestedResource = location.createRelative(resourcePath);
return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
: new ClassPathResource("/static/index.html");
}
});
}
}
Credits: https://keepgrowing.in/java/springboot/make-spring-boot-surrender-routing-control-to-angular/
The solution that works to me is to overwrite the BasicErrorController of Spring Boot:
#Component
public class CustomErrorController extends BasicErrorController {
public CustomErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes, new ErrorProperties());
}
#RequestMapping(produces = "text/html")
#Override
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NOT_FOUND) {
return new ModelAndView("forward:/");
} else {
return super.errorHtml(request, response);
}
}
}
The method errorHtml only intercepts not found requests and is transparent for responses 404 (not found) from the api.
Most reasonable solution, imho, for Spring Boot 2+ (code is in Kotlin):
#Component
class ForwardErrorsToIndex : ErrorViewResolver {
override fun resolveErrorView(request: HttpServletRequest?,
status: HttpStatus?,
model: MutableMap<String, Any>?): ModelAndView {
return ModelAndView("forward:/index.html")
}
}
For whole application, you can add context path in application.properties
server.contextPath=/api
It will append "/api" to every requested URL after http://localhost:8080/api/home
For Redirection,
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController("/", "/home");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
super.addViewControllers(registry);
}
Put this bunch of code in WebMVCConfig.java
In the #Configuration bean you can add a ServletRegistrationBean to make the spring server for the /api/* resquest only, then in the Controller you don't need to add it.
#Bean
public ServletRegistrationBean dispatcherRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(
dispatcherServlet());
registration.addUrlMappings("/api/*");
registration.setLoadOnStartup(1);
registration.setName("mvc-dispatcher");
return registration;
}
I don't know why, but the root url "/" would not resolve without adding a little more code. This is what I ended up with.
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.CacheControl;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.PathResourceResolver;
#EnableWebMvc
#Configuration
public class MvcConfiguration implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/")
.addResourceLocations("classpath:/static/")
.resourceChain(true)
.addResolver(new PathResourceResolver() {
#Override
protected Resource getResource(String resourcePath, Resource location) throws IOException {
Resource requestedResource = location.createRelative(resourcePath);
return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
: new ClassPathResource("/static/index.html");
}
});
registry.addResourceHandler("/**/*")
.addResourceLocations("classpath:/static/")
.resourceChain(true)
.addResolver(new PathResourceResolver() {
#Override
protected Resource getResource(String resourcePath, Resource location) throws IOException {
Resource requestedResource = location.createRelative(resourcePath);
return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
: new ClassPathResource("/static/index.html");
}
});
}
}
Ok, let's start with the simple part of your question:
Is there a way to prefix all controllers with api so that I do not have to write api every time?
The answer is yes, just mark your controller with a "global" #RequestMapping annotation, for example:
#RestController
#RequestMapping("/api")
public class ApiController{
#RequestMapping("/hello")
public String hello(){
return "hello simple controller";
}
#RequestMapping("/hello2")
public String hello2(){
return "hello2 simple controller";
}
}
In the example above you can invoke hello method with this URL: /api/hello
and the second method with this URL: /api/hello2
This is how I didn't have to mark each method with /api prefix.
Now, to the more complex part of your question:
is how to achieve a redirect if the request doesn't start with /api prefix?
You can do it by returning an HTTP status code (302) of Redirect, after all, angularJs "speaks" REST natively, thus you can't force a redirect from Java/Spring code like you use to.
Then just return an HTTP message with the status code of 302, and on your angularJS do the actual redirection.
For example:
On AngularJS:
var headers = {'Content-Type':'application/json', 'Accept':'application/json'}
var config = {
method:'GET'
url:'http://localhost:8080/hello',
headers:headers
};
http(config).then(
function onSuccess(response){
if(response.status == 302){
console.log("Redirect");
$location("/")
}
}, function onError(response){
console.log("An error occured while trying to open a new game room...");
});
On Spring:
#RestController
#RequestMapping("/api")
public class ApiController{
#RequestMapping("/hello")
public ResponseEntity<String> hello(){
HttpHeaders header = new HttpHeaders();
header.add("Content-Type", "application/json");
return new ResponseEntity<String>("", header, HttpStatus.FOUND);
}
}
of course, you'll need to custom it to your project.
All you need to try is put the index.html to src/main/resources/static/
See Example: https://github.com/reflexdemon/shop/tree/master/src/main/resources/static
In my package.josn I try to copy it to this location.
See PackageJSON: https://github.com/reflexdemon/shop/blob/master/package.json#L14

How to disable authorization middleware in cakePHP4?

By default, the authorization plugin is apply to a global scope. For some controllers that I did not want to apply any authorization. I have to use the skipAuthorization config manually for each action. For authentication plugin, I can just only load the authentication component for each controller that requires authentication. However, the authorization middleware seems will always work even if I did not load the authorization component in the controller. So, why is that? And is there a way I can disable the authorization process for the entire controller?
You probably mean Authentication and not Authorization. In any case, from the Docs:
// in src/Controller/AppController.php
public function initialize()
{
parent::initialize();
$this->loadComponent('Authentication.Authentication');
}
By default the component will require an authenticated user for all
actions. You can disable this behavior in specific controllers using
allowUnauthenticated():
// in a controller beforeFilter or initialize // Make view and index not require a logged in user.
$this->Authentication->allowUnauthenticated(['view', 'index']);
More information: The Authentication plugin in the Cake Book.
I think you are not doing it in the right way. For authorization, you have to write a request policy. Whenever you bake controller just add --prefix Admin or whatever you want to.
cake bake controller Users --prefix Admin
Put all admin controllers in one place.
Add routes in your routes file
$builder->prefix('Admin',['_namePrefix' => 'admin:'], function (RouteBuilder $builder) {
$builder->connect('/', ['controller' => 'Users', 'action' => 'Index']);
$builder->fallbacks(DashedRoute::class);
});
`
Request Policy. Create a role table and add column role_id in the Users table and the rest you will understand with code below.
<?php
namespace App\Policy;
use Authorization\IdentityInterface;
use Authorization\Policy\RequestPolicyInterface;
use Cake\Http\ServerRequest;
class RequestPolicy implements RequestPolicyInterface
{
/**
* Method to check if the request can be accessed
*
* #param IdentityInterface|null Identity
* #param ServerRequest $request Server Request
* #return bool
*/
public function canAccess($identity, ServerRequest $request)
{
$role = 0;
if(!empty($identity)){
$data = $identity->getOriginalData();
$role = $data['role_id'];
}
if(!empty($request->getParam('prefix'))){
switch($request->getParam('prefix')){
case 'User' : return (bool)($role === 3);
case 'Admin': return (bool)($role === 1) || (bool)($role === 2);
}
}else{
return true;
}
return false;
}
}
`
and then implements AuthorizationServiceProviderInterface to the Application
use App\Policy\RequestPolicy;
use Authorization\AuthorizationServiceProviderInterface;
use Authorization\AuthorizationService;
use Authorization\Policy\MapResolver;
use Cake\Http\ServerRequest;
use Psr\Http\Message\ServerRequestInterface;
class Application extends BaseApplication implements AuthorizationServiceProviderInterface{
public function getAuthorizationService(ServerRequestInterface $request): AuthorizationServiceInterface
{
$mapResolver = new MapResolver();
$mapResolver->map(ServerRequest::class, RequestPolicy::class);
return new AuthorizationService($mapResolver);
}
}

Symfony: How to load all paths from the same Index route (to use dynamic routing in a React SPA)

I'm creating a SPA backed by Symfony and ApiPlatform so I want to always load my main route despite the real path of the URL.
I want something like this:
/**
* {#inheritdoc}
*/
class DefaultController extends Controller
{
/**
* #Route("/*", name="homepage")
*
* #return Response
*/
public function indexAction(): Response
{
// replace this example code with whatever you need
return $this->render('default/index.html.twig');
}
}
In my intentions, also if the URL is something like /path/to/the/spa/page I want to anyway load the DefaultController::indexAction()route.
How to do this? (obviously the provided example doesn't work).
Ok, I've found the solution after an "illumination".
I remembered that there is the possibility to rewrite all URL adding or removing the trailing slash
Reading that article I saw this:
class RedirectingController extends Controller
{
/**
* #Route("/{url}", name="remove_trailing_slash",
* requirements={"url" = ".*\/$"})
*/
public function removeTrailingSlash(Request $request)
{
// ...
}
}
So, to intercept all URL despite the path, my DefaultController::indexAction() becomes this:
class DefaultController extends Controller
{
/**
* #Route("/{url}",requirements={"url"=".*"}, name="homepage")
*
* #return Response
*/
public function indexAction(): Response
{
// replace this example code with whatever you need
return $this->render('default/index.html.twig');
}
}
Now all URL are all handled by DefaultController::indexAction() despite the URL path.
I would recommend you to use symfony's event system instead.
Subscribe to either kernel.request or kernel.router events.
In case of kernel.request you have to overtake the Symfony\Component\HttpKernel\EventListener\RouterListener::onKernelRequest()
which priotiry is 32 (use 33 at least).
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class SpaSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => ['onKernelRequest', 33],
];
}
public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
if (!$request->isXmlHttpRequest()) {
$html = $this->twig->render('spa.html.twig', [
'uri' => $request->getUri(),
]);
$response = new Response($html, Response::HTTP_OK);
$event->setResponse($response);
}
}
}
In case of kernel.router use priority 1 at least.
You can use the php bin/console debug:event-dispatcher command to find out which listeners are registered for events and their priorities.

How to access the user Token in an injected service to reencode passwords?

I have the below code where I am trying to re-encode passwords as users log in (the database has bee migrated form a legacy website). However, I'm not sure what I'm doing wrong as I keep getting errors:
Attempted to call an undefined method named "forward" of class "AppBundle\Service\HubAuthenticator".
I have set things up as follows:
security.yml
security:
encoders:
AppBundle\Entity\Member:
id: club.hub_authenticator
services.yml
services:
//This should be central service than then calls the second
club.hub_authenticator:
class: AppBundle\Service\HubAuthenticator
club.password_rehash:
class: AppBundle\Service\PasswordRehash
Hubauthenticator.php
namespace AppBundle\Service;
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
class HubAuthenticator extends \Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder implements PasswordEncoderInterface
{
function __construct($cost=13)
{
parent::__construct($cost);
}
function isPasswordValid($encoded, $raw, $salt)
{
// Test for legacy authentication (and conditionally rehash the password stored in the database if true)
if ($this->comparePasswords($encoded, sha1("saltA".$raw."saltB"))) {
$this->forward('club.password_rehash:rehash');
}
// Test for Symfony's Bcrypt authentication (any passwords just rehashed in previous step should work here)
if (parent::isPasswordValid($cost=13, $encoded,$raw,$salt)) return true ;
}
}
PasswordRehash.php
namespace AppBundle\Service;
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
class PasswordRehash extends \Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder
{
// Customises BCryptPasswordEncoder class to use legacy SHA method
function rehash($member, $raw, $salt)
{
//Salt is null as Symfony documentation says it is better to generate a new one
parent::encodePassword($member->getPlainPassword, $salt=null ) ;
}
}
Some other previous attempts for completeness:
My guess is that the problem is that I am misunderstanding what objects are available to me. My understanding is that the user hasn't been authenticated at this point so have tried and removed the below attempts:
Trying to inject the $member into the HubAuthenticator service:
function __construct($cost=13)
{
parent::__construct($cost, \Member $member);
}
When trying to get the plainpassword to rehash:
$this->get('security.context')->getToken()->getUser()->getPlainPassword();
In your services, you can only access what dependencies you've injected.
So, to access the current user object, you need to pass it as argument:
service:
club.password_rehash:
class: AppBundle\Service\PasswordRehash
arguments: [ "#security.token_storage" ]
Constructor:
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class HubAuthenticator extends \Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder implements PasswordEncoderInterface
{
private $storage;
function __construct($cost = 13, TokenStorageInterface $storage)
{
parent::__construct($cost);
$this->storage = $storage;
// Now you can use:
// $user = $this->storage->getToken()->getUser();
}
}
Then, to access the second service, same way, inject it.
Add it to the service arguments:
club.password_rehash:
class: AppBundle\Service\PasswordRehash
arguments: [ "#security.token_storage", "#club.password_rehash" ]
Add it to your constructor:
private $storage;
private $passwordRehash
function __construct($cost = 13, TokenStorageInterface $storage, PasswordRehash $passwordRehash)
{
parent::__construct($cost);
$this->storage = $storage;
$this->passwordRehash = $passwordRehash;
// Now you can use:
// $this->passwordRehash->rehash(...);
}
Hope this helps you.

CakePHP change locale dynamically

I've my CakePHP app internationalized through .po files.
The file structure is as follows:
- src
- Locale
- en_EN
- en_ES
The app is correctly translated at startup by:
ini_set('intl.default_locale', 'en_ES');
But, I need the app to be translated dynamically, in an action listener button for example.
I tried the following, but it doesn't work:
use Cake\I18n\I18n;
I18n::locale('en_EN');
You need to save the locale in the session so that it persists between page requests.
A possible approach:
class AppController extends Controller {
public function initialize() {
if ($this->request->session()->check('Config.locale')) {
I18n::locale($this->request->session()->read('Config.locale'));
}
//rest of your init code
}
public function change_locale($locale){
$this->request->session()->write('Config.locale', $locale);
return $this->redirect($this->referer());
}
}

Resources