Я работаю над REST API. При попытке доступа к ресурсу: мы хотим либо дать 403 (Forbidden), либо 404 (Not Found) ошибку. Наши таблицы:
CREATE TABLE 'Action' (
'id' int(10) unsigned NOT NULL AUTO_INCREMENT,
'created_By_Id' int(10) unsigned NOT NULL,
'name' varchar(60) NOT NULL,
'updated_action_at' datetime(3) DEFAULT NULL,
'created_At' datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
'notes' varchar(400) DEFAULT NULL,
PRIMARY KEY ('id'),
KEY 'action_empId_fk' ('created_By_Id'),
CONSTRAINT 'action_empId_fk' FOREIGN KEY ('created_By_Id')
REFERENCES 'Employee' ('id') ON DELETE CASCADE,
) ENGINE=InnoDB AUTO_INCREMENT=502004 DEFAULT CHARSET=latin1
CREATE TABLE 'ActionAssignedTo' (
'action_Id' int(10) unsigned DEFAULT NULL,
'assignee_Id' int(10) unsigned DEFAULT NULL,
KEY 'actionassignedto_emp_id_foreign' ('emp_Id'),
KEY 'actionassignedto_action_id_foreign' ('action_Id'),
CONSTRAINT 'ActionAssignedTo_ibfk_1' FOREIGN KEY ('assignee_Id')
REFERENCES 'Employee' ('id') ON DELETE CASCADE,
CONSTRAINT 'ActionAssignedTo_ibfk_2' FOREIGN KEY ('action_Id')
REFERENCES 'Action' ('id') ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE TABLE 'Employee' (
'id' int(10) unsigned NOT NULL AUTO_INCREMENT,
'vendor_Id' int(10) unsigned DEFAULT NULL,
'name' varchar(40) NOT NULL,
'mobile_Number' varchar(15) NOT NULL,
'active' tinyint(1) DEFAULT '1',
'updated_At' datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
'created_At' datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY ('id'),
KEY 'employee_vendor_id_foreign' ('vendor_Id'),
CONSTRAINT 'employee_vendor_id_foreign' FOREIGN KEY ('vendor_Id')
REFERENCES 'Vendor' ('vendor_Id')
) ENGINE=InnoDB AUTO_INCREMENT=511 DEFAULT CHARSET=latin1
Мы запускаем запрос для получения действия с id: 17 сотрудником-создателем с идентификатором: 9 и идентификатором поставщика: 1, и этот сотрудник был создан, чтобы он мог просмотреть его (бизнес-правило). Действие может быть назначено нескольким сотрудникам.
select Action.name,
group_concat(AssigneeNameTable.name) as assignedTo,
group_concat(AssigneeNameTable.id) as assignedToId,
ActionAssignedTo.action_Id as actionId
from Action
inner join Employee
on Action.created_By_Id = Employee.id
and Employee.vendor_Id = 1
inner join ActionAssignedTo
on Action.id = ActionAssignedTo.action_Id
and ActionAssignedTo.action_Id = 17
inner join Employee as AssigneeNameTable
on ActionAssignedTo.assignee_Id = AssigneeNameTable.Id
where Action.created_By_Id = 9
and Action.deleted_at is null
group by Action.id
limit 2
Теперь давайте скажем, если Action вообще не существует в DB ->, в этом случае указанный выше запрос возвращает пустой набор результатов
the problem is we can not differentiate the query return empty set because
1. either the action with id:17 did not exist(404- Not Found)
2. or the business rule failed (as in the person requested the action was not
at all related to the action(403 - Forbidden).
Одним из решений, о котором я могу думать, является: Сначала запустите небольшой запрос, например:
select * from Action where id = 17
если этот запрос возвращает пустой набор, что означает, что действие не существует в db.
После этого я запускаю запрос Bigger
Различные комбинации набора результатов (число в массиве указывает на возвращаемые записи):
Small Query | Big Query | Interpretation
---------------------------------------
[0] | [0] | Resource Not Found(404)
[1] | [0] | Forbidden (403)
Если Small Query возвращает 0 результат → мы можем напрямую отправить 404 Error; иначе мы выполняем Большой запрос.
Я использовал концепцию Left Outer Join, как предложил мой друг. Ниже вы найдете новый запрос:
select *
from
(select id
from Action
where id = 17) AS act1
left Outer Join
(select Action.name,
group_concat(AssigneeNameTable.name) as assignedTo,
group_concat(AssigneeNameTable.id) as assignedToId,
ActionAssignedTo.action_Id as actionId
from Action
inner join Employee
on Action.created_By_Id = Employee.id
and Employee.vendor_Id = 1
inner join ActionAssignedTo
on Action.id = ActionAssignedTo.action_Id
and ActionAssignedTo.action_Id = 17
inner join Employee as AssigneeNameTable
on ActionAssignedTo.assignee_Id = AssigneeNameTable.Id
where Action.created_By_Id = 9
and Action.deleted_at is null
group by Action.id
limit 2) AS act2
on act1.id = act2.actionId
Концепция проста
если результат не содержит результата → Объект не найден (404)
если вывод содержит поле id
но не содержит ни одного поля из второго подзапроса, что означает, что сущность существует в db, но бизнес-правила не позволяют и, следовательно, запрещены (403).