In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-15 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
In this issue, the editor will bring you about how to deal with the unexpected data in JavaScript. The article is rich in content and analyzed and described from a professional point of view. I hope you can get something after reading this article.
The biggest problem with dynamically typed languages is that there is no guarantee that the data flow is always correct, because we cannot "force" a parameter or variable to, for example, not null. The standard practice when faced with these situations is to simply make a judgment:
Function foo (mustExist) {
If (! mustExist) throw new Error ('Parameter cannot be null')
Return...
}
The problem with this is that it pollutes our code, because we have to make judgments everywhere, and there is no guarantee that everyone who develops the code will judge like this; we don't even know whether a parameter passed in this way is undefined or null, which is common and probable when different teams are responsible for front and back ends.
How can the side effects of "unexpected" data be minimized in a better way? As a back-end developer, I would like to give some personal opinions.
one。 The source of everything.
There are many sources of data, the most important of which, of course, is user input. However, there are other sources of defective data, such as databases, invisible empty data in function return values, external API, and so on.
We will discuss later how to deal with each situation differently, knowing that there is no panacea after all. Most of these unexpected data originate from human error, and when the language parses to null or undefined, the accompanying logic is not ready to deal with them.
two。 User input
In this case, there is not much we can do, and if it is a user input problem, we deal with it in a way called Hydration. In other words, we have to take the raw input from the user, such as a payload in an API, and convert it into some form that we can apply without error.
On the back end, when using a web server like Express, we can perform all operations on user input from the front end through standard JSON Schema (https://www.npmjs.com/package/ajv) or Joi).
An example of what we can do with Express and AJV for a route might be as follows:
Const Ajv = require ('ajv')
Const Express = require ('express')
Const bodyParser = require ('body-parser')
Const app = Express ()
Const ajv = new Ajv ()
App.use (bodyParser.json ())
App.get ('/ foo', (req, res) = > {
Const schema = {
Type: 'object'
Properties: {
Name: {type: 'string'}
Password: {type: 'string'}
Email: {type: 'string', format:' email'}
}
AdditionalProperties: false
Required: ['name',' password', 'email']
}
Const valid = ajv.validate (schema, req.body)
If (! valid) return res.status (422) .json (ajv.errors)
/ /...
})
App.listen (3000)
It can be seen that we have verified the body requested in a route. By default, body is an object received by payload from the body-parser packet. In this case, it is passed to a JSON-Schema instance for verification to see if one of the attributes has a different type or format.
Important: note that we returned a HTTP 422 Unprocessable Entity status code, which means "entity that cannot be processed". Many people deal with requests like this with body or query errors, using a 400 Bad Request error message that represents the overall error; in this case, there is nothing wrong with the request itself, but the data sent by the user does not meet expectations.
Optional parameters for default values
One of the perks of our previous validation is that we opened up the possibility that if an optional field is not passed, a null value can be passed into our application. For example, imagine a paging route with two parameters page and size as query strings, but neither is required; if neither is received, you must set a default value.
Ideally, we should have a function like this in our controller:
Function searchSomething (filter, page = 1, size = 10) {
/ /...
}
Note: as with the previous 422, for paging queries, it is important to return the appropriate status code. Whenever a request contains only part of the data in the return value, it should return HTTP 206Partial Content, that is, "incomplete content". When the user reaches the last page and there is no more data, 200will be returned. If the user attempts to query the number of pages that are out of the total range, a 204 No Content is returned.
This will solve the case where we accept two null values, but it touches on a point that is usually very controversial in JavaScript. The default value of the optional parameter is assumed only if and only if it is empty, but it does not work when it is null. So if we do this:
Function foo (a = 10) {
Console.log (a)
}
Foo (undefined) / / 10
Foo (20) / 20
Foo (null) / / null
Therefore, you cannot rely solely on optional parameters. There are two ways to deal with such a situation:
The if statement in the front controller, although it looks a little verbose:
Function searchSomething (filter, page = 1, size = 10) {
If (! page) page = 1
If (! size) size = 10
/ /...
}
Directly use JSON-Schema to handle routing:
You can use AJV or @ expresso/validator again to verify the data:
App.get ('/ foo', (req, res) = > {
Const schema = {
Type: 'object'
Properties: {
Page: {type: 'number', default: 1}
Size: {type: 'number', default: 10}
}
AdditionalProperties: false
}
Const valid = ajv.validate (schema, req.params)
If (! valid) return res.status (422) .json (ajv.errors)
/ /...
})
three。 Deal with Null and Undefined
Personally, I'm not interested in the debate about whether to use null or undefined to represent null values in JavaScript.
Now that we know each definition, JavaScript will add two experimental features in 2020.
Null merge operator?
Null merge operator? Is a logical operator. When the left Operand is null or undefined, it returns the Operand on the right. Otherwise, the Operand on the left is returned.
Let myText =''
Let notFalsyText = myText | | 'Hello world'
Console.log (notFalsyText); / / Hello world
Let preservingFalsy = myText? 'Hi neighborhood'
Console.log (preservingFalsy); / /''
Optional chain operator?.
?. The operator function is similar to. Operator, the difference is that if a reference to null or undefined on the chain,. The operator causes an error, and?. The operator returns a undefined as short-circuited.
Const adventurer = {
Name: 'Alice'
Cat: {
Name: 'Dinah'
}
}
Const dogName = adventurer.dog?.name
Console.log (dogName)
/ / undefined
Console.log (adventurer.someNonExistentMethod?. ())
/ / undefined
Merge operators with null values? Use:
Let customer = {
Name: "Carl"
Details: {age: 82}
}
Let customerCity = customer?.city? "Unknown city"
Console.log (customerCity); / / Unknown city
These two new features will make things much easier because we can focus on null and undefined to do the right thing. Instead of Boolean judgment! obj is easier to handle a lot of error situations.
four。 Implicit null function
This hidden problem is more complicated. Some functions assume that the data to be processed is populated correctly, but sometimes it is not satisfactory:
Function foo (num) {
Return 23*num
}
If num is null, the return value of the function will be 0. This does not meet our expectations. In this case, all we can do is add judgment. There are two possible forms of judgment, the first of which can simply use if:
Function foo (num) {
If (! num) throw new Error ('Error')
Return 23*num
}
The second approach is to use a Monad called Either. Monad is a general abstraction mechanism for the process of function evaluation. The key is to unify the form and mode of operation, which is equivalent to wrapping the value in a context. Https://zhuanlan.zhihu.com/p/65449477). This is a good idea for the ambiguous question of whether the data is null, because JavaScript already has a native function that supports double action flow, namely Promise:
Function exists (value) {
Return x! = null
? Promise.resolve (value)
: Promise.reject (`Invalid value: ${value} `)
}
Async function foo (num) {
Return exists (num) .then (v = > 23 * v)
}
In this way, the catch method from exists can be delegated to the function that calls foo:
Function init (n) {
Foo (n)
.then (console.log)
.catch (console.error)
}
Init (12) / /
Init (null) / / Invalid value: null
five。 External API and database records
This is also quite common, especially when the system is developed on top of a previously created and populated database. For example, a new product that uses a previously successful product database, integrating users between different systems, and so on.
The big problem here is not that we don't know the database, but actually we don't know what has been done at the database level, and we can't prove whether the data will be null or undefined. Another problem is the lack of documentation, and the unsatisfactory documentation of the database will still lead to the previous problem.
Because the amount of data returned may be large, there is not much room for such a situation. In addition to having to judge individual data, it is a good practice to filter the grouped data with map or filter before performing formal operations.
Throw Errors
It is also a good practice to use assertion functions (Assertion Functions) for server code in databases and external API, basically by returning otherwise error if the data exists. Most common cases of such functions are, for example, an API that searches for certain data based on an id:
Async function findById (id) {
If (! id) throw new InvalidIDError (id)
Const result = await entityRepository.findById (id)
If (! result) throw new EntityNotFoundError (id)
Return result
}
In practical application, Entity should be replaced with a suitable name, such as UserNotFoundError.
This is good because the user we can find with such a function can be used by another function to retrieve relevant data in other databases, such as user details; and when we call the latter retrieval function, the pre-function findUser has ensured the real existence of user, because if there is an error, an error will be thrown and the problem can be found directly in the routing logic.
Async function findUserProfiles (userId) {
Const user = await findUser (userId)
Const profile = await profileRepository.findById (user.profileId)
If (! profile) throw new ProfileNotFoundError (user.profileId)
Return profile
}
The routing logic would look like this:
App.get ('/ users/ {id} / profiles', handler)
/ /
Async function handler (req, res) {
Try {
Const userId = req.params.id
Const profile = await userService.findUserProfiles (userId)
Return res.status (2000.json (profile)
} catch (e) {
If (e instanceof UserNotFoundError
| | e instanceof ProfileNotFoundError)
Return res.status (404) json (e.message)
If (e instanceof InvalidIDError)
Return res.status (4000.json (e.message)
}
}
Just check the name of the error instance to see what type of error was returned.
Judge unexpected data separately where necessary; set default values for optional parameters; use tools such as ajv to replenish possibly incomplete data; properly use experimental null merging operators? And optional chain operators?; use Promise to package implicit null values and unify the mode of operation; use pre-map or filter to filter unexpected data in groups of data; in controller functions with clear responsibilities, each throw clearly typed errors; using these methods to process data can get a continuous and predictable flow of information.
The above is the editor for you to share how to deal with the unexpected data in JavaScript, if you happen to have similar doubts, you might as well refer to the above analysis to understand. If you want to know more about it, you are welcome to follow the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.