I am working with JWT token to make API in Laravel, I want to Make CRUD operation, I already test my API in Postman.
Now the issue is that I want to implement it via Laravel blade views, in postman I can set Authorization Bearer in header, How would I do that in Laravel application? Not in Postman, in short I want to set header, so I can access token in each every request.
Controller:
<?php
class NewController extends Controller
{
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
$token = auth()->attempt($credentials);
return $this->createNewToken($token);
}
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|between:2,100',
'email' => 'required|string|email|max:100|unique:users',
'password' => 'required|string|confirmed|min:6',
]);
if ($validator->fails()) {
return response()->json(array(
"status" => false,
"errors" => $validator->errors()
), 400);
}
$user = User::create(array_merge(
$validator->validated(),
['password' => bcrypt($request->password)]
));
return response()->json([
'status' => true,
'message' => 'User successfully registered',
'user' => $user
], 201);
}
public function refresh()
{
return $this->createNewToken(auth()->refresh());
}
public function userProfile()
{
return $this->getAuthenticatedUser();
}
public function me()
{
$user = JWTAuth::parseToken()->authenticate();
return response()->json(array($user), 400);
}
protected function createNewToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60,
'user' => auth()->user()
]);
}
public function getAuthenticatedUser()
{
try {
if (!$user = JWTAuth::parseToken()->authenticate()) {
return response()->json(['user_not_found'], 404);
}
} catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
return response()->json(['token_expired'], $e->getStatusCode());
} catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
return response()->json(['token_invalid'], $e->getStatusCode());
} catch (Tymon\JWTAuth\Exceptions\JWTException $e) {
return response()->json(['token_absent'], $e->getStatusCode());
}
return response()->json(compact('user'));
}
}
Routes:
'prefix' => 'auth',
], function ($router) {
Route::post('login', [App\Http\Controllers\NewController::class, 'login'])->name('apisignin');
Route::post('getauth', [App\Http\Controllers\NewController::class, 'getAuthenticatedUser'])->name('getAuthenticatedUser');
Route::post('userProfile', [App\Http\Controllers\NewController::class, 'userProfile']);
Route::post('me', [App\Http\Controllers\NewController::class, 'me']);
});
Related
I have been using laravel for a short time, and I have been reading about good practices, I want to make an ajax json request with a validation in the form, I have seen that it is better to create a class "extend FormRequest" to better manage the information, in my case I am using validator but I don't know how to manage it in such a way to simplify the controller code as much as possible.
Currently I have:
Controller:
public function store(Request $request)
{
if($request->ajax()) {
$messages = [
'nombre.required' => 'El campo nombre es necesario',
'descripcion.required' => 'El campo descripción es necesario',
];
$rules = [
'nombre' => 'required',
'descripcion' => 'required',
'url' => 'required|alpha_dash'
];
$validator = Validator::make($request->all(), $rules, $messages);
if ($validator->fails()) {
return response()->json([
'status' => 400,
'errors' => $validator->messages()//$validator->errors()
]);
} else {
$crear = new Proyecto([
'nombre' => $request->nombre,
'descripcion' => $request->descripcion,
'url' => $request->url
]);
$crear->save();
return response()->json([
'status' => 200,
'message' => 'Registro insertado con éxito'
]);
}
}else{
return view("404");
}
}
Model:
class Proyecto extends Model
{
use HasFactory;
protected $table = 'proyecto';
protected $primaryKey = 'id_proyecto';
protected $fillable = ['nombre', 'descripcion', 'url'];
public function getRouteKeyName()
{
return 'url';
}
}
js:
$('#crear_proyecto').submit(function(e){
e.preventDefault();
var formu = $("#crear_proyecto").serialize();
$.ajax({
type: "POST",
url: "proyecto/crearproyecto",
data: formu,
dataType: "json",
beforeSend:function(){
$(".loader").show();
},
success: function (data) {
$('#crear_proyecto').find('span.text-danger').remove();
if (data.status == 400) {
$.each(data.errors, function (key, error_value) {
$('#'+key).after("<span class='text-danger'>"+error_value+"</span>");
});
$(".loader").fadeOut("slow");
}else{
$(".loader").fadeOut("slow");
alert(data.message);
$('#crear_proyecto').trigger("reset");
$("#nombre").focus();
$('#modalformu').modal('toggle');
}
}
});
I do not know if the code I have previously put is a bad practice or to leave it like that, what I have been seeing is to leave the controller as light as possible by creating a RequestForm, but I do not know how to adapt it with json and ajax, the idea was to do something like that:
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\ValidationException;
class PostProyectoRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'nombre' => 'required',
'descripcion' => 'required',
'url' => 'required|alpha_dash',
];
}
public function messages()
{
return [
'nombre.required' => 'El campo nombre es necesario',
'descripcion.required' => 'El campo descripción es necesario'
];
}
}
EDIT
Now I have this code in the controller:
public function store(PostProyectoRequest $request)
{
$crear = new Proyecto([
'nombre' => $request->nombre,
'descripcion' => $request->descripcion,
'url' => $request->url
]);
$crear->save();
return response()->json([
'status' => 200,
'message' => 'Registro insertado con éxito'
]);
}
And this y RequestForm
namespace App\Http\Requests;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Validator;
class PostProyectoRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'nombre' => 'required',
'descripcion' => 'required',
'url' => 'required|alpha_dash'
];
}
public function messages()
{
return [
'nombre.required' => 'El campo nombre es necesario',
'descripcion.required' => 'El campo descripción es necesario'
];
}
protected function failedValidation(Validator $validator)
{
if($this->wantsJson())
{
$response = response()->json([
'status' => 400,
'errors' => $validator->errors()//$validator->errors()
]);
}else{
$response = redirect()
->route('guest.login')
->with('message', 'Ops! Some errors occurred')
->withErrors($validator);
}
throw (new ValidationException($validator, $response))
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}
}
So you are actually right on track and you don't need to keep the old validation in the controller anymore. You want to do something like this.
//old
//public function store(Request $request)
//new
public function store(PostProyectoRequest $request)
{
$crear = new Proyecto([
'nombre' => $request->nombre,
'descripcion' => $request->descripcion,
'url' => $request->url
]);
$crear->save();
//you can't have two responses because the first one would run only because
return response()->json([
'status' => 200,
'message' => 'Registro insertado con éxito'
]);
//or
$response = [
'status' => 200,
'message' => 'Registro insertado con éxito'
];
return json_encode($response);
}
So is instead of using the normal Request facade, you would use the form request PostProyectoRequest you created. By doing so, you are telling the controller to pass the request through the form request which would validate it and if any of the validation fails it, it would return a validation error and wouldn't run the code in the controller. Read more about form requests here also read about early returns and returns in general
i am new here, i have a problem with an api in laravel.
I want to show all the records from two tables.
Table a : Flights;
Table b: Details_flights;
class FilterController extends Controller
{
public function product(Request $request) {
$flights= Flight::all();
$details = Flights_detail::all();
if(empty($flights)) {
return response()->json([
'success' => true,
'length' => 0,
'error' => 'No fly found',
'results' => []
]);
}else {
return response()->json([
'success' => true,
'length' => $flights->count(),
'results' => $flights
]);
}
}
}
I tried with array_merge(table A, table B) but it didn't work.
Someone can help me?
You can convert them to array, then merge them in a new array called for example data. Below code may help you:
$data=[
'flights' => $flights->toArray(),
'details' => $details->toArray(),
];
return response()->json([
'success' => true,
'length' => $flights->count(),
'results' => $data
]);
You can try this https://stackoverflow.com/a/30524136/1529662
And your $flights and $details variables are collection. If you want to convert, you should use toArray() method.
There can be two scenarios:
Flight record hasOne/hasMany FlightDetail records (related)
flight_details table will have a foreign key flight_id
Flight and FlightDetail are not related
flight_details table doesn't have foreign key flight_id
Scenario 1
//Models
class Flight extends Model
{
public function details()
{
return $this->hasMany(FlightDetail::class, 'flight_id', 'id');
}
}
class FlightDetail extends Model
{
public function flight()
{
return $this->belongsTo(Flight::class, 'flight_id', 'id');
}
}
class FilterController extends Controller
{
public function product(Request $request) {
$flights= Flight::with('details`)->get();
if(empty($flights)) {
return response()->json([
'success' => true,
'length' => 0,
'error' => 'No fly found',
'results' => []
]);
}else {
return response()->json([
'success' => true,
'length' => $flights->count(),
'results' => $flights
]);
}
}
}
Scenario 2
class FilterController extends Controller
{
public function product(Request $request) {
$flights= Flight::all();
$details = Flights_detail::all();
if(empty($flights)) {
return response()->json([
'success' => true,
'length' => 0,
'error' => 'No fly found',
'results' => []
]);
}else {
return response()->json([
'success' => true,
'length' => [
'flights' => $flights->count(),
'flightDetails' => $details->count()
],
'results' => [
'flights' => $flights,
'flightDetails' => $details
]
]);
}
}
}
I try to take data from DB and use it in VUE script, but in console I see message
GET http://lara7.test/api/furnitura 401 (Unauthorized)
In Chrome devtools Network tab I see response
furnitura
{"message":"Unauthenticated."}
Here is my code
routes\api.php
Route::middleware('auth:api')->group( function () {
Route::resource('furnitura', 'API\FurnituraController');
});
App\Http\Controllers\API\BaseController
namespace App\Http\Controllers\API;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
abstract class BaseController extends Controller
{
public function sendResponse($result, $message)
{
$response = [
'success' => true,
'data' => $result,
'message' => $message,
];
return response()->json($response, 200);
}
public function sendError($error, $errorMessages = [], $code = 404)
{
$response = [
'success' => false,
'message' => $error,
];
if(!empty($errorMessages)){
$response['data'] = $errorMessages;
}
return response()->json($response, $code);
}
}
App\Http\Controllers\API\FurnituraController
namespace App\Http\Controllers\API;
class FurnituraController extends BaseController
{
public function index()
{
$furnitura = Furnitura::all();
return $this->sendResponse($furnitura->toArray(), 'Furnitura retrieved successfully.');
}
}
resources\js\app.js
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
...
...
mounted() {
console.log("Vue ROOT instance mounted");
axios.get('/api/furnitura').then(response => this.furnitura = response.data);
console.log(this.furnitura);
},
Maybe, you do not pass the auth: API middleware. You are getting 401 from middleware.
maybe you use token for user validation or API user validation that token is missing
I have a problem with my old website. Now I try to move it to new server (php5.6) and when i try to save data I have error:
Request blackholed due to "auth" violation.
I serach the place whose do it this error:
if ($this->Node->saveWithMeta($this->request->data)) {
Croogo::dispatchEvent('Controller.Nodes.afterAdd', $this, array('data' => $this->request->data));
$this->Session->setFlash(__('%s has been saved', $type['Type']['title']), 'default', array('class' => 'success'));
if (isset($this->request->data['apply'])) {
$this->redirect(array('action' => 'edit', $this->Node->id));
} else {
$this->redirect(array('action' => 'index'));
}
}
I think error do function saveWithMeta(). This function view like:
public function saveWithMeta(Model $model, $data, $options = array()) {
$data = $this->_prepareMeta($data);
return $model->saveAll($data, $options);
}
Can I replace/edit this function so that it starts to work?
edit
if (!$db->create($this, $fields, $values)) {
$success = $created = false;
} else {
$created = true;
}
These lines cause an error.
In my REST API i want to use JWT fro Authorization.
So, I include this extension - https://github.com/sizeg/yii2-jwt
It clear how to create JWT token, but how to validate Token in API side ? I heart, i must use two tokens - auth_token and refresh_token. For what? What different when i whatt to validate and check user ?
I mean - ok, when i receive username and password, I create auth_token (JWT) and update token in users DB, after i return token to frontend.
After frontend will send auth token in each request, and I will validate token and check user in users DB and check access etc. How to realize refresh token and for what?
For example my controller:
class UploadController extends Controller {
public $serializer = [
'class' => 'yii\rest\Serializer',
'collectionEnvelope' => 'items',
];
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => JwtHttpBearerAuth::className()
];
return $behaviors;
}
public function actionIndex() {
//Work with User
}
}
And how to get token from headers ?
Controller
public function actionLogin()
{
$username = Yii::$app->request->post('username');
$password = Yii::$app->request->post('password');
$provider = new ActiveDataProvider([
'query' => User::find()
->where(['user_name' => $username])->asArray()->one(),
]);
$result = $provider->query;
if($result)
{
if (Yii::$app->getSecurity()->validatePassword($password, $result['user_pass']))
{
$tokenId = base64_encode(mcrypt_create_iv(32));
$issuedAt = time();
$notBefore = $issuedAt; //Adding 10 seconds
$expire = $notBefore + 5184000; // Adding 60 Days
$serverName = 'your-site.com';
$data = [
'iat' => $issuedAt, // Issued at: time when the token was generated
'jti' => $tokenId, // Json Token Id: an unique identifier for the token
'iss' => $serverName, // Issuer
'nbf' => $notBefore, // Not before
'exp' => $expire, // Expire
'data' => [ // Data related to the signer user
'id' => $result['user_id'],
'username' => $result['user_name'],
'mobile' => $result['user_mobile'],
'email' => $result['user_email'],
'city' => $result['user_city'],
'state' => $result['user_state'],
'country' => $result['user_country'],
'picture' => $result['user_picture'],
]
];
$jwt = JWT::encode(
$data,
JWT_KEY,
'HS512'
);
$response = [
'status' => true,
'message' => 'Login Success..',
'era_tkn' => $jwt,
];
}
else
{
$response = [
'status' => false,
'message' => 'Wrong username or password.',
];
}
}
else
{
$response = [
'status' => false,
'message' => 'Wrong username or password.',
];
}
return $response;
}
Make global method for check token
public function check_token()
{
$headers = Yii::$app->request->headers;
$token = $headers->get('era_tkn');
if($token)
{
try{
$valid_data = JWT::decode($token, JWT_KEY, array('HS512'));
$valid_data = $valid_data->data;
}catch(Exception $e){
$valid_data = $e->getMessage();
}
}
else
{
$valid_data = 'Required Authentication';
}
return $valid_data;
}
Call check_token mathod
$user_data = $this->check_token();
if (!empty($user_data->id))
{
echo $user_data->id;
}
else
{
echo "Invalid Token.";
}