I have a login functionality from Web and also from API. I have created one Request file where I have written rules for this. For web, it is working perfectly and given output as expected but when I use it in API controller it redirects me to the login page, it should return JSON response.
One more thing, I want to add extra parameter "status" as it is failed or succeeds.
Here is my code
Request File
public function rules()
{
return [
'username' => 'required',
'password' => 'required'
];
}
API controller
public function login(Request $request)
{
$response = array();
$validator = Validator::make($request->all(), [
'username' => 'required',
'password' => 'required'
]);
if ($validator->fails()) {
$response['status'] = false;
$response['message'] = $validator->messages();
return Response::json($response);
}
try {
$username = trim($request->username);
$password = trim($request->password);
$isAuth = $this->userRepository->login($username, $password);
if ($isAuth) {
$user = Auth::user();
$response['status'] = true;
$response['message'] = Lang::get('custom.auth_success');
$response['user_detail'] = $user;
} else {
$response['status'] = false;
$response['message'] = Lang::get('auth.failed');
}
} catch (\Exception $e) {
$response = array();
$response['status'] = false;
$response['message'] = Lang::get('custom.something_wrong');
}
return Response::json($response);
}
and Web controller
public function checkAuth(UserAuthenticate $request)
{
try {
$username = trim($request->username);
$password = trim($request->password);
$isRemember = false;
if (isset($request->remember) && $request->remember == 'on') {
$isRemember = true;
}
$isAuth = $this->userRepository->login($username, $password, $isRemember);
if ($isAuth) {
return redirect('programs');
} else {
return redirect('login')->withInput()->withErrors(['username' => Lang::get('auth.failed')]);
}
} catch (\Exception $e) {
return redirect('login')->withInput()->withErrors(['error' => Lang::get('custom.something_wrong')]);
}
}
Route/web.php
Route::group(['middleware' => ['guest']], function () {
Route::get('login', ['as' => 'login', 'uses' => 'Front\UserController#login']);
});
Route/api.php
Route::post('user/authenticate', 'API\UserController#login');
I looked for the solutions but didn't found anything
Edit: if you want to use extends Request for both validators do the web validation through ajax
Since you're using passport you already have the token so you can skip login
For api your validator needs to extend Request instead of FormRequest
You can't use the same validator, because the web validator extends FormRequest and that return html. Two validators are necessary, there is no way around it.
use App\Http\Requests\Request;
class YourApiRequest extends Request
{
/**
* 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()
{}....
In your normal web request you would have
use Illuminate\Foundation\Http\FormRequest;
class YourWebRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
// only allow updates if the user is logged in
return \Auth::check();
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{....
In Handler.php you need to add something to the render method (if you return json from your api)
If you do not prefix your routes with api/ then figure out how to check if you are in api
if (strpos($prefix, 'api') !== false) {
if ($exception instanceof ValidationException) {
return response()->json(['success' => false, 'error' => $exception->errors(), 'data' => null], 200);
}
return response()->json(['success' => false, 'error' => $exception->getMessage(), 'data' => null], 200);
}
You may try to over ride the Laravel form request validation's failedValidation() method.
public function failedValidation(Validator $validator)
{
//wantsJson() that checks Accept header of the request and returns TRUE if JSON was requested.
if ($this->wantsJson()) {
throw new HttpResponseException(response()->json(["response" => [
'msg' => $validator->errors()->all(),
]]));
}
}
[Not tested on api calls]
Related
i'm new to laravel , hope someone could help me with this problem ,
i've created a request class to validate my inputs . But when the validation fails it doesn't return any error messages instead showing a 404 error.
my request class , recoverIdRequest
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class recoverIdRequest 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 [
'dob' => 'required',
'email' => 'required',
];
}
}
and here's my controller : testController
class testController extends Controller
{
/**
*
* #param \App\Http\Requests\recoverIdRequest $request
* #return Illuminate\Http\Response
*/
public function test(recoverIdRequest $request)
{
$validated = $request->validated();
$dob = $request->input('dob');
$new_dob = Carbon::parse($dob)->format('Y-m-d');
$email = $request->input('email');
$exist =Appl_students::where('email', $email)
->whereBetween('dob', [$new_dob, $new_dob])
->select('id', 'first_name')
->first();
if ($exist == null) {
return response()->json(['data' => $exist, 'success' => false, 'message' => "User not found"]);
} else {
$date_of_birth = Carbon::parse($dob)->format('d-m-Y');
$institute =Master::select(
'institution_name', 'website_url', 'institution_place',
'institution_post', 'institution_district', 'institution_pin',
'institution_state', 'institution_phone', 'institution_email')
->first();
return $institute;
Mail::to($email)->send(new RecoverApplicationId($exist->id, $date_of_birth, $exist->first_name, $institute->institution_name));
return response()->json(['data' => $exist, 'success' => true, 'message' => "Application ID has seen sent to registered mail"]);
}
}
}
and this is the response in postman when the validation fails :
routes/api.php
Route::group([ 'prefix'=>'modelTesting', ], function() {
Route::post('test/{id}' [testController::class,'test'])->middleware(['auth:api', 'scope:admin']);
});
Resolved
it was a problem with postman headers,i was able to fix the issue using the following headers :
Accept: application/json
X-Requested-With: XMLHttpRequest
You should follow the naming convention first in all your classes.
As the information not very much clear but it should return 422 error status. It might be the problem that when validation is failed then it is trying to redirect none existence URL. Please check the type of method you are using in postman to call the api. If it not resolved please paste the request data from the postman. And the routes.php
It does not give 404, if the validation process is not successful, validation redirects back to the previous page, in your case it gives 404 because there is no rest API and a previous page ... let's agree here
it's very natural and you just have to write a small validation method for it
try it, add this method to your form request class(recoverIdRequest) and try again
/**
* Returns validations errors.
*
* #param Validator $validator
* #throws HttpResponseException
*/
protected function failedValidation(Validator $validator)
{
// you can debug with dd() in this method
if ($this->wantsJson() || $this->ajax()) {
throw new HttpResponseException(response()->json($validator->errors(), 422));
}
parent::failedValidation($validator);
}
second step you should change handler
app/exceptions/Handler.php
public function render($request, Exception $e)
{
if ($request->ajax() || $request->wantsJson())
{
$json = [
'success' => false,
'error' => [
'code' => $e->getCode(),
'message' => $e->getMessage(),
],
];
return response()->json($json, 400);
}
return parent::render($request, $e);
}
I'm making an app that uses jwt as authentication system ,
when I try to update my Category model the policy always returns 403 unauthorized,
I'm using apiResource to crud my model.
my code
in api.php:
Route::apiResource('category', CategoryController::class);
in CategoryController.php:
public function update(Request $request, $id)
{
// print_r($request->all());
$validator = Validator::make(
$request->all(),
[
'name' => 'required|min:2|unique:categories,name,' . $request->id,
'description' => 'required|min:1',
],
[
"name.unique" => "اسم الصنف مستخدم مسبقا",
"name.required" => "اسم الصنف مطلوب",
"name.min" => "اسم الصنف يجب أن يحتوي على حرفين على الأقل",
"description.required" => "وصف الصنف مطلوب",
]
);
if ($validator->fails()) {
return response()->json(['errors' => $validator->messages(), 'status' => 422], 200);
}
$category = Category::find($id);
$category->name = $request->name;
$category->description = $request->description;
$category->save();
return response()->json([
"message" => "تم تحديث الصنف",
"status" => 200
], 200);
}
in CategoryPolicy.php:
public function update(User $user, Category $category)
{
return $category->user_id === $user->id;
}
It seems like the request is not even reaching the update method in CategoryPolicy.php
because even if the method always returning true it's not working :
public function update(User $user, Category $category)
{
return true;
}
any way the viewAny method is working as expected.
I'm using axios to fetch and update data and I'm sending the request with the bearer token and every thing is working ok except the issue above.
In CategoryController.php, instead of injecting $id:
public function update(Request $request, $id)
Try injecting the type-hinted model instance:
public function update(Request $request, Category $category)
And remove the find() command:
//$category = Category::find($id);
When generating new controllers, you can also use this artisan command to include type-hinted models in the function arguments.
php artisan make:controller CategoryController --api --model=Category
It's hard to see what is going wrong because it can also be the middleware and JWT token. What you could do is in your update method check if the user is logged in, add the following as the first line in the method. If false please check your JWT implementation
dd(auth()->check());
I would also suggest clean up your controller:
class CategoryController
{
/**
* CateogryController constructor.
*/
public function __construct()
{
$this->authorizeResource(Category::class); // if your are using CRUD, validate like this
}
/**
* Update specific resource.
*
* #param Category $category
* #param Request $request
* #return \Illuminate\Http\JsonResponse
*/
public function update(Category $category, CategoryRequest $request): JsonResponse
{
// notice the model route binding.
$this->authorize('update', $category); // If you only have update method, but remove the __construct.
$category->update([
'name' => $request->get('name'),
'description' => $request->get('description')
]);
return response()->json(['message' => 'تم تحديث الصنف']); // take the 200 from the headers, not add it in as text.
}
}
Your request looks similar to this:
class CategoryRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true; // you could consider to validate the user->category relation. I like it more separated and put it in a separated policy.
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required|min:2|unique:categories,name',
'description' => 'required|min:1',
];
}
/**
* #return string[]
*/
public function messages()
{
return [
"name.unique" => "اسم الصنف مستخدم مسبقا",
"name.required" => "اسم الصنف مطلوب",
"name.min" => "اسم الصنف يجب أن يحتوي على حرفين على الأقل",
"description.required" => "وصف الصنف مطلوب",
];
}
}
And your policy like:
class CategoryPolicy
{
use HandlesAuthorization;
/**
* Determine if the user can update category resource.
*
* #param User $user
* #param Category $category
* #return bool
*/
public function update(User $user, Category $category): bool
{
return $user->categories()->where('id', $category->id)->exists(); // or somthing like this.
}
}
Request Class
class LoginRequest extends FormRequest
{
public function wantsJson() {
return true;
}
public function authorize() {
return true;
}
public function rules() {
$validators = [
'email' => 'required',
'password' => 'required'
];
return $validators;
}
public function failedValidation(\Illuminate\Contracts\Validation\Validator $validator) {
if($validator->fails()) {
//print_r($validator->errors());
//die();
}
return parent::failedValidation($validator);
}
}
I have an api written in Laravel. I am trying to test the validation through Postman extension. When I submit some values of email and password, it works. I get the message that the credentials exists or not.
In case, I don't submit the values, then, there is no json messagebag returned.
I can confirm that there are validation error messages in MessageBag. Here is the screenshot. If the screenshot is not clear then please click it see it.
Another strange things is that the status code returned is 200
Please let me know if you need more info
In my situation I setup my Laravel API like this.
in my App\Exceptions\Handler
public function render($request, Exception $exception)
{
// return parent::render($request, $exception);
$rendered = parent::render($request, $exception);
if ($exception instanceof ValidationException) {
$json = [
'error' => $exception->validator->errors(),
'status_code' => $rendered->getStatusCode()
];
} elseif ($exception instanceof AuthorizationException) {
$json = [
'error' => 'You are not allowed to do this action.',
'status_code' => 403
];
}
else {
// Default to vague error to avoid revealing sensitive information
$json = [
'error' => (app()->environment() !== 'production')
? $exception->getMessage()
: 'An error has occurred.',
'status_code' => $exception->getCode()
];
}
return response()->json($json, $rendered->getStatusCode());
}
also import this on top
use Illuminate\Validation\ValidationException;
use Illuminate\Auth\Access\AuthorizationException;
It helps to format the errors into JSON format.
My LoginRequest looks like this (simple)
class LoginRequest 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 [
'email' => 'required|email',
'password' => 'required'
];
}
}
Facing a problem on submit form data.
When I fill up all form data then it is inserted into the database. But when I fill only mandatory field data and leave other data as blank it is not working and it redirect to the same form.
On removing validation also not working.
My controller function code:
public function save(Request $request) {
try {
$validator = Validator::make($request->all(), Activity::rules());
$activity = Activity::saveOrUpdate($request);
if($activity !== false) {
return redirect()->route('lists-activity')->with('success', trans('activity data added successfully.!!'));
} else {
return back()->with('error', "Unable to save activity data.!!")->withInput();
}
} catch (\Exception $ex) {
return back()->with('error', "Unable to save activity data.!!")->withInput()->withErrors($validator);
}
}
My model code :
namespace App;
use Illuminate\Http\Request;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Input;
class Activity extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'category_id',
'title',
'description',
'country_id',
'city_id',
'latitude',
'longitude',
'addressOne',
'addressTwo',
'hours_recomended',
'hours_fixed',
'time_fixed',
'start_time',
'end_time',
'file_type',
'flag_image'
];
/**
* Indicates if the model should be timestamped.
*
* #var bool
*/
public $timestamps = false;
public static function rules() {
return [
'category_id' => 'required',
'title' => 'required|string|max:255',
'country_id' => 'required',
'city_id' => 'required',
'hours_fixed' => 'required',
'start_time' => 'required',
'end_time' => 'required'
];
}
public static function saveOrUpdate(Request $request) {
try {
$id = $request->get('id', false);
$activity = false;
DB::transaction(function () use ($request, &$activity, $id) {
$activity = $id ? Activity::findOrFail($id) : new Activity();
$activity->fill($request->all());
try {
$activity->save();
} catch (\Exception $ex) {
throw $ex;
}
});
return $activity;
} catch (\Exception $ex) {
throw $ex;
}
} }
Form view :
Don't know what I am doing wrong?
Looks like validation works and redirects back as it supposes to do.
Put {{ dump($errors->all()) }} to the view and you'll see errors in the form after submitting the form.
Also, use the updateOrCreate() which does exactly what you're trying to do and will help you to avoid errors:
public static function saveOrUpdate(Request $request) {
return $this->updateOrCreate(['id' => $request->id], $request->all());
}
I am trying to use form request in my REST API built using laravel 5.2. My controller is
public function save(SbcEntityFormRequest $request)
{
$requestData = Input::all();
try {
list($success, $message) = $this->sbcService->saveSbcEntity($requestData);
if ($success) {
return $this->successJsonResponse($request, ['id' => $message]);
}
return $this->errorJsonResponse($request, Response::HTTP_BAD_REQUEST, [$message]);
} catch (Exception $e) {
AppLog::write($e);
$message = [config('messages.save_failed')];
return $this->errorJsonResponse($request, Response::HTTP_BAD_REQUEST, $message);
}
}
My form request is
namespace App\Http\Requests;
use Illuminate\Http\Request;
class SbcEntityFormRequest extends Request
{
/**
* 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 [
'logo' => 'Required',
'bio' => 'Required|Max:150'
];
}
}
My validation rules are never called. I put a die statement in authorize() function and it is neither called. When I printed $request->all() in the controller it shows empty array. Any Idea on what is wrong here?