Yii Site gets 404 error - yii2

I programmed an own yii-site called myScript_Formular.php,which has been put in
E:\xampp\htdocs\MyProject_Yii2.0\views\site. All other sites are in this folder,too!Furthermore,I added following code in SiteController:
public function actionScript() //A new method, programmed by Thomas Kipp
{
$model = new myScriptForm();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
return $this->render('myScript', ['model' => $model]);
}
else
{
return $this->render('myScript_Formular', ['model' => $model]);
}
}
If I try to get this page in yii2.0 in order to put in following url:
http://localhost/MyProject_Yii2.0/web/index.php?r=site%2FmyScript_Formular
I just get error:Not Found (#404)
What the hell is that? I controlled the name of the file thousands of times,it's correct and available in folder site.What should I do?

To be a little bit more precise I try to give you an official answer. The 404 error you got means that the url manager cannot resolve the url you provided to a route. The route in your case consists of a controller (site) and an action (script). You access your view through the action, therfore the url must form a valid route which identifies the controller and the action.
You should really read up on routing here: Yii2 routing

Related

Yii2 beginner. Display REST exception handling

In my controller, I extend the Controller class instead of ActiveController
I have a simple actionIndex() method:
public function actionIndex(){
return json_encode(["text" => "some text"]);
}
When I access the certain route in browser, in the debugger, I see that this function is executing (the breakpoint stops inside the function), but I get 500 status code (Internal server error). How can I find the cause of the error? I have implemented the actionError() method, but it is not executing.
public function actionError() {
$exception = Yii::$app->errorHandler->exception;
if ($exception !== null) {
return $this->render('error', ['exception' => $exception]);
}
}
If I put the logic of the actionError() method in the actionIndex(), the $exception variable is null
The only output I get is:
{
name: "PHP Warning",
message: "Expected array for frame 0",
code: 2,
type: "yii\base\ErrorException",
file: "Unknown",
line: 0,
stack-trace: []
}
but it's warning, not error. May this cause the status code 500?
How can I get the error? Thanks!
According to this thread https://github.com/yiisoft/yii2/issues/11120 this is related to Xdebug for Yii 2 < 2.0.9.
When handling an exception, XDebug modifies the exception and Yii2 was not able to handle it correctly.
So several possible solutions here
The best idea is to update Yii2 to a version >= 2.0.9
Also you sould correct the source of the exception (the warning). It is never a good idea to have warnings. It can hide some more issues.
And as a workaround, you can disable XDebug. It is very useful during development but must be disabled in production in all cases.
Don't know about your error, but there is generally no need to return a response as json encoded. Yii checks the Accept-header of the request and adjust output accordingly. In your case I would just use:
public function actionIndex()
{
return ["text" => "some text"];
}
Possibly it might also solve your error...

Calling Jersey from Angular2

I'm beggining to play with Angular2. I have developed a basic RESTful API using Jersey. I tested it and it works fine (with browser and SOAP UI). This is the code:
#GET
#Produces(MediaType.APPLICATION_JSON)
public TwoWordsMessage getMessage() {
TwoWordsMessage message = new TwoWordsMessage();
message.setFirstWord("hello");
message.setSecondWord("world");
return message;
}
I'm tryng to call the service from an Angular2 app:
this.http.request(this.url).subscribe((res: Response) => {
this.message = res.json();
});
I can see (debbuging) that "getMessage" method is called and it returns the TwoWordsMessage object but the Angular2 application never gets it. The same code with the url http://jsonplaceholder.typicode.com/posts/1 works fine.
What I'm doing wrong?
Thanks!
Are you calling the http request inside a component or a service? Does a function or method fire off the http request?
Also, can you see if there are errors coming back from the response? The subscribe method can take three functions as parameters, first one being on success, second on error, third on completion. If there's an error in the AJAX call (400s, 500s, etc), your code would never be able to handle it. Try this:
this.http.request(this.url).subscribe((res: Response) => {
this.message = res.json();
}, (error) => {
console.warn(error)
});
and see what is spit out. To further debug, you can even use the .do() method on the Observable:
this.http.request(this.url)
.do((res: Response) => console.log(res)) // or other stuff
.subscribe((res: Response) => {
this.message = res.json();
});
The .do() method will execute an arbitrary function with the response without actually affecting it.
If not, you could also try changing the http call to http.get(). I don't think that's the problem, but the Angular docs do not state what method is defaulted to with http.request() (although I would be almost certain it's a GET).
I finally got it working. It's a CORS problem.
The console showed the error:
"No 'Access-Control-Allow-Origin' header is present on the requested
resource"
I changed the resource method like this:
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getMessage() {
TwoWordsMessage message = new TwoWordsMessage();
message.setFirstWord("hello");
message.setSecondWord("world");
return Response.status(200).header("Access-Control-Allow-Origin", "*").entity(message).build();
}
You can find useful information here:
http://www.codingpedia.org/ama/how-to-add-cors-support-on-the-server-side-in-java-with-jersey/

Yii2 - module login in basic application template

Using the "basic" application template, what is the correct way of setting up a module login that is separate from the main site login?
For example I have an "admin" module which requires a login. I also need a user login for the main site.
I have done the following:
Created admin module using gii tool
Created models folder within the admin module folder
Placed LoginForm.php and User.php within this folder (also updated the namespace declarations in these files)
Added AccessControl behaviour and login/logout actions to modules\admin\controllers\DefaultController.php
Updated config\web.php as follows:
'modules' => [
'admin' => [
'class' => 'app\modules\admin\Module',
],
],
Updated app\modules\admin\Module.php as follows:
public function init()
{
parent::init();
Yii::$app->set('user', [
'class' => 'yii\web\User',
'identityClass' => 'app\modules\admin\models\User',
'enableAutoLogin' => true,
'loginUrl' => ['admin/default/login'],
]);
Yii::$app->set('session', [
'class' => 'yii\web\Session',
'name' => '_adminSessionId',
]);
}
The problem I am having is that if I try to access an admin page when I am not logged in, it shows the login form (this is correct). However upon logging in, it is just redirects me back to the main site. It should redirect me to the admin page I was trying to access.
In DefaultController.php, it has the following (default code):
if ($model->load(Yii::$app->request->post()) && $model->login())
return $this->goBack();
What is the correct way of doing this so I can have independent logins for the admin module and for the main site? I don't want to use the "advanced application template" as that adds some unnecessary complexity.
The User component allows you to set a returnUrl, the getter explained: This method reads the return URL from the session. It is usually used by the login action which may call this method to redirect the browser to where it goes after successful authentication.
Recommended: Before processing the data, set the returnUrl by calling Yii::$app->user->setReturnUrl(Url::current()); (this will store the page the user was on in the session, moreover, you can manipulate GET parameters using Url::current() before passing it to the session), and let the framework do its magic.
Not recommended: After processing the data, you can rely on referrer (which won't work in some cases) as following
return Yii::$app->request->referrer ? $this->redirect(Yii::$app->request->referrer) : $this->goHome(); or instead of goHome which basically redirects to app->homeUrl that can be set during Module init, you could say $this->redirect('your/admin/index');
I recommend setting the ['homeUrl' => 'your/admin/index'] during the initialization of the Module, as you might need it for other features as well and Yii2 uses it as a "fallback" to some redirects as well.
The best way is to create new controller in the admin module, which should have login, logout actions. Because, in future you may add there some extra logic.
In that login action you can use same LoginForm.
You can specify redirect url in that login action.
Admin module class can looks like this:
namespace app\modules\admin;
class Module extends \yii\base\Module
{
public $layout = 'main';
public $defaultRoute = 'main/index';
public function init()
{
parent::init();
Yii::$app->errorHandler->errorAction = '/admin/main/error';
Yii::$app->user->loginUrl = '/admin/main/login';
.....
}
}
goBack() defaults to the homeUrl if the returnUrl for the user hasn't been set.
Why not just redirect?
if ($model->load(Yii::$app->request->post()) && $model->login())
return $this->redirect(['myadminmodule']);
go to config/web.php and add it to the components
'backendUrlManager'=>[
'class' => 'yii\web\urlManager',
'enablePrettyUrl'=>true,
'showScriptName'=>false,
'baseUrl'=>'/admin',
],
In DefaultController.php, it has the following (default code):
if ($model->load(Yii::$app->request->post()) && $model->login())
return Yii::$app->getResponse()->redirect(Yii::$app->backendUrlManager->baseUrl);

Execute my code before any action of any controller

I would like to check if my user have filled certain fields in his profile before he can access any action of any controller.
For example
if(empty(field1) && empty(field2))
{
header("Location:/site/error")
}
In yii1 I could do it in protected\components\Controller.php in init() function
But in yii2 I'm not sure where to put my code. I cannot modify core files, but not sure what to do in backend of my advanced application to make it work.
I know I can user beforeAction() but I have too many controllers to do that and to keep track of every controller
In case you need to execute a code before every controller and action, you can do like below:
1 - Add a component into your components directory, for example(MyGlobalClass):
namespace app\components;
class MyGlobalClass extends \yii\base\Component{
public function init() {
echo "Hi";
parent::init();
}
}
2 - Add MyGlobalClass component into your components array in config file:
'components' => [
'MyGlobalClass'=>[
'class'=>'app\components\MyGlobalClass'
],
//other components
3 - Add MyGlobalClass into bootstarp array in config file:
'bootstrap' => ['log','MyGlobalClass'],
Now, you can see Hi before every action.
Please note that, if you do not need to use Events and Behaviors you can use \yii\base\Object instead of \yii\base\Component
Just add in config file into $config array:
'on beforeAction' => function ($event) {
echo "Hello";
},
Create a new controller
namespace backend\components;
class Controller extends \yii\web\Controller {
public function beforeAction($event)
{
..............
return parent::beforeAction($event);
}
}
All your controllers should now extend backend\components\Controller and not \yii\web\Controller. with this, you should modify every controller. I would go for this solution.
I believe you might also replace 1 class with another (so no change to any controller necessary), something like
\Yii::$classMap = array_merge(\Yii::$classMap,[
'\yii\web\Controller'=>'backend\components\Controller',
]);
See more details here: http://www.yiiframework.com/doc-2.0/guide-tutorial-yii-integration.html and I took the code from here: https://github.com/mithun12000/adminUI/blob/master/src/AdminUiBootstrap.php
you can put this in your index.php file. However, make sure you document this change very well as somebody that will come and try to debug your code will be totally confused by this.
Just i think this code on config file can help you:
'on beforeAction' => function ($event) {
// To log all request information
},
'components' => [
'response' => [
'on beforeSend' => function($event) {
// To log all response information
},
],
];
Or, https://github.com/yiisoft/yii2/blob/master/docs/guide/security-authorization.md use RBAC, to restrict access to controllers actions one at a time based on rules. Why would you want to restrict access to controller actions based on user fields is beyond me. You will not be able to access anything (including the login form) if you put a restriction there.

ZF2 View strategy

I'm trying to implement the following:
Simple controller and action. Action should return response of 2 types depending on the request:
HTML in case of ordinary request (text\html),
JSON in case of ajax request (application\json)
I've managed to do this via a plugin for controller, but this requres to write
return $this->myCallBackFunction($data)
in each action. And what if I wan't to do this to whole controller? Was trying to figure out how to implement it via event listener, but could not succed.
Any tips or link to some article would be appreciated!
ZF2 has the acceptable view model selector controller plugin specifically for this purpose. It will select an appropriate ViewModel based on a mapping you define by looking at the Accepts header sent by the client.
For your example, you first need to enable the JSON view strategy by adding it to your view manager config (typically in module.config.php):
'view_manager' => array(
'strategies' => array(
'ViewJsonStrategy'
)
),
(It's likely you'll already have a view_manager key in there, in which case add the 'strategies' part to your current configuration.)
Then in your controller you call the controller plugin, using your mapping as the parameter:
class IndexController extends AbstractActionController
{
protected $acceptMapping = array(
'Zend\View\Model\ViewModel' => array(
'text/html'
),
'Zend\View\Model\JsonModel' => array(
'application/json'
)
);
public function indexAction()
{
$viewModel = $this->acceptableViewModelSelector($this->acceptMapping);
return $viewModel;
}
}
This will return a normal ViewModel for standard requests, and a JsonModel for requests that accept a JSON response (i.e. AJAX requests).
Any variables you assign to the JsonModel will be shown in the JSON output.